@damiangilgonzalez/harness 1.3.23 → 1.3.25

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/README.md CHANGED
@@ -61,6 +61,23 @@ Todo confluye en una rama de integración.
61
61
  `code-review` revisa contra los estándares del repo y contra la spec. `pr` abre la PR con
62
62
  resumen, evidencia y peligro de fusión.
63
63
 
64
+ ### Seguridad y front dentro de la cadena
65
+
66
+ La cadena no cambia: ni pasos nuevos ni comandos nuevos. Lo que cambia es que dos pasos que ya
67
+ existían ahora piden seguridad y front **cuando el cambio los toca** (auth, un endpoint, una tabla,
68
+ un secreto o una pantalla):
69
+
70
+ | Cuándo | Qué pasa |
71
+ |---|---|
72
+ | Al iniciar sesión | Un aviso corto según el proyecto. Con backend, API o `.env`: los 10 puntos de seguridad. Con react, next, vue, svelte o astro: el modelo mental del front. Sin ninguno de los dos, no sale nada. |
73
+ | `to-spec` | La spec lleva una sección «Decisiones de seguridad y de front»: qué puntos aplican y cuáles quedan fuera a propósito. |
74
+ | `code-review` | El eje de Estándares suma un baseline: una ruta sin rate limit o sin validación en el servidor es `should-fix`; un secreto en el repo es `blocker`. |
75
+ | Antes de publicar | `secure-coding` trae la lista con una prueba por punto. Es un punto de partida; la auditoría es `hardening`. |
76
+
77
+ Los 10 puntos: rate limiting, claves fuera del código, secretos de producción en la plataforma,
78
+ límites por IP, entradas validadas, validación en el servidor, RLS, contraseñas con hash y datos
79
+ cifrados, sesiones que vencen y CORS solo con tu dominio. Se apaga con `.rsc/.no-stack-hints`.
80
+
64
81
  La guía completa del método, fase a fase: [`docs/skills/`](docs/skills/).
65
82
 
66
83
  ---
@@ -107,7 +124,7 @@ Empiezas pequeño. El resto se carga bajo demanda: el harness te propone una ski
107
124
  la pide, con la evidencia que lo justifica.
108
125
 
109
126
  ```bash
110
- rsc install --profile minimal # la base: orient + suggest + bro + unslop + show-me + eli5 + harness + init + teach + tunel-ngrok + hardening
127
+ rsc install --profile minimal # la base: orient + suggest + bro + unslop + show-me + eli5 + harness + init + teach + tunel-ngrok + hardening + no-me-rayes
111
128
  rsc install --profile core # la base + el método completo
112
129
  rsc install --profile full # todo el catálogo
113
130
  ```
@@ -128,10 +145,10 @@ Hooks que vigilan por ti: recuerdan la cadena, piden confirmación antes de un `
128
145
 
129
146
  ![Plugins de serie: superpowers, skins, savvy-progress y cache-tax](docs/img/readme/readme-16-plugins.png)
130
147
 
131
- En Claude Code quedan activados `skins`, `savvy-progress` y `cache-tax`
132
- (mantiene caliente la caché, y eso gasta tokens). `superpowers` llega cuando el proyecto usa el método.
148
+ En Claude Code quedan activados `skins`, `savvy-progress`, `context-bar`, `prompt-enhancer`, `modelo-justo`,
149
+ `where-am-i` y `cache-tax` (este mantiene caliente la caché, y eso gasta tokens). `superpowers` llega cuando el proyecto usa el método.
133
150
 
134
- En cifras: 296 skills, 34 agentes y 43 entradas de comando, para 17 asistentes
151
+ En cifras: 297 skills, 34 agentes y 43 entradas de comando, para 17 asistentes
135
152
  (Claude Code, Codex, Cursor, Copilot, Gemini…). Detalle en la [referencia técnica](docs/referencia-tecnica.md).
136
153
 
137
154
  ---
@@ -249,9 +266,9 @@ Guía completa: [la memoria del harness](docs/como-funciona/memoria.md).
249
266
 
250
267
  ## Catálogo
251
268
 
252
- ![296 skills por dominio](docs/img/readme/readme-15-catalogo.png)
269
+ ![297 skills por dominio](docs/img/readme/readme-15-catalogo.png)
253
270
 
254
- 296 skills en 25 dominios: software, negocio, contenido, datos e IA.
271
+ 297 skills en 25 dominios: software, negocio, contenido, datos e IA.
255
272
  Cada una se activa sola cuando la tarea encaja.
256
273
 
257
274
  **[Ver el catálogo completo →](docs/catalogo.md)** · [Referencia técnica](docs/referencia-tecnica.md) ·
package/manifest.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
- "version": "1.3.23",
2
+ "version": "1.3.25",
3
3
  "counts": {
4
- "skills": 296,
4
+ "skills": 297,
5
5
  "agents": 34,
6
6
  "commands": 43,
7
7
  "fixedCommands": 10
@@ -3532,6 +3532,30 @@
3532
3532
  ],
3533
3533
  "profiles": []
3534
3534
  },
3535
+ {
3536
+ "id": "no-me-rayes",
3537
+ "description": "Use on EVERY reply and progress update, especially after tool work or a long task: the assistant must report in short bullet points what is done, what it is doing now, what blocks it, and a final summary, each marked with its own emoji. Cuts the wall-of-text answers some models give. NOT rewriting a text to sound human (that is `bro`), NOT a plain-language explanation (that is `eli5`), NOT a session handoff document (that is `handoff`).",
3538
+ "tags": [
3539
+ "no-me-rayes",
3540
+ "concise",
3541
+ "bullets",
3542
+ "status-report",
3543
+ "progress",
3544
+ "emojis",
3545
+ "short-answers"
3546
+ ],
3547
+ "recommends": [
3548
+ "bro",
3549
+ "eli5",
3550
+ "handoff"
3551
+ ],
3552
+ "profiles": [
3553
+ "minimal",
3554
+ "core",
3555
+ "ui",
3556
+ "full"
3557
+ ]
3558
+ },
3535
3559
  {
3536
3560
  "id": "nodejs",
3537
3561
  "description": "Use when building or operating a plain Node.js / Express 5 backend service: project layout, async correctness, central error middleware, fail-fast config, graceful shutdown on SIGTERM. NOT DI modules/providers/guards (that is `nestjs`), NOT the type system or tsconfig (that is `typescript`), NOT REST contract design (that is `api-design`).",
@@ -4539,7 +4563,7 @@
4539
4563
  },
4540
4564
  {
4541
4565
  "id": "secure-coding",
4542
- "description": "Use when securing application code: threat-modeling a feature, security-reviewing a diff, hardening auth/authz, secrets, CORS/CSP, or fixing OWASP-class bugs (access control, injection, SSRF, supply chain) in FastAPI, Next.js, Go or Flutter. NOT running the scanners (that is security-scan), NOT agent prompt-injection guardrails (that is agent-safety).",
4566
+ "description": "Use when securing application code: threat-modeling a feature, security-reviewing a diff, hardening auth/authz, secrets, CORS/CSP, running the pre-publish checklist (rate limiting, RLS, server-side validation) before shipping an AI-built project, or fixing OWASP-class bugs (access control, injection, SSRF, supply chain) in FastAPI, Next.js, Go or Flutter. NOT running the scanners (that is security-scan), NOT agent prompt-injection guardrails (that is agent-safety).",
4543
4567
  "tags": [
4544
4568
  "security",
4545
4569
  "owasp",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@damiangilgonzalez/harness",
3
- "version": "1.3.23",
3
+ "version": "1.3.25",
4
4
  "description": "A guided harness builder that turns your goal into a minimal, verified AI-agent setup using strict best practices.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -6,7 +6,7 @@
6
6
  // without paying for its body on unrelated turns. `tunel-ngrok` y `hardening` son
7
7
  // obligatorias por decisión del usuario: van al suelo para que también lleguen con
8
8
  // el update a los proyectos que ya existían, no solo a las instalaciones nuevas.
9
- export const DEFAULT_SKILL_FLOOR = Object.freeze(['orient', 'suggest', 'bro', 'tunel-ngrok', 'hardening']);
9
+ export const DEFAULT_SKILL_FLOOR = Object.freeze(['orient', 'suggest', 'bro', 'tunel-ngrok', 'hardening', 'no-me-rayes']);
10
10
 
11
11
  export function withDefaultSkillFloor(skillIds = []) {
12
12
  return [...new Set([...DEFAULT_SKILL_FLOOR, ...skillIds])];
@@ -27,7 +27,7 @@ export const DOMAINS = [
27
27
  { title: 'Motion & interface craft', ids: ['motion-craft', 'ui-engineering', 'variant-explorer'] },
28
28
  { title: 'Design & content craft', ids: ['design-loop', 'design', 'design-dna', 'presentations', 'course-storytelling', 'course-builder', 'technical-writing', 'translation-l10n'] },
29
29
  { title: 'Knowledge & meta', ids: ['knowledge-ops', 'codebase-onboarding', 'research-ops', 'continuous-learning', 'skill-scout', 'context-budget', 'roast-me', 'show-me', 'eli5', 'fable-operator'] },
30
- { title: 'Personal catalog — workflow (Spanish)', ids: ['grill-me', 'grill-with-docs', 'grilling', 'wayfinder', 'to-spec', 'to-tickets', 'to-questionnaire', 'implement', 'implement-spec', 'pr', 'retro', 'revision-de-cambios', 'code-review', 'research', 'prototype', 'domain-modeling', 'handoff', 'write-adr', 'wait-what', 'writing-for-agents', 'claude-project-setup', 'muscle-memory', 'teach', 'tunel-ngrok', 'hardening'] },
30
+ { title: 'Personal catalog — workflow (Spanish)', ids: ['grill-me', 'grill-with-docs', 'grilling', 'wayfinder', 'to-spec', 'to-tickets', 'to-questionnaire', 'implement', 'implement-spec', 'pr', 'retro', 'revision-de-cambios', 'code-review', 'research', 'prototype', 'domain-modeling', 'handoff', 'write-adr', 'wait-what', 'writing-for-agents', 'claude-project-setup', 'muscle-memory', 'teach', 'tunel-ngrok', 'hardening', 'no-me-rayes'] },
31
31
  { title: 'Personal catalog — interface design (Spanish)', ids: ['flujo-interfaz', 'animar', 'diseno-apple', 'diseno-landing', 'ingeniero-diseno-web', 'leyes-de-percepcion', 'leyes-de-retencion', 'mejor-accesibilidad', 'mejor-colores', 'mejor-layout', 'mejor-redaccion', 'mejor-tipografia', 'mejor-ui', 'sitios-calidad-premio', 'tastemaker', 'video-a-superprompt', 'vocabulario-animacion', 'revision-interfaz'] },
32
32
  ];
33
33
 
@@ -60,6 +60,15 @@ Cada smell se lee *qué es* → *cómo arreglarlo*; contrástalo con el diff:
60
60
  - **Intermediario (Middle Man)** — una clase o función que casi solo delega hacia adelante. → córtala, llama al objetivo real directamente.
61
61
  - **Herencia rechazada (Refused Bequest)** — una subclase o implementador que ignora o sobrescribe casi todo lo que hereda. → suelta la herencia, usa composición.
62
62
 
63
+ ### Baseline de seguridad y de front
64
+
65
+ Cuando el diff toca una ruta, un endpoint, una tabla, un secreto, una sesión o un componente, el eje de Estándares lleva además este baseline. Es un juicio, no una violación dura, y un estándar documentado del repo lo supera. Pégalo completo en el prompt del sub-agente, que no tiene otro acceso a él:
66
+
67
+ - **Seguridad** (los 10 puntos de `secure-coding`, `references/pre-publish.md`). Una ruta nueva sin rate limit o sin validación en el servidor es un hallazgo `should-fix`, no un nit. Un secreto en el repo o con el prefijo público del framework es `blocker`. Mira también: tabla nueva sin RLS, CORS con `*` y credenciales, hash débil, sesión que no vence, consulta SQL construida a mano.
68
+ - **Front** (`react` y `nextjs`). Estado copiado desde props o derivado guardado en `useState`, `useEffect` que calcula en vez de sincronizar, dependencias que faltan, valores no deterministas en el primer render (`Date.now()`, `Math.random()`, `window`) que rompen la hidratación, `"use client"` demasiado arriba y secretos importados desde un componente de cliente.
69
+
70
+ Si el diff no toca nada de esto, no lo añadas.
71
+
63
72
  ### 4. Lanzar ambos sub-agentes en paralelo
64
73
 
65
74
  Envía un único mensaje con dos llamadas a la herramienta `Agent`. Usa el subagente `general-purpose` para ambos.
@@ -67,7 +76,7 @@ Envía un único mensaje con dos llamadas a la herramienta `Agent`. Usa el subag
67
76
  **Prompt del sub-agente de Estándares** — incluye:
68
77
 
69
78
  - El comando de diff completo y la lista de commits.
70
- - La lista de archivos-fuente de estándares que encontraste en el paso 3, **más el smell baseline del paso 3 pegado completo** — el sub-agente no tiene otro acceso a él.
79
+ - La lista de archivos-fuente de estándares que encontraste en el paso 3, **más el smell baseline del paso 3 pegado completo, y el baseline de seguridad y de front cuando aplique** — el sub-agente no tiene otro acceso a él.
71
80
  - El encargo: "Reporta — por archivo/hunk donde proceda — (a) cada lugar donde el diff viola un estándar documentado: cita el estándar (archivo + la regla); y (b) cualquier smell del baseline que detectes: nómbralo y cita el hunk. Distingue violaciones duras de juicios — las infracciones de estándar documentado pueden ser duras, pero los smells del baseline son siempre juicios, y un estándar documentado del repo prevalece sobre el baseline. Sáltate lo que el tooling imponga. Menos de 400 palabras."
72
81
 
73
82
  **Prompt del sub-agente de Spec** — incluye:
@@ -66,7 +66,15 @@ Encargo de cada subagente del paso 2 de [`SKILL.md`](SKILL.md). Copia el **marco
66
66
  - longitud de los textos;
67
67
  - inyección de prompt con efecto en las herramientas;
68
68
  - fórmulas en el Excel.
69
- - Límite de peticiones y de gasto.
69
+ - Límite de peticiones y de gasto:
70
+ - por IP y por identidad, con la IP real tras el proxy;
71
+ - más estricto en login, OTP, registro y endpoints que cuestan dinero;
72
+ - un contador en memoria no vale con más de una réplica.
73
+ - Validación en el servidor, no solo en el navegador: rehacer a mano la petición con el campo que el formulario bloquearía.
74
+ - Sesiones que vencen, rotación del refresh y cookie `HttpOnly`/`Secure`/`SameSite`.
75
+ - Contraseñas con hash fuerte (Argon2id o bcrypt) y datos sensibles cifrados.
76
+ - CORS con lista de orígenes, nunca `*` con credenciales.
77
+ - Secretos de producción en la plataforma (Vercel, Supabase, GitHub), nunca solo en `.env` ni en el repo.
70
78
  - Errores que filtran detalles internos, cabeceras, CSP y CORS.
71
79
  - Front: `innerHTML` y dónde se guarda la sesión.
72
80
  - Dependencias: si están fijadas y si algo las escanea. `npm audit` se puede lanzar porque solo lee.
@@ -334,6 +334,8 @@ const r = schema.safeParse(Object.fromEntries(formData));
334
334
  if (!r.success) return { status: "error", message: "Invalid" };
335
335
  ```
336
336
 
337
+ Server vs Client placement, what breaks hydration and the stable-first-render fix: `references/rendering-hydration.md`.
338
+
337
339
  ## Auth & security (deep dive → references/security.md)
338
340
 
339
341
  Defense in depth with **three layers — middleware is NOT one of them**. Full wiring (Auth.js v5
@@ -0,0 +1,53 @@
1
+ # Rendering and hydration in the App Router
2
+
3
+ Short version of the five-word model (component, state, hooks, rendering, hydration) applied to
4
+ Next.js. The generic model is in `../../react/references/mental-model.md` when `react` is installed.
5
+
6
+ ## Where each piece runs
7
+
8
+ | Piece | Runs | Can use | Cannot use |
9
+ |---|---|---|---|
10
+ | **Server Component** (default) | Server only, at request or build time | `await`, DB, secrets, `fs` | state, effects, event handlers, browser APIs |
11
+ | **Client Component** (`"use client"`) | Server for the first HTML **and** the browser after | state, effects, handlers | secrets (anything it imports ships to the browser) |
12
+ | **Server Action** (`"use server"`) | Server, called from the browser | DB, secrets | trusting its input: it is a public endpoint |
13
+
14
+ ## Hydration, step by step
15
+
16
+ 1. The server renders the component tree to HTML and streams it.
17
+ 2. The browser paints that HTML (fast, not yet interactive).
18
+ 3. The JS loads and React **hydrates**: it renders the client components again and expects the
19
+ *same* output to attach handlers to the existing DOM.
20
+ 4. If the output differs, React warns (and may discard and re-render the subtree).
21
+
22
+ ## What breaks hydration
23
+
24
+ - `Date.now()`, `new Date()` formatting, `Math.random()`, `crypto.randomUUID()` in the render body.
25
+ - `typeof window !== "undefined"` branches that change the markup.
26
+ - Locale/time-zone formatting that differs between server and browser.
27
+ - Invalid nesting (`<div>` inside `<p>`, `<a>` inside `<a>`) that the browser "fixes" before React looks.
28
+ - Reading `localStorage`/`matchMedia` during render.
29
+
30
+ **Fix pattern:** render a stable first output, then update after mount.
31
+
32
+ ```tsx
33
+ "use client";
34
+ import { useEffect, useState } from "react";
35
+
36
+ export function LocalTime({ iso }: { iso: string }) {
37
+ const [text, setText] = useState(iso); // same on server and first client render
38
+ useEffect(() => setText(new Date(iso).toLocaleString()), [iso]); // browser-only, after hydration
39
+ return <time dateTime={iso}>{text}</time>;
40
+ }
41
+ ```
42
+
43
+ For values that must exist in render and legitimately differ (theme from a cookie), read them on
44
+ the server and pass them down, so both sides agree.
45
+
46
+ ## Rules
47
+
48
+ - Keep `"use client"` as deep (as leaf-like) as possible; a client boundary drags its imports into
49
+ the bundle.
50
+ - Pass the client component data, not the secret that produced it.
51
+ - Treat every Server Action and Route Handler as a public API: authenticate, authorize the object
52
+ and validate the input again. See `secure-coding` (the 10 holes, points 5–7).
53
+ - `suppressHydrationWarning` is for a single known-different text node, never a blanket fix.
@@ -0,0 +1,60 @@
1
+ ---
2
+ name: no-me-rayes
3
+ description: "Use on EVERY reply and progress update, especially after tool work or a long task: the assistant must report in short bullet points what is done, what it is doing now, what blocks it, and a final summary, each marked with its own emoji. Cuts the wall-of-text answers some models give. NOT rewriting a text to sound human (that is `bro`), NOT a plain-language explanation (that is `eli5`), NOT a session handoff document (that is `handoff`)."
4
+ tags: [no-me-rayes, concise, bullets, status-report, progress, emojis, short-answers]
5
+ recommends: [bro, eli5, handoff]
6
+ profiles: [minimal, core, ui, full]
7
+ origin: damiangil
8
+ ---
9
+
10
+ # No me rayes
11
+
12
+ Hay modelos que hablan demasiado. El usuario no quiere párrafos: quiere ver de un vistazo qué pasó. Esta skill se aplica **siempre**, en cada respuesta que cuente trabajo, y la forma no se negocia.
13
+
14
+ ## Formato obligatorio
15
+
16
+ Cada bloque lleva su emoji y sus viñetas. Una viñeta, una idea, una línea.
17
+
18
+ ```
19
+ ✅ Hecho
20
+ - Creé `skills/no-me-rayes/SKILL.md`
21
+ - Pasé los tests de la base
22
+
23
+ 🔄 Ahora
24
+ - Regenerando el manifest
25
+
26
+ ⛔ Bloqueos
27
+ - Falta el token de ngrok en `.env`
28
+
29
+ 📌 Resumen
30
+ - La skill ya está en la base; falta regenerar el manifest
31
+ - Necesito el token para seguir con el túnel
32
+ ```
33
+
34
+ | Bloque | Emoji | Qué va dentro |
35
+ | --- | --- | --- |
36
+ | Hecho | ✅ | Lo que ya está terminado y comprobado. |
37
+ | Ahora | 🔄 | Lo que haces en este momento. Una cosa, no tu plan entero. |
38
+ | Bloqueos | ⛔ | Lo que te frena y qué necesitas del usuario para seguir. |
39
+ | Resumen | 📌 | Dos o tres viñetas con lo esencial, para quien solo lea esto. |
40
+
41
+ ## Reglas
42
+
43
+ 1. **Viñetas, nunca párrafos.** Ninguna viñeta pasa de una línea. Si no cabe, se parte en dos viñetas.
44
+ 2. **Solo los bloques que tengan algo.** Si no hay bloqueos, no escribas «Bloqueos: ninguno»: omite el bloque. `📌 Resumen` va siempre al final.
45
+ 3. **Resumen al cerrar un trabajo largo.** Tras varios pasos o herramientas, `📌 Resumen` es lo que el usuario lee de verdad: que se entienda sin subir.
46
+ 4. **Lo hecho es lo comprobado.** No pongas ✅ a algo que no has ejecutado o verificado; va en 🔄 o se dice que no se probó.
47
+ 5. **El bloqueo lleva su salida.** Cada ⛔ dice qué hace falta y de quién, no solo que algo falla.
48
+ 6. **Sin relleno.** Nada de «Claro, voy a…», ni de repetir lo que ya se vio en la herramienta, ni de cerrar con un párrafo de cortesía.
49
+ 7. **Código, rutas y comandos van exactos**, entre comillas inversas; no se resumen ni se parafrasean.
50
+ 8. **Una pregunta de una frase o una respuesta de una línea** no necesita los bloques: responde en una línea. Los bloques son para contar trabajo.
51
+
52
+ ## Cómo convive con el resto
53
+
54
+ - **`orient`** cierra cada turno con su bloque-brújula (📍 ✅ 🧭 ➡️). Esta skill manda sobre **el cuerpo** de la respuesta; la brújula va después, sin repetir lo que ya dijo `✅ Hecho`.
55
+ - **`bro`** reescribe un texto para que suene humano; **`eli5`** explica sin jerga. Si el usuario los pide, mandan ellos sobre el contenido; el formato de viñetas se mantiene.
56
+ - **`handoff`** genera un documento de traspaso para otro agente; esto es el parte del turno, no ese documento.
57
+
58
+ ## Hecho cuando
59
+
60
+ La respuesta se entiende leyendo solo los emojis y la primera palabra de cada viñeta.
@@ -0,0 +1,33 @@
1
+ skill: no-me-rayes
2
+
3
+ should_trigger:
4
+ - prompt: "Hablas demasiado, dime solo qué has hecho."
5
+ why: "Disparador directo de no-me-rayes."
6
+ - prompt: "No me rayes con párrafos, ponlo en viñetas."
7
+ why: "Disparador directo de no-me-rayes."
8
+ - prompt: "Dame un parte de cómo va la tarea: qué está hecho, qué haces ahora y qué te bloquea."
9
+ why: "Disparador directo de no-me-rayes."
10
+ - prompt: "Acabas de tocar siete archivos. Resúmelo en viñetas con emojis."
11
+ why: "Disparador directo de no-me-rayes."
12
+ - prompt: "Your answers are too long; give me done, doing and blocked as short bullets."
13
+ why: "Disparador directo de no-me-rayes."
14
+
15
+ should_not_trigger:
16
+ - prompt: "Reescribe este correo para que suene más natural y humano."
17
+ route_to: "bro"
18
+ why: "Reescribir un texto para que suene humano es el trabajo de bro, no de no-me-rayes."
19
+ - prompt: "Explícame qué es un túnel como si tuviera cinco años."
20
+ route_to: "eli5"
21
+ why: "Explicar sin jerga es el trabajo de eli5, no de no-me-rayes."
22
+ - prompt: "Prepara un documento de traspaso para que otro agente continúe esta conversación."
23
+ route_to: "handoff"
24
+ why: "El documento de traspaso entre agentes es el trabajo de handoff, no de no-me-rayes."
25
+ - prompt: "Haz una retrospectiva de esta sesión y dime qué cambiar en el entorno del agente."
26
+ route_to: "retro"
27
+ why: "La retrospectiva de una sesión es el trabajo de retro, no de no-me-rayes."
28
+
29
+ capability:
30
+ - scenario: "El agente acaba de hacer varios cambios y debe contárselo al usuario."
31
+ must_include:
32
+ - "✅"
33
+ - "📌"
@@ -36,6 +36,8 @@ Adjacent jobs route out: the type system itself (generics, discriminated unions,
36
36
 
37
37
  ## Component & state architecture
38
38
 
39
+ The five words behind most front-end bugs (component, state, hooks, rendering, hydration) and a symptom → cause table: `references/mental-model.md`.
40
+
39
41
  - **Colocate state** next to where it's used; only **lift** when two siblings must share it. Lifting earlier than needed is the #1 cause of re-render spread.
40
42
  - **Derive, don't duplicate.** If a value is computable from props/state, compute it in render — never mirror it into another `useState` synced by an effect.
41
43
  - **Context for low-frequency, wide values** (theme, current user, locale). It re-renders every consumer on every change.
@@ -0,0 +1,38 @@
1
+ # The front-end mental model — five words that explain most bugs
2
+
3
+ Frameworks change every few months; these five ideas do not. When something on screen is wrong,
4
+ find which of the five is misplaced before touching code.
5
+
6
+ | Word | What it is | The mistake it explains |
7
+ |---|---|---|
8
+ | **Component** | A function that takes props and returns UI. A boundary, not a file. | One giant component that fetches, formats and renders: nothing reusable, everything re-renders. |
9
+ | **State** | Data that changes over time and that the screen must reflect. | State copied from props, or derived values stored (`fullName` in `useState`) and left to drift. |
10
+ | **Hooks** | The way a function component remembers (`useState`) or reaches outside itself (`useEffect`). | Effects used to *compute* things (derive in render instead), missing/extra dependencies, hooks called in conditions. |
11
+ | **Rendering** | Running your component to produce a description of the UI; React diffs it and updates the DOM. | Thinking render is a one-off: it runs on every state/prop change, so it must be pure and cheap. |
12
+ | **Hydration** | In SSR/SSG, the server sends HTML; the browser then attaches React to it so it becomes interactive. | Server and client producing different output (`Date.now()`, `Math.random()`, `window`, locale formatting): hydration mismatch. |
13
+
14
+ ## Rules that follow
15
+
16
+ - **State** goes to the outermost place that still needs it and no further (URL → server → shared
17
+ parent → component). See [`../SKILL.md`](../SKILL.md) and `ui-engineering`.
18
+ - **Derive, don't mirror.** If it can be computed from props/state during render, compute it there.
19
+ - **Render is pure.** No fetches, timers, `localStorage`, random or time reads in the body. Those
20
+ go in event handlers or effects.
21
+ - **An effect is for synchronizing with something outside React** (a subscription, the DOM, a
22
+ network call). If you are using it to set state from other state, delete it.
23
+ - **Hydration needs identical first output.** Anything that differs between server and browser
24
+ renders after mount (`useEffect` sets it) or is marked with `suppressHydrationWarning` only for
25
+ genuinely unavoidable text (a timestamp). Never use that flag to silence a real mismatch.
26
+ - **Server vs client boundary** (frameworks with server components): data and secrets stay on the
27
+ server side; the client component gets only what it needs. A secret read in a client component
28
+ is public. See `secure-coding` hole 3 and 6.
29
+
30
+ ## Quick diagnosis
31
+
32
+ | Symptom | Look first at |
33
+ |---|---|
34
+ | "Text content does not match server-rendered HTML" | Hydration: a non-deterministic value in the first render |
35
+ | Value is one step behind / stale in a handler | State: closure over an old value; use the functional update or derive it |
36
+ | Infinite re-render loop | Hooks: an effect that sets state it depends on |
37
+ | Whole tree re-renders on every keystroke | State: it lives too high; colocate it or select a narrow slice |
38
+ | Works on reload, breaks on navigation (or reverse) | State: it should be in the URL, or an effect ran once too often |
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: secure-coding
3
- description: "Use when securing application code: threat-modeling a feature, security-reviewing a diff, hardening auth/authz, secrets, CORS/CSP, or fixing OWASP-class bugs (access control, injection, SSRF, supply chain) in FastAPI, Next.js, Go or Flutter. NOT running the scanners (that is security-scan), NOT agent prompt-injection guardrails (that is agent-safety)."
3
+ description: "Use when securing application code: threat-modeling a feature, security-reviewing a diff, hardening auth/authz, secrets, CORS/CSP, running the pre-publish checklist (rate limiting, RLS, server-side validation) before shipping an AI-built project, or fixing OWASP-class bugs (access control, injection, SSRF, supply chain) in FastAPI, Next.js, Go or Flutter. NOT running the scanners (that is security-scan), NOT agent prompt-injection guardrails (that is agent-safety)."
4
4
  tags: [security, owasp, stride, auth, review]
5
5
  recommends: [deployment]
6
6
  origin: risco
@@ -291,6 +291,15 @@ export default { async headers() { return [{ source: "/:path*", headers }]; } };
291
291
 
292
292
  Full runbook: `references/secrets-and-supply-chain.md`.
293
293
 
294
+ ## Before publishing — the 10 holes
295
+
296
+ Anything built fast with AI, before it goes public and again before each important launch: rate
297
+ limiting, keys out of the code, production secrets in the platform, per-IP limits, validated and
298
+ parameterized inputs, **validation on the server (the browser's is UX)**, RLS so each user sees only
299
+ their data, hashed passwords and encrypted data, sessions that expire, CORS with only your domain.
300
+ Each point has a concrete proof that it is closed, never "looks fine": `references/pre-publish.md`.
301
+ It is a starting point, not an audit; the audit is [`hardening`](../hardening/SKILL.md).
302
+
294
303
  ## Anti-patterns
295
304
 
296
305
  | What gets said | Reality |
@@ -0,0 +1,47 @@
1
+ # Before publishing — the 10 holes
2
+
3
+ A project built fast with AI ships the same ten holes again and again. This is a starting
4
+ point, **not an audit** (the audit is [`hardening`](../../hardening/SKILL.md)). Run it before
5
+ publishing and again before every important launch, about once a month.
6
+
7
+ For each point: what goes wrong, how to **prove** it is closed (a command, a request, a query —
8
+ never "looks fine"), and where the long version lives.
9
+
10
+ | # | Hole | Prove it is closed |
11
+ |---|---|---|
12
+ | 1 | **Rate limiting** | Fire 20–50 requests in a few seconds at login, OTP, signup, search and any endpoint that costs money (LLM, email, SMS). Expect `429` well before the end. |
13
+ | 2 | **Keys out of the code** | `gitleaks dir . --redact`; `git log -p -S"sk-" --all` for the history; `grep -rn "process.env" src` only reads names, never literals. `.env` is in `.gitignore` and not in `git ls-files`. |
14
+ | 3 | **Production secrets in the platform** | The values live in Vercel / Supabase / GitHub Actions secrets, not in the repo or in a chat. Only `NEXT_PUBLIC_*` (or the framework's public prefix) reaches the browser: check that no secret carries it. |
15
+ | 4 | **Limits per IP (and per identity)** | The limiter keys on IP **and** on user/session. Behind a proxy, it reads the real client IP (trusted `X-Forwarded-For` hop), not the proxy's. In-memory counters break with more than one replica: use Redis/KV. |
16
+ | 5 | **Validated inputs, parameterized queries** | Every body, query and param passes a schema (Zod `.strict()`, Pydantic, validator) at the boundary. No string-built SQL: `grep -rnE "(query|execute)\(.*(\+|\\$\{)"`. Send `'; --` and `<script>` and see a `400`, not a `500`. |
17
+ | 6 | **Validation on the server** | The browser validating is UX. Re-send the request by hand (`curl`, DevTools "Copy as cURL") with the field the form would have blocked. The server must refuse it. Server Actions and API routes are public. |
18
+ | 7 | **RLS: each user sees only their data** | Every table exposed to the client has RLS **on** and a policy per operation. Log in as user A, request user B's row id: expect empty/`403`. `service_role` never ships to the browser. |
19
+ | 8 | **Passwords hashed, data encrypted** | Argon2id (or bcrypt cost ≥ 12), never SHA-256/MD5, never plain. HTTPS only (HSTS). Sensitive columns (tokens, ids, health/payment data) encrypted at rest, and never written to logs. |
20
+ | 9 | **Sessions that expire** | Short-lived access token, refresh rotation, `HttpOnly` + `Secure` + `SameSite` cookie, logout invalidates server-side. Check: a token from yesterday is rejected. |
21
+ | 10 | **CORS only with your domain** | `Access-Control-Allow-Origin` is an explicit allowlist, never `*` with credentials. Check: `curl -H "Origin: https://evil.example" -I <api>` must not echo that origin back. |
22
+
23
+ ## The connection map (what each side may be trusted with)
24
+
25
+ ```
26
+ Browser ──request──▶ Server ──query──▶ Database
27
+ │ validated here │ validate AGAIN here │ RLS decides here
28
+ └─ UX only └─ the real gate └─ last line of defence
29
+ ```
30
+
31
+ An attacker never uses your browser: they replay or forge the request. Every check that lives
32
+ only in the left box is decoration. Trust nothing that crossed the network; re-check identity,
33
+ ownership and shape at the server, and let the database refuse what the server forgot.
34
+
35
+ ## How to use it in a task
36
+
37
+ - **Writing code** that touches auth, an endpoint, a table or a secret: name which of the ten the
38
+ change affects and close it in the same change, with the proof above.
39
+ - **Reviewing a diff** (`code-review`): any new route, table, env var or form must show its
40
+ matching rows. A route with no rate limit and no server validation is a finding, not a nit.
41
+ - **Specifying a feature** (`to-spec`): the "Security" decisions section lists the rows that apply
42
+ and the ones that explicitly do not.
43
+ - **Found a hole:** rotate the credential first, then fix the code, then add the test that fails
44
+ without the fix.
45
+
46
+ Detail per topic: [`owasp-by-stack.md`](owasp-by-stack.md), [`authn-authz.md`](authn-authz.md),
47
+ [`secrets-and-supply-chain.md`](secrets-and-supply-chain.md).
@@ -61,6 +61,13 @@ NO incluyas rutas de archivo concretas ni snippets de código. Pueden quedar obs
61
61
 
62
62
  Excepción: si un prototipo produjo un snippet que codifica una decisión con más precisión que la prosa (máquina de estados, reducer, schema, forma de un tipo), inclúyelo dentro de la decisión correspondiente y anota brevemente que salió de un prototipo. Recórtalo a las partes ricas en decisión — no una demo funcional, solo lo importante.
63
63
 
64
+ ## Decisiones de seguridad y de front
65
+
66
+ Solo si la feature toca autenticación, un endpoint, una tabla, un secreto o una pantalla. Si no toca nada de eso, escribe «No aplica» y por qué.
67
+
68
+ - **Seguridad.** De los 10 puntos de `secure-coding` (`references/pre-publish.md`: rate limiting, claves fuera del código, secretos de producción, límites por IP, entradas validadas, validación en el servidor, RLS, hash y cifrado, sesiones que vencen, CORS), anota los que aplican a esta feature y los que quedan fuera a propósito. Cada uno que aplique se convierte en una historia de usuario o en un criterio de aceptación comprobable.
69
+ - **Front.** Dónde vive el estado de cada pieza nueva (URL, servidor, padre compartido o componente), qué es componente de servidor y qué de cliente, y qué primer render debe ser idéntico en servidor y navegador (hidratación). Detalle en `react` y `nextjs` (`references/mental-model.md` y `references/rendering-hydration.md`).
70
+
64
71
  ## Decisiones de testing
65
72
 
66
73
  Una lista de las decisiones de testing tomadas. Incluye:
package/targets/claude.js CHANGED
@@ -10,11 +10,17 @@ const HERE = dirname(fileURLToPath(import.meta.url));
10
10
  const SHELL_TOOLS = 'Bash|PowerShell';
11
11
 
12
12
  // Mods de la terminal de Claude Code que el harness deja activados en cada proyecto.
13
+ // Los cuatro `@harness-mods` viven en este mismo repo (`mods/plugins/`, catálogo de los mods en
14
+ // la raíz del repo): context-bar, prompt-enhancer, modelo-justo y where-am-i.
13
15
  // `cache-tax` mantiene caliente la caché del prompt en las pausas, y eso gasta tokens.
14
16
  export const CLAUDE_CODE_MODS = Object.freeze([
15
17
  { id: 'skins@hellosverre-mods', marketplace: 'hellosverre-mods', repo: 'hellosverre/claude-skins' },
16
18
  { id: 'savvy-progress@claude-kit', marketplace: 'claude-kit', repo: 'johnnyvizz/claude-kit' },
17
19
  { id: 'cache-tax@claude-code-mods', marketplace: 'claude-code-mods', repo: 'karanb192/claude-code-mods' },
20
+ { id: 'context-bar@harness-mods', marketplace: 'harness-mods', repo: 'damiangilgonzalez1995/rsc-harness' },
21
+ { id: 'prompt-enhancer@harness-mods', marketplace: 'harness-mods', repo: 'damiangilgonzalez1995/rsc-harness' },
22
+ { id: 'modelo-justo@harness-mods', marketplace: 'harness-mods', repo: 'damiangilgonzalez1995/rsc-harness' },
23
+ { id: 'where-am-i@harness-mods', marketplace: 'harness-mods', repo: 'damiangilgonzalez1995/rsc-harness' },
18
24
  ]);
19
25
 
20
26
  export function writeSkill(id, fromDir, toPath) {
@@ -43,7 +43,7 @@ const SEMVER = /^\d+\.\d+\.\d+(?:-[0-9a-z.-]+)?$/i;
43
43
  * nothing checks is just drift with a head start.
44
44
  */
45
45
  export const PROJECT_OPT_OUTS = [
46
- 'audit', 'claudemd-check', 'danger-guard', 'feature-gate', 'gitmoji', 'knowledge-sync', 'ship-guard', 'trunk-guard', 'trunk-open', 'worktree-cleanup', 'worktree-isolation',
46
+ 'audit', 'claudemd-check', 'danger-guard', 'feature-gate', 'gitmoji', 'knowledge-sync', 'ship-guard', 'stack-hints', 'trunk-guard', 'trunk-open', 'worktree-cleanup', 'worktree-isolation',
47
47
  ];
48
48
 
49
49
  /**
@@ -24,6 +24,7 @@ export const PROJECT_OPT_OUTS = [
24
24
  'gitmoji', // the commit-message convention
25
25
  'knowledge-sync', // 01-TOOLS/ + docs/ sync: one person off and the team stops seeing their work
26
26
  'ship-guard', // branch/trunk discipline
27
+ 'stack-hints', // the session-start reminder that matches the stack (security floor, front-end model)
27
28
  'trunk-guard', // «desbloquea main»: the default branch open for the agent, for the whole team
28
29
  'trunk-open', // «ramas y PR»: the default branch closed even with nothing that looks complex
29
30
  'worktree-cleanup', // the reaper's notice (it only ever names, never acts)
@@ -166,6 +166,53 @@ Originals are copied, never moved; deleting an emptied folder needs explicit con
166
166
  }
167
167
  }
168
168
 
169
+ // Stack hints: a short reminder that matches what THIS project is made of, so it costs nothing
170
+ // where it does not apply. The detail lives in the skills (`secure-coding`, `react`, `nextjs`);
171
+ // these lines are only the part that must be present before anyone asks. Emitted once per session
172
+ // (emitBody de-dups across scopes). Opt out with .rsc/.no-stack-hints.
173
+ function readText(...p) { try { return readFileSync(join(root, ...p), 'utf8'); } catch { return ''; } }
174
+ function stackOf() {
175
+ const pkg = readText('package.json');
176
+ const py = readText('pyproject.toml') + readText('requirements.txt');
177
+ const dep = (re) => re.test(pkg);
178
+ const front = dep(/"(react|preact|next|vue|nuxt|svelte|@sveltejs\/kit|solid-js|astro)"/);
179
+ const back = dep(/"(express|fastify|hono|koa|@nestjs\/core|next|@supabase\/supabase-js|prisma|@prisma\/client|drizzle-orm|firebase)"/)
180
+ || /fastapi|django|flask/i.test(py) || has('go.mod') || has('supabase')
181
+ || has('.env') || has('.env.local') || has('.env.example') || has('app', 'api') || has('pages', 'api');
182
+ return { front, back };
183
+ }
184
+ const skillIn = (id) => [root, homedir()].some((r) => existsSync(join(r, '.claude', 'skills', id, 'SKILL.md')));
185
+ if (emitBody && !has('.rsc', '.no-stack-hints')) {
186
+ const { front, back } = stackOf();
187
+ if (back) {
188
+ process.stdout.write(`
189
+ ===== rsc security floor =====
190
+ This project has a backend, an API or secrets. Before shipping, and in any change that touches auth,
191
+ an endpoint, a table or a key, close these ten and PROVE each one (a request, a query, a command):
192
+ rate limiting · keys out of the code (.env) · production secrets in the platform (Vercel, Supabase,
193
+ GitHub) · limits per IP and per identity · validated inputs + parameterized queries · validation on
194
+ the SERVER (the browser's is UX) · RLS: each user sees only their data · hashed passwords, encrypted
195
+ data · sessions that expire · CORS only with your own domain.
196
+ Browser validates -> server validates too -> database refuses what the server forgot.
197
+ It is a starting point, not an audit.${skillIn('secure-coding') ? ' Detail and proofs: skill `secure-coding`, references/pre-publish.md. Audit: `hardening`.' : ''}
198
+ Opt out with .rsc/.no-stack-hints.
199
+ ==============================
200
+ `);
201
+ }
202
+ if (front) {
203
+ process.stdout.write(`
204
+ ===== rsc front-end model =====
205
+ When building the front: component (a boundary, not a file) · state (outermost place that still
206
+ needs it: URL, server, shared parent, component; derive, never mirror) · hooks (an effect syncs
207
+ with the outside world, it does not compute) · rendering (pure, runs on every change) · hydration
208
+ (server HTML and first client render must match: no Date.now/Math.random/window in render).
209
+ Secrets and server-only data never go into a client component.${skillIn('react') || skillIn('nextjs') ? ' Detail: skills `react`, `nextjs` (references/mental-model.md, rendering-hydration.md).' : ''}
210
+ Opt out with .rsc/.no-stack-hints.
211
+ ===============================
212
+ `);
213
+ }
214
+ }
215
+
169
216
  // Git required: version control is the substrate the SDD chain + ship guard assume.
170
217
  // If the project is not under git, push for it once per session. Persist the user's
171
218
  // "no" as .rsc/.no-git so the decision lives in a file and we stop re-asking.