argos-harness 0.1.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.
Files changed (217) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +21 -0
  3. package/assets/agents/auditor.md +140 -0
  4. package/assets/agents/commit-pr-pilot.md +154 -0
  5. package/assets/agents/explorer.md +93 -0
  6. package/assets/agents/implementer.md +117 -0
  7. package/assets/agents/leader.md +149 -0
  8. package/assets/agents/researcher.md +89 -0
  9. package/assets/agents/review-readability.md +87 -0
  10. package/assets/agents/review-reliability.md +100 -0
  11. package/assets/agents/review-resilience.md +87 -0
  12. package/assets/agents/review-risk.md +87 -0
  13. package/assets/agents/reviewer.md +167 -0
  14. package/assets/agents/ticket-audit.md +129 -0
  15. package/assets/hooks/argos-guard-destructive.sh +127 -0
  16. package/assets/hooks/argos-quality-gate.sh +129 -0
  17. package/assets/managed/aterrizaje.md +24 -0
  18. package/assets/managed/formato-respuesta.md +21 -0
  19. package/assets/managed/identidad.md +48 -0
  20. package/assets/managed/operaciones-seguras.md +14 -0
  21. package/assets/managed/orquestacion.md +169 -0
  22. package/assets/output-styles/argos.md +71 -0
  23. package/assets/skills/ai-sdk-5/SKILL.md +230 -0
  24. package/assets/skills/angular/SKILL.md +19 -0
  25. package/assets/skills/angular/references/architecture.md +137 -0
  26. package/assets/skills/angular/references/core.md +197 -0
  27. package/assets/skills/angular/references/forms.md +115 -0
  28. package/assets/skills/angular/references/performance.md +124 -0
  29. package/assets/skills/apollo-client/SKILL.md +61 -0
  30. package/assets/skills/app-blueprint/SKILL.md +45 -0
  31. package/assets/skills/app-blueprint/assets/module-template.md +48 -0
  32. package/assets/skills/app-blueprint/assets/system-template.md +38 -0
  33. package/assets/skills/app-blueprint/references/workflow.md +101 -0
  34. package/assets/skills/app-builder/SKILL.md +43 -0
  35. package/assets/skills/app-builder/phases/0-product.md +58 -0
  36. package/assets/skills/app-builder/phases/1-scaffold.md +33 -0
  37. package/assets/skills/app-builder/phases/10-store.md +40 -0
  38. package/assets/skills/app-builder/phases/2-data.md +34 -0
  39. package/assets/skills/app-builder/phases/3-domain.md +33 -0
  40. package/assets/skills/app-builder/phases/4-ui-nav.md +32 -0
  41. package/assets/skills/app-builder/phases/5-identity.md +30 -0
  42. package/assets/skills/app-builder/phases/6-polish.md +34 -0
  43. package/assets/skills/app-builder/phases/7-brand.md +31 -0
  44. package/assets/skills/app-builder/phases/8-web.md +30 -0
  45. package/assets/skills/app-builder/phases/9-docs.md +31 -0
  46. package/assets/skills/app-ia/SKILL.md +54 -0
  47. package/assets/skills/astro/SKILL.md +39 -0
  48. package/assets/skills/axios/SKILL.md +61 -0
  49. package/assets/skills/branch-pr/SKILL.md +200 -0
  50. package/assets/skills/bullmq/SKILL.md +55 -0
  51. package/assets/skills/chained-pr/SKILL.md +48 -0
  52. package/assets/skills/chained-pr/references/chaining-details.md +99 -0
  53. package/assets/skills/cognitive-doc-design/SKILL.md +81 -0
  54. package/assets/skills/comment-writer/SKILL.md +74 -0
  55. package/assets/skills/dashboard-ia/SKILL.md +54 -0
  56. package/assets/skills/django-drf/SKILL.md +180 -0
  57. package/assets/skills/go-testing/SKILL.md +47 -0
  58. package/assets/skills/go-testing/references/examples.md +89 -0
  59. package/assets/skills/issue-creation/SKILL.md +223 -0
  60. package/assets/skills/jira-epic/SKILL.md +306 -0
  61. package/assets/skills/jira-task/SKILL.md +382 -0
  62. package/assets/skills/judgment-day/SKILL.md +52 -0
  63. package/assets/skills/judgment-day/references/prompts-and-formats.md +98 -0
  64. package/assets/skills/lightsail-deploy/SKILL.md +44 -0
  65. package/assets/skills/lightsail-deploy/references/runbook.md +102 -0
  66. package/assets/skills/loop-back-debug/SKILL.md +102 -0
  67. package/assets/skills/mantine-form/SKILL.md +57 -0
  68. package/assets/skills/mongoose/SKILL.md +66 -0
  69. package/assets/skills/nextjs-15/SKILL.md +144 -0
  70. package/assets/skills/not-boring-mobile/SKILL.md +43 -0
  71. package/assets/skills/not-boring-mobile/references/not-boring-playbook.md +65 -0
  72. package/assets/skills/playwright/SKILL.md +315 -0
  73. package/assets/skills/pr-comments/SKILL.md +93 -0
  74. package/assets/skills/pr-create/SKILL.md +64 -0
  75. package/assets/skills/promo-video/SKILL.md +52 -0
  76. package/assets/skills/promo-video/assets/package.template.json +22 -0
  77. package/assets/skills/promo-video/assets/promo.template.tsx +469 -0
  78. package/assets/skills/promo-video/assets/theme.template.ts +27 -0
  79. package/assets/skills/promo-video/references/pipeline.md +119 -0
  80. package/assets/skills/promo-video-web/SKILL.md +51 -0
  81. package/assets/skills/promo-video-web/assets/browser-promo.template.tsx +385 -0
  82. package/assets/skills/promo-video-web/assets/capture.template.ts +70 -0
  83. package/assets/skills/promo-video-web/assets/package.template.json +26 -0
  84. package/assets/skills/promo-video-web/references/pipeline.md +84 -0
  85. package/assets/skills/pytest/SKILL.md +180 -0
  86. package/assets/skills/react-19/SKILL.md +118 -0
  87. package/assets/skills/react-hook-form/SKILL.md +59 -0
  88. package/assets/skills/react-router/SKILL.md +60 -0
  89. package/assets/skills/redux-toolkit/SKILL.md +60 -0
  90. package/assets/skills/review-diff/SKILL.md +101 -0
  91. package/assets/skills/ship-docs/SKILL.md +45 -0
  92. package/assets/skills/ship-docs/references/ship-docs-playbook.md +31 -0
  93. package/assets/skills/skill-creator/SKILL.md +97 -0
  94. package/assets/skills/skill-creator/assets/SKILL-TEMPLATE.md +68 -0
  95. package/assets/skills/skill-creator/references/skill-style-guide.md +79 -0
  96. package/assets/skills/skill-improver/SKILL.md +50 -0
  97. package/assets/skills/skill-improver/references/skill-style-guide.md +79 -0
  98. package/assets/skills/socketio/SKILL.md +58 -0
  99. package/assets/skills/spec-bootstrap/SKILL.md +62 -0
  100. package/assets/skills/store-ship/SKILL.md +52 -0
  101. package/assets/skills/store-ship/assets/android-supply.template.md +22 -0
  102. package/assets/skills/store-ship/assets/eas.template.json +32 -0
  103. package/assets/skills/store-ship/assets/maestro-flow.template.yaml +27 -0
  104. package/assets/skills/store-ship/assets/store.config.template.json +28 -0
  105. package/assets/skills/store-ship/references/pipeline.md +198 -0
  106. package/assets/skills/stripe/SKILL.md +83 -0
  107. package/assets/skills/tailwind-4/SKILL.md +193 -0
  108. package/assets/skills/tamagui/SKILL.md +60 -0
  109. package/assets/skills/tanstack-query/SKILL.md +58 -0
  110. package/assets/skills/ticket-intake/SKILL.md +55 -0
  111. package/assets/skills/typescript/SKILL.md +134 -0
  112. package/assets/skills/verify-before-done/SKILL.md +111 -0
  113. package/assets/skills/webapp-rebuilder/SKILL.md +48 -0
  114. package/assets/skills/webapp-rebuilder/assets/charter-template.md +46 -0
  115. package/assets/skills/webapp-rebuilder/references/workflow.md +47 -0
  116. package/assets/skills/winston-logging/SKILL.md +61 -0
  117. package/assets/skills/work-unit-commits/SKILL.md +84 -0
  118. package/assets/skills/zod-4/SKILL.md +210 -0
  119. package/assets/skills/zustand-5/SKILL.md +216 -0
  120. package/bin/argos.js +6 -0
  121. package/dist/commands/adopt.d.ts +35 -0
  122. package/dist/commands/adopt.d.ts.map +1 -0
  123. package/dist/commands/adopt.js +347 -0
  124. package/dist/commands/adopt.js.map +1 -0
  125. package/dist/commands/doctor.d.ts +20 -0
  126. package/dist/commands/doctor.d.ts.map +1 -0
  127. package/dist/commands/doctor.js +478 -0
  128. package/dist/commands/doctor.js.map +1 -0
  129. package/dist/commands/init.d.ts +32 -0
  130. package/dist/commands/init.d.ts.map +1 -0
  131. package/dist/commands/init.js +358 -0
  132. package/dist/commands/init.js.map +1 -0
  133. package/dist/commands/remove.d.ts +62 -0
  134. package/dist/commands/remove.d.ts.map +1 -0
  135. package/dist/commands/remove.js +487 -0
  136. package/dist/commands/remove.js.map +1 -0
  137. package/dist/commands/workspace.d.ts +73 -0
  138. package/dist/commands/workspace.d.ts.map +1 -0
  139. package/dist/commands/workspace.js +354 -0
  140. package/dist/commands/workspace.js.map +1 -0
  141. package/dist/index.d.ts +2 -0
  142. package/dist/index.d.ts.map +1 -0
  143. package/dist/index.js +24 -0
  144. package/dist/index.js.map +1 -0
  145. package/dist/lib/assets.d.ts +29 -0
  146. package/dist/lib/assets.d.ts.map +1 -0
  147. package/dist/lib/assets.js +57 -0
  148. package/dist/lib/assets.js.map +1 -0
  149. package/dist/lib/atomic-write.d.ts +17 -0
  150. package/dist/lib/atomic-write.d.ts.map +1 -0
  151. package/dist/lib/atomic-write.js +41 -0
  152. package/dist/lib/atomic-write.js.map +1 -0
  153. package/dist/lib/backup.d.ts +11 -0
  154. package/dist/lib/backup.d.ts.map +1 -0
  155. package/dist/lib/backup.js +42 -0
  156. package/dist/lib/backup.js.map +1 -0
  157. package/dist/lib/config.d.ts +36 -0
  158. package/dist/lib/config.d.ts.map +1 -0
  159. package/dist/lib/config.js +52 -0
  160. package/dist/lib/config.js.map +1 -0
  161. package/dist/lib/detect.d.ts +58 -0
  162. package/dist/lib/detect.d.ts.map +1 -0
  163. package/dist/lib/detect.js +330 -0
  164. package/dist/lib/detect.js.map +1 -0
  165. package/dist/lib/ficha.d.ts +10 -0
  166. package/dist/lib/ficha.d.ts.map +1 -0
  167. package/dist/lib/ficha.js +38 -0
  168. package/dist/lib/ficha.js.map +1 -0
  169. package/dist/lib/git.d.ts +27 -0
  170. package/dist/lib/git.d.ts.map +1 -0
  171. package/dist/lib/git.js +79 -0
  172. package/dist/lib/git.js.map +1 -0
  173. package/dist/lib/managed-files.d.ts +35 -0
  174. package/dist/lib/managed-files.d.ts.map +1 -0
  175. package/dist/lib/managed-files.js +97 -0
  176. package/dist/lib/managed-files.js.map +1 -0
  177. package/dist/lib/markers.d.ts +64 -0
  178. package/dist/lib/markers.d.ts.map +1 -0
  179. package/dist/lib/markers.js +157 -0
  180. package/dist/lib/markers.js.map +1 -0
  181. package/dist/lib/navori-import.d.ts +42 -0
  182. package/dist/lib/navori-import.d.ts.map +1 -0
  183. package/dist/lib/navori-import.js +65 -0
  184. package/dist/lib/navori-import.js.map +1 -0
  185. package/dist/lib/openclaw-agents.d.ts +53 -0
  186. package/dist/lib/openclaw-agents.d.ts.map +1 -0
  187. package/dist/lib/openclaw-agents.js +118 -0
  188. package/dist/lib/openclaw-agents.js.map +1 -0
  189. package/dist/lib/package-root.d.ts +11 -0
  190. package/dist/lib/package-root.d.ts.map +1 -0
  191. package/dist/lib/package-root.js +23 -0
  192. package/dist/lib/package-root.js.map +1 -0
  193. package/dist/lib/paths.d.ts +11 -0
  194. package/dist/lib/paths.d.ts.map +1 -0
  195. package/dist/lib/paths.js +16 -0
  196. package/dist/lib/paths.js.map +1 -0
  197. package/dist/lib/settings-merge.d.ts +125 -0
  198. package/dist/lib/settings-merge.d.ts.map +1 -0
  199. package/dist/lib/settings-merge.js +373 -0
  200. package/dist/lib/settings-merge.js.map +1 -0
  201. package/dist/lib/version.d.ts +3 -0
  202. package/dist/lib/version.d.ts.map +1 -0
  203. package/dist/lib/version.js +13 -0
  204. package/dist/lib/version.js.map +1 -0
  205. package/dist/lib/which.d.ts +8 -0
  206. package/dist/lib/which.d.ts.map +1 -0
  207. package/dist/lib/which.js +30 -0
  208. package/dist/lib/which.js.map +1 -0
  209. package/dist/lib/workspaces.d.ts +133 -0
  210. package/dist/lib/workspaces.d.ts.map +1 -0
  211. package/dist/lib/workspaces.js +241 -0
  212. package/dist/lib/workspaces.js.map +1 -0
  213. package/dist/lib/zod-messages.d.ts +4 -0
  214. package/dist/lib/zod-messages.d.ts.map +1 -0
  215. package/dist/lib/zod-messages.js +28 -0
  216. package/dist/lib/zod-messages.js.map +1 -0
  217. package/package.json +44 -0
@@ -0,0 +1,52 @@
1
+ ---
2
+ name: judgment-day
3
+ description: Run blind dual review, fix confirmed issues, then re-judge. Trigger: judgment day, dual review, adversarial review, juzgar.
4
+ ---
5
+
6
+ ## Activation Contract
7
+
8
+ Load this skill only when the user explicitly asks for Judgment Day, dual/adversarial review, or equivalent Spanish trigger (`juzgar`, `que lo juzguen`). Review a specific target: files, feature, PR, or architecture slice.
9
+
10
+ ## Hard Rules
11
+
12
+ - Resolve project skills before launching agents: if the harness exposes a skill index, use it to resolve exact `SKILL.md` paths; otherwise, search the installed skills directory for the skills that match the target files/task by name. Inject the same `Skills to load before work` block into both judge prompts and fix prompts.
13
+ - Launch **two blind judges in parallel** with identical target and criteria; never review the code yourself.
14
+ - Wait for both judges before synthesis; never accept a partial verdict.
15
+ - Classify warnings as `WARNING (real)` only if normal intended use can trigger them; otherwise downgrade to INFO as `WARNING (theoretical)`.
16
+ - Ask before fixing Round 1 confirmed issues.
17
+ - After any fix agent runs, immediately re-launch both judges in parallel before commit/push/done/session summary.
18
+ - Terminal states are only `JUDGMENT: APPROVED` or `JUDGMENT: ESCALATED`.
19
+ - After 2 fix iterations with remaining issues, ask the user whether to continue.
20
+
21
+ ## Decision Gates
22
+
23
+ | Condition | Action |
24
+ |---|---|
25
+ | Target unclear | Ask for scope; do not launch judges. |
26
+ | No skill index and nothing found by name search | Warn, proceed with generic criteria, and record `Skill Resolution: none`. |
27
+ | Both judges find same CRITICAL/real WARNING | Confirmed; ask/fix according to round rules. |
28
+ | One judge finds issue | Suspect; report and triage, do not auto-fix. |
29
+ | Judges contradict | Escalate for manual decision. |
30
+ | Round 2+ has only theoretical warnings/suggestions | Report as INFO; do not re-judge. |
31
+
32
+ ## Execution Steps
33
+
34
+ 1. Confirm target and optional custom criteria.
35
+ 2. Resolve exact skill paths via the harness's skill index if one is available, or by searching the skills directory for matching `SKILL.md` files; warn if none are found.
36
+ 3. Start Judge A and Judge B concurrently via delegation.
37
+ 4. Synthesize findings into confirmed, suspect, contradiction, and INFO buckets.
38
+ 5. Ask before Round 1 fixes; delegate a separate fix agent for confirmed approved fixes only.
39
+ 6. Re-judge in parallel after fixes; repeat until approved, escalated, or user asks to stop.
40
+ 7. Before any terminal action, verify every active Judgment Day has a terminal state.
41
+
42
+ ## Output Contract
43
+
44
+ Return `## Judgment Day — {target}` with round number, verdict table, confirmed/suspect/contradiction counts, fixes applied, re-judgment result, `Skill Resolution`, and final `JUDGMENT: APPROVED ✅` or `JUDGMENT: ESCALATED ⚠️`.
45
+
46
+ ## References
47
+
48
+ - [references/prompts-and-formats.md](references/prompts-and-formats.md) — judge/fix prompts, warning rubric, verdict tables, and language snippets.
49
+
50
+ ---
51
+
52
+ _Adapted from a skill originally authored by gentleman-programming._
@@ -0,0 +1,98 @@
1
+ # Judgment Day Prompts and Formats
2
+
3
+ ## Judge Prompt
4
+
5
+ ```markdown
6
+ You are an adversarial code reviewer. Your ONLY job is to find problems.
7
+
8
+ ## Target
9
+ {files, feature, architecture, component}
10
+
11
+ ## Skills to load before work
12
+ {matching SKILL.md paths, if available}
13
+
14
+ ## Review Criteria
15
+ - Correctness: logical errors and behavior mismatches
16
+ - Edge cases: missing states, inputs, or platform constraints
17
+ - Error handling: propagation, logging, recovery
18
+ - Performance: N+1, wasteful loops, excessive allocations
19
+ - Security: injection, secrets, auth boundaries
20
+ - Naming/conventions: project standards and local patterns
21
+ {custom criteria, if provided}
22
+
23
+ ## Return Format
24
+ Findings only. No praise.
25
+
26
+ Each finding:
27
+ - Severity: CRITICAL | WARNING (real) | WARNING (theoretical) | SUGGESTION
28
+ - File: path/to/file.ext (line N if applicable)
29
+ - Description: what is wrong and why it matters
30
+ - Suggested fix: one-line intent
31
+
32
+ WARNING rule: normal intended use can trigger it → `WARNING (real)`; contrived/malicious/impossible path → `WARNING (theoretical)`.
33
+
34
+ If clean: `VERDICT: CLEAN — No issues found.`
35
+
36
+ Always end with: `Skill Resolution: {paths-injected|fallback-index|fallback-path|none} — {details}`.
37
+ ```
38
+
39
+ ## Fix Agent Prompt
40
+
41
+ ```markdown
42
+ You are a surgical fix agent. Apply ONLY the confirmed issues listed below.
43
+
44
+ ## Confirmed Issues to Fix
45
+ {confirmed findings table}
46
+
47
+ ## Skills to load before work
48
+ {matching SKILL.md paths, if available}
49
+
50
+ ## Instructions
51
+ - Fix only confirmed issues.
52
+ - Do not refactor beyond the required fix.
53
+ - Do not change unflagged code.
54
+ - If fixing a repeated pattern in touched files, fix all occurrences of that same pattern.
55
+ - Return changed file, line, and fix summary.
56
+
57
+ End with: `Skill Resolution: {paths-injected|fallback-index|fallback-path|none} — {details}`.
58
+ ```
59
+
60
+ ## Verdict Table
61
+
62
+ ```markdown
63
+ | Finding | Judge A | Judge B | Severity | Status |
64
+ |---------|---------|---------|----------|--------|
65
+ | Missing null check in auth.go:42 | ✅ | ✅ | CRITICAL | Confirmed |
66
+ | Windows volume root edge case | ❌ | ✅ | WARNING (theoretical) | INFO |
67
+ | Naming mismatch | ✅ | ❌ | SUGGESTION | Suspect |
68
+ ```
69
+
70
+ Approved criteria after Round 1: zero confirmed CRITICALs and zero confirmed real WARNINGs. Theoretical warnings and suggestions may remain.
71
+
72
+ ## Delegation Patterns
73
+
74
+ When JD agents are configured as named sub-agents (Claude Code's `jd-judge-a`, `jd-judge-b`, and `jd-fix-agent`, or an equivalent multi-mode overlay in another harness), use named delegation:
75
+
76
+ ```
77
+ Judge A: delegate(agent="jd-judge-a", prompt="...")
78
+ Judge B: delegate(agent="jd-judge-b", prompt="...")
79
+ Fix Agent: delegate(agent="jd-fix-agent", prompt="...")
80
+ ```
81
+
82
+ Each named agent uses its configured model from the Model Assignments table.
83
+
84
+ When named JD agents are NOT available (Cursor, Windsurf, Gemini, Codex, etc.), use the adapter's generic delegate syntax. These adapters do not support the `agent` parameter — all calls use the same delegate entry point and the model is controlled externally:
85
+
86
+ ```
87
+ // Generic delegate — no named agent support; adapter-native syntax
88
+ Judge A: delegate(prompt="...")
89
+ Judge B: delegate(prompt="...")
90
+ Fix Agent: delegate(prompt="...")
91
+ ```
92
+
93
+ The model is controlled by the adapter's native model-switching mechanism (e.g., model sentinels in agent .md files). Pass the model alias from the Model Assignments table if the adapter supports per-call model parameters.
94
+
95
+ ## Language Snippets
96
+
97
+ - Spanish: “Juicio iniciado”, “Los jueces trabajan en paralelo”, “Los jueces coinciden”, “Juicio terminado — Aprobado”, “Escalado — necesita revisión humana”.
98
+ - English: “Judgment initiated”, “Both judges are working in parallel”, “Both judges agree”, “Judgment complete — Approved”, “Escalated — requires human review”.
@@ -0,0 +1,44 @@
1
+ ---
2
+ name: lightsail-deploy
3
+ description: Operate and debug Bonum service deploys to AWS Lightsail (notifications, reports360). Trigger: deploy, despliegue, Lightsail, dev environment, deployment failed, container canceled, ECR pull.
4
+ ---
5
+
6
+ ## Activation Contract
7
+
8
+ Use when deploying any Bonum service to Lightsail, creating a new Lightsail service, debugging a failed/canceled deployment, or verifying a deploy end-to-end.
9
+
10
+ ## Hard Rules
11
+
12
+ - Deploys go through GitHub Actions only: push to `develop` (development) or `main` (production). No manual Lightsail deploys.
13
+ - Every NEW Lightsail service MUST have `ecrImagePullerRole` activated and its `principalArn` MERGED into the ECR repo policy — never overwrite existing statements. Without it, containers never start and logs stay empty.
14
+ - Never commit `.env`; local copies hold real credentials.
15
+ - Health checks accept success codes 200-499. A 404 on `/` is healthy.
16
+ - Docker builds use buildx with `cache-from/to: type=gha`.
17
+
18
+ ## Decision Gates
19
+
20
+ | Service | ECR repo | Secrets model |
21
+ |---|---|---|
22
+ | notifications--server | `notifications` | GitHub environments `development`/`production`, same secret names, env-scoped values |
23
+ | reports-server-bonum | `reports360` | Repo-level secrets (`MONGODB_URI`, AWS keys); env split happens in `config.py` via `ENVIRONMENT` |
24
+
25
+ | Symptom | Cause / Action |
26
+ |---|---|
27
+ | `Started 1 new node` ×3 then `Canceled`, empty logs | Image pull failure → check `ecrImagePullerRole` + ECR policy (runbook) |
28
+ | Logs empty right after deploy | Lightsail log lag is minutes — wait, don't rediagnose |
29
+ | App logs errors on boot | Check env vars/secrets for that service (runbook lists them) |
30
+ | Need AWS API without local CLI | Use `curl --aws-sigv4` recipes in runbook |
31
+
32
+ ## Execution Steps
33
+
34
+ 1. Merge/push to the target branch; the workflow tests (where applicable), builds, pushes to ECR, and calls `create-container-service-deployment`.
35
+ 2. Poll deployment state until `ACTIVE` (~5 min floor; Lightsail blue/green is not compressible).
36
+ 3. Verify end-to-end per service (runbook: test email payload for notifications, report endpoint for reports360).
37
+
38
+ ## Output Contract
39
+
40
+ Report deployment state (`ACTIVE`/`FAILED`), service URL, and log evidence of the service's external connections (Mongo, RabbitMQ, evaluations API).
41
+
42
+ ## References
43
+
44
+ - `references/runbook.md` — per-service topology, sigv4 curl recipes, new-service checklist, secret lists, known gotchas.
@@ -0,0 +1,102 @@
1
+ # Lightsail Deploy Runbook — Bonum services
2
+
3
+ Learned 2026-07-22/23 while bringing up the dev environments.
4
+
5
+ ## Shared topology
6
+
7
+ - AWS account `762614731979`, region `us-east-1` for ECR and Lightsail.
8
+ - ECR registry: `762614731979.dkr.ecr.us-east-1.amazonaws.com`.
9
+ - MongoDB Atlas: single shared cluster; env separation is per database, not per cluster.
10
+ - RabbitMQ: single shared broker `amqp://3.144.196.4` (notifications only).
11
+
12
+ ## Per-service topology
13
+
14
+ ### notifications--server (Node)
15
+
16
+ - ECR repo `notifications`; tags `notifications-dev-<sha>` (dev) / `notifications-<sha>` (prod).
17
+ - Dev: Lightsail `notifications-server-dev` (nano/512MB), URL `https://notifications-server-dev.tclik5ai2vodi.us-east-1.cs.amazonlightsail.com/`. Prod: `container-service-1` (small/1GB). Port 3002.
18
+ - Workflows: `develop.yml` + `main.yml` (near-clones; differ in branch, GitHub environment, service name).
19
+ - Queues `correos` (prod) / `correos_dev` (dev, hardcoded `EMAIL_QUEUE` in develop.yml). Queue is `durable: false`.
20
+ - Secrets in GitHub environments `development`/`production`: `AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, ONE_SIGNAL_REST_API_KEY, ONE_SIGNAL_API_URL, ONE_SIGNAL_APP_ID, EMAIL_PASSWORD, EMAIL_USER, EMAIL_FROM, SMTP_USER, SMTP_PASS, MONGO_USER, MONGO_PASSWORD, MONGO_DATABASE, MONGO_CLUSTER, NEXUS_API_URL` (+ `PORT`). Bulk upload: `gh secret set -f .env.dev --env development --repo bonumtech1/notifications--server`.
21
+ - `RABBITMQ_URL` intentionally absent in dev → falls back to shared prod broker with the `correos_dev` queue.
22
+
23
+ ### reports-server-bonum (Python/Flask)
24
+
25
+ - ECR repo `reports360`; tags `reports360v1` (prod) / `reports360-dev` (dev) — fixed tags, not per-sha.
26
+ - Prod: Lightsail `container-reports360`. Dev: `container-reports360-dev` (nano). Port 8000.
27
+ - Single workflow `deploy.yml` triggers on `main` AND `develop`; branch derives `DEPLOY_ENV`, `IMAGE_TAG`, `SERVICE_NAME` at job level.
28
+ - Secrets at REPO level (no GitHub environments by design): `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `MONGODB_URI`. Same Atlas URI both envs; `config.py` picks DB by `ENVIRONMENT`: production → `bonum_production` with fallback `bonum`; development → `bonum` only, plus dev evaluations URL.
29
+ - Tests (`test_config.py`, `test_report_helpers.py`) gate the build; missing `MONGODB_URI` = intentional crashloop (fail-fast).
30
+ - Health check path `/`, success codes 200-499.
31
+
32
+ ## New Lightsail service checklist
33
+
34
+ 1. Create the container service (console or API), region us-east-1.
35
+ 2. Activate ECR pull access:
36
+ ```
37
+ UpdateContainerService {"serviceName":"<svc>","privateRegistryAccess":{"ecrImagePullerRole":{"isActive":true}}}
38
+ ```
39
+ Service goes `UPDATING`/`PROVISIONING_SERVICE` for a few minutes; `principalArn` is empty until it finishes. Poll `GetContainerServices`.
40
+ 3. MERGE the new `principalArn` into the ECR repo policy as a new statement (`Sid: AllowLightsailPull-<svc>`, actions `ecr:BatchGetImage`, `ecr:GetDownloadUrlForLayer`). Fetch the existing policy first and append — never clobber (other services' statements live there).
41
+ 4. Ensure the secrets exist where that service expects them (GitHub environment for notifications, repo level for reports360).
42
+ 5. Push to the trigger branch or `gh run rerun <run-id>`.
43
+
44
+ ## AWS API without local CLI (curl sigv4)
45
+
46
+ Keys come from local `.env` (`AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY`).
47
+
48
+ ```bash
49
+ # Lightsail (JSON-RPC): GetContainerServices, GetContainerLog,
50
+ # GetContainerServiceDeployments, UpdateContainerService
51
+ curl -s "https://lightsail.us-east-1.amazonaws.com/" \
52
+ -H "X-Amz-Target: Lightsail_20161128.<Operation>" \
53
+ -H "Content-Type: application/x-amz-json-1.1" \
54
+ -d '{"serviceName":"<service-name>"}' \
55
+ --aws-sigv4 "aws:amz:us-east-1:lightsail" --user "$KEY:$SECRET"
56
+
57
+ # ECR: GetRepositoryPolicy / SetRepositoryPolicy
58
+ curl -s "https://api.ecr.us-east-1.amazonaws.com/" \
59
+ -H "X-Amz-Target: AmazonEC2ContainerRegistry_V20150921.<Operation>" \
60
+ -H "Content-Type: application/x-amz-json-1.1" \
61
+ -d '{"repositoryName":"<ecr-repo>"}' \
62
+ --aws-sigv4 "aws:amz:us-east-1:ecr" --user "$KEY:$SECRET"
63
+ ```
64
+
65
+ `GetContainerLog` needs `serviceName` AND `containerName` (for notifications both equal the service name; for reports360 the container is `reports360-container`).
66
+
67
+ ## Failure signatures
68
+
69
+ - **`Started 1 new node` ×3 → `Canceled`, zero app log lines**: the container never ran — image pull failure. Check `ecrImagePullerRole.isActive` and the ECR policy statement.
70
+ - **Container logs lag several minutes** behind reality; a "Get logs" step right after deploy always prints `logEvents: []` (misleading, not broken — removed from workflows).
71
+ - **`nano` power (dev) = 512MB**: if the app OOMs or restarts under load, bump to `small` like prod.
72
+
73
+ ## End-to-end verification
74
+
75
+ ### notifications: test email
76
+
77
+ Publish to the dev queue with the project's own `amqplib` (payload hits `treatTypes` default case → straight to `sendEmail`; `checking_in` only needs `coacheeName`):
78
+
79
+ ```js
80
+ const payload = {
81
+ emails: ["<recipient>"],
82
+ subject: "Dev environment test - correos_dev",
83
+ mailTemplate: "checking_in", // BARE template name — sendEmail appends _<language> itself (checking_in_es would resolve to checking_in_es/checking_in_es_es.hbs and ENOENT)
84
+ clientTheme: "bonum",
85
+ language: "es",
86
+ type: null,
87
+ context: { coacheeName: "Ricardo" },
88
+ };
89
+ // ch.assertQueue("correos_dev", { durable: false }); ch.sendToQueue(...)
90
+ ```
91
+
92
+ Then confirm `📨 MENSAJE RECIBIDO!` / `email to send` in container logs (mind the lag).
93
+
94
+ ### reports360: boot + report
95
+
96
+ Container must NOT be in crashloop (crashloop right after deploy = missing `MONGODB_URI`). Hit `/` (404 is healthy) and generate a report against a known dev evaluation to confirm Mongo + evaluations API connectivity.
97
+
98
+ ## Gotchas
99
+
100
+ - Repos live under a personal account (`bonumtech1`): collaborators cannot get admin; only the owner manages environments/secrets. Environments on private personal repos require GitHub Pro.
101
+ - Lightsail has no Secrets Manager integration; container env vars are visible in plaintext to anyone with read access to the service. Keep IAM scoped.
102
+ - Time floor: ~5 min of Lightsail blue/green (`ACTIVATING`) that no workflow change can reduce.
@@ -0,0 +1,102 @@
1
+ ---
2
+ name: loop-back-debug
3
+ description: Protocolo para cuando un fix no resuelve el síntoma la primera vez — fuerza re-leer el síntoma original, validar la hipótesis contra el diff aplicado, y generar hipótesis alternativas antes de aplicar otro patch. Trigger: un fix aplicado no elimina el bug en el repro, o se acumulan varios intentos fallidos sobre el mismo síntoma.
4
+ ---
5
+
6
+ # Loop-Back Debug
7
+
8
+ ## El anti-pattern que este skill ataca
9
+
10
+ Patrón recurrente cuando un bug es persistente:
11
+
12
+ 1. Intento de fix #1 → "debería andar" → no anda.
13
+ 2. Intento de fix #2 → "ahora sí" → no anda.
14
+ 3. Intento de fix #3 → cambio random → no anda.
15
+ 4. Eventualmente el código está peor que al inicio y el bug sigue.
16
+
17
+ El error de raíz es **escalar el cambio sin re-validar la hipótesis**. Cada intento asume que el anterior estaba "casi bien", cuando en realidad el modelo mental del bug estaba mal desde el #1.
18
+
19
+ ## La regla
20
+
21
+ ```
22
+ SI UN FIX NO ARREGLÓ EL SÍNTOMA EN EL PRIMER REPRO POST-FIX,
23
+ PARAS DE PATCHEAR Y RE-VALIDAS LA HIPÓTESIS.
24
+ ```
25
+
26
+ No más patches encima del patch anterior. Vuelves al síntoma original y al diff actual, comparas contra la hipótesis. Si la hipótesis era correcta y el síntoma persiste, el fix no es completo. Si la hipótesis era incorrecta, ningún patch sobre esta línea va a funcionar — cambia la hipótesis.
27
+
28
+ ## Función de gate (post-fix)
29
+
30
+ DESPUÉS de aplicar un fix y ANTES de afirmar "arreglado":
31
+
32
+ 1. **REPRO**: corre el repro exacto que producía el síntoma original. En este turno. No "asumiendo que".
33
+ 2. **OBSERVE**: ¿el síntoma sigue / cambió / desapareció?
34
+ 3. **CLASSIFY**:
35
+ - **Desapareció** → aplica `verify-before-done` para confirmar y declarar fix completo.
36
+ - **Persiste exactamente igual** → hipótesis estaba mal. NO patchees encima. Vuelve a § Reset hipótesis.
37
+ - **Cambió de forma** → el fix tocó algo real pero no era la causa raíz. Vuelve a § Reset hipótesis con la nueva info.
38
+
39
+ ## Reset hipótesis (cuando el fix no funcionó)
40
+
41
+ NO aplicar otro fix hasta completar estos pasos:
42
+
43
+ 1. **Lee de vuelta el síntoma original**, literal del ticket / bug report / repro inicial. No el síntoma de tu cabeza, el escrito.
44
+ 2. **Lista el diff aplicado** (`git diff HEAD~1`) y descríbelo en una frase: "Cambié X en archivo:línea de Y a Z porque hipótesis era W".
45
+ 3. **Valida lógicamente**: si la hipótesis W fuera cierta, ¿el cambio Y→Z debería haber arreglado el síntoma?
46
+ - Si la respuesta es "sí debería", pero el síntoma persiste → tu modelo del flujo está incompleto. Hay un step intermedio que no estás viendo.
47
+ - Si la respuesta es "no necesariamente", la hipótesis era débil desde el inicio.
48
+ 4. **Genera 2–3 hipótesis alternativas** antes de tocar código:
49
+ - ¿Hay caching? (browser, build, CDN, redis).
50
+ - ¿El código que cambiaste es el que se ejecuta? (path resolution, dynamic imports, env-gated branches).
51
+ - ¿El cambio fue al server o al cliente cuando el bug está del otro lado?
52
+ - ¿Hay un middleware / interceptor entre lo que cambiaste y donde se observa el síntoma?
53
+ - ¿El estado en memoria / DB ya estaba en un estado inválido y tu fix solo cubre el path "nuevo"?
54
+ - Si el repo convive con código legacy y código nuevo (rutas legacy declaradas en `project.legacyPaths` de su `argos.config.json`), ¿el fix tocó el path que realmente se ejecuta, o el otro?
55
+ 5. **Elige UNA hipótesis nueva** con razonamiento basado en evidencia. Documéntala antes de tocar código.
56
+ 6. **Aplica el siguiente intento** sabiendo qué estás probando.
57
+
58
+ ## Cuándo escalar / pedir ayuda al usuario
59
+
60
+ Si llevas **2 intentos fallidos** sobre el mismo bug, paras y reportas al usuario:
61
+
62
+ - Síntoma original.
63
+ - Hipótesis #1, fix aplicado, resultado del repro.
64
+ - Hipótesis #2, fix aplicado, resultado del repro.
65
+ - Hipótesis #3 que piensas probar, con evidencia que la sustenta.
66
+ - Pregunta concreta: "¿conoces más contexto que apoye / refute esta hipótesis?"
67
+
68
+ No es debilidad — es eficiencia. 3 intentos a ciegas valen menos que 1 conversación con quien tiene contexto.
69
+
70
+ ## Señales de alerta (PARA)
71
+
72
+ - Estás por hacer un segundo cambio sobre la misma línea sin haber re-corrido el repro.
73
+ - Estás por escribir "ahora sí debería andar" sin evidencia fresca.
74
+ - Estás revertiendo y re-aplicando variaciones del mismo cambio.
75
+ - Estás agregando logs / try-catch / fallbacks para "cubrir" en vez de entender.
76
+ - El diff acumula >3 commits sobre el mismo archivo intentando arreglar la misma cosa.
77
+
78
+ ## Anti-patrones
79
+
80
+ - ❌ "Voy a probar esto otro fix" sin haber corrido el repro del fix anterior.
81
+ - ❌ Patches defensivos: try-catch alrededor del código sospechoso para "que no rompa". Eso oculta el bug, no lo arregla.
82
+ - ❌ "Es flaky" como excusa sin evidencia de flakiness real.
83
+ - ❌ Cambiar lib / framework / approach porque "tal vez con X andaría" — eso es ducha caliente, no debugging.
84
+ - ❌ Pedirle al usuario "pruébalo de nuevo" sin haber cambiado nada relevante.
85
+
86
+ ## Conexión con el resto del harness
87
+
88
+ - `implementer`: invoca este skill cuando el primer fix no resuelve el síntoma. NO devuelve `done` hasta haber pasado por Reset hipótesis si el repro inicial falla.
89
+ - `verify-before-done`: este skill se aplica ANTES de verify-before-done — primero validas que el fix realmente arregló el síntoma (esto), luego validas que el resto del quality gate sigue verde (verify-before-done).
90
+ - `ticket-audit`: cuando un bug entra al agente ticket-audit, la "Hipótesis de causa raíz" es el primer candidate del loop. Si el fix de esa hipótesis no funciona, ticket-audit puede ser reinvocado con la info nueva.
91
+
92
+ ## Cierre
93
+
94
+ Al aplicar este skill, el output al usuario incluye:
95
+
96
+ 1. Hipótesis que se probó.
97
+ 2. Cambio aplicado (archivo:línea + descripción).
98
+ 3. Resultado del repro post-fix (en este turno).
99
+ 4. Si funcionó → aplicar `verify-before-done` para cierre.
100
+ 5. Si no funcionó → próxima hipótesis a probar O escalación al usuario (según conteo de intentos).
101
+
102
+ Este skill vive una sola vez en el motor global y aplica a cualquier repo. Los patrones de bug recurrentes de un proyecto concreto (caches que ese repo suele olvidar invalidar, race conditions conocidas en un módulo, comandos de repro estandarizados) no se agregan a este archivo: viven en la ficha del repo o en `argos.config.json` (por ejemplo `project.legacyPaths` para migraciones a medias), y se leen en runtime al depurar ese repo específico.
@@ -0,0 +1,57 @@
1
+ ---
2
+ name: mantine-form
3
+ description: Patrones de @mantine/form — useForm, getInputProps, validación con zodResolver, campos anidados y listas. Trigger: al crear o tocar formularios con Mantine.
4
+ ---
5
+
6
+ # Mantine Form — convenciones
7
+
8
+ ## Cuándo usar este skill
9
+
10
+ Al crear o tocar un formulario con `@mantine/form`: cablear inputs de Mantine, validar, manejar submit, o campos anidados/listas. `useForm` es la fuente de verdad del form — no espejees sus valores en `useState`, y valida con un schema Zod (vía `mantine-form-zod-resolver`), no con funciones sueltas por campo.
11
+
12
+ ## El patrón
13
+
14
+ `useForm` + `getInputProps` (esparce value/onChange/error de un jalón) + `zodResolver` para el schema:
15
+
16
+ ```tsx
17
+ const schema = z.object({
18
+ email: z.string().email(),
19
+ role: z.enum(['coach', 'coachee']),
20
+ });
21
+
22
+ const form = useForm({
23
+ mode: 'uncontrolled', // menos re-renders; el default recomendado
24
+ initialValues: { email: '', role: 'coachee' },
25
+ validate: zodResolver(schema), // desde 'mantine-form-zod-resolver'
26
+ });
27
+
28
+ <form onSubmit={form.onSubmit((values) => save(values))}>
29
+ <TextInput {...form.getInputProps('email')} />
30
+ <Select data={['coach', 'coachee']} {...form.getInputProps('role')} />
31
+ <Button type="submit">Guardar</Button>
32
+ </form>
33
+ ```
34
+
35
+ ## Gotchas que muerden
36
+
37
+ - **`getInputProps('campo')` cablea todo; no lo desarmes.** Ya trae `value`/`onChange`/`error`/`onBlur`. Pasar `value`/`onChange` a mano encima rompe el binding — deja que el spread mande.
38
+ - **`mode: 'uncontrolled'` cambia cómo lees valores.** En uncontrolled, `form.values` no re-renderiza al teclear; para reflejar un campo en la UI usa `form.watch('campo')` o `form.getValues()`. En `controlled` sí re-renderiza cada tecla (más caro en forms grandes).
39
+ - **Validación con `zodResolver`, no funciones por campo.** `validate: { email: (v) => … }` disemina reglas y tipos. Un schema Zod + `zodResolver` da una fuente única y el tipo por `z.infer`. Requiere el paquete `mantine-form-zod-resolver`.
40
+ - **Campos anidados/listas con notación de path.** `getInputProps('address.city')`, y listas con `form.insertListItem('items', {...})` / `form.removeListItem('items', i)` + `getInputProps('items.0.name')`. No manejes el array en `useState` aparte.
41
+ - **`initialValues` define el shape; llénalo completo.** Un campo ausente arranca `undefined` → warning uncontrolled→controlled. Para edición async usa `form.setValues(data)` / `form.initialize(data)` en un efecto, no valores a mano por render.
42
+ - **Submit con `form.onSubmit(handler)`.** Corre la validación y solo llama tu handler si pasa; además expone el segundo callback `(errors) => …` para enfocar el primer inválido. No valides "a mano" antes de enviar.
43
+
44
+ ## Reglas duras
45
+
46
+ 1. `useForm` es la única fuente del estado del form; nada de `useState` espejo.
47
+ 2. Inputs cableados con `getInputProps`; no dupliques `value`/`onChange`.
48
+ 3. Validación con schema Zod vía `zodResolver`; tipo por `z.infer`, sin reglas por campo.
49
+ 4. `initialValues` completo; edición async con `setValues`/`initialize`.
50
+ 5. Submit vía `form.onSubmit`; listas/anidados con la API de path, no arrays sueltos.
51
+
52
+ ## Antes de declarar listo
53
+
54
+ - Todos los inputs usan `getInputProps`; sin estado espejo ni handlers duplicados.
55
+ - Validación centralizada en un schema Zod con `zodResolver`; tipos por `z.infer`.
56
+ - `initialValues` seteado; sin warnings uncontrolled→controlled. Submit con `form.onSubmit`.
57
+ - El quality gate del repo (`qualityGate.fast` en `argos.config.json`) en verde.
@@ -0,0 +1,66 @@
1
+ ---
2
+ name: mongoose
3
+ description: Patrones de Mongoose en un servicio TS — queries seguras, populate, paginate, soft delete, ObjectId, evitar N+1. Trigger: al tocar domain/models u ops de Mongoose en controllers.
4
+ ---
5
+
6
+ # Mongoose — convenciones del servicio
7
+
8
+ ## Cuándo usar este skill
9
+
10
+ Cuando la tarea toca `domain/models` o ejecuta operaciones de Mongoose en los controllers. Sin repository wrappers: los controllers tocan los Models directo, así que null-guards, casts de ObjectId y `.lean()` viven en cada controller method.
11
+
12
+ En **NestJS** (`@nestjs/mongoose`) el Model se inyecta: `@InjectModel(Resource.name) private model: Model<ResourceDocument>`; el resto de patrones aplican igual.
13
+
14
+ ## Patrón canónico
15
+
16
+ ```ts
17
+ const doc = await Resource.findById(id);
18
+ if (!doc) throw new NotFoundError(`Resource ${id} not found`);
19
+
20
+ const docs = await Resource.find(filter).lean<IResource[]>(); // read-only
21
+
22
+ const updated = await Resource.findByIdAndUpdate(
23
+ id, { $set: dto }, { returnDocument: 'after', runValidators: true }
24
+ );
25
+ ```
26
+
27
+ `returnDocument: 'after'` (reemplaza el deprecado `new: true`) devuelve el doc actualizado; `runValidators: true` valida el update parcial. Ojo: `findByIdAndUpdate` **no** dispara hooks `pre('save')` ni valida el doc completo — si hay lógica en middleware `save`, usa `doc.save()`.
28
+
29
+ ## ObjectId — la trampa más común
30
+
31
+ Un `id` de `req.params`/`req.body` es **string**. `findById` lo castea solo, pero aggregations y queries complejas requieren `new Types.ObjectId(id)` (el `new` es obligatorio en Mongoose 6+). Valida el formato antes (`/^[a-f\d]{24}$/i`) o un string mal-formado lanza `CastError`. Compara ObjectId con `.equals()`, nunca `==`.
32
+
33
+ ## Gotchas que muerden
34
+
35
+ - **Query injection**: `Model.find(req.query)` crudo deja pasar operadores (`{ $ne: null }`). Arma el filtro campo por campo o `.setOptions({ sanitizeFilter: true })`. Y `strictQuery` es `false` por default (Mongoose 7+): un campo con typo se ignora → filtro vacío que devuelve **toda** la colección.
36
+ - **Atomicidad multi-doc**: escrituras relacionadas en `connection.transaction(async (session) => {...})`, pasando `{ session }` a cada op. `bulkWrite` no es transacción.
37
+ - **Índices**: filtros y `.sort()` sobre campos sin índice = COLLSCAN. Declara `schema.index(...)`, verifica con `.explain()`.
38
+ - **populate** batchea con `$in` (1 query por path, no N); no filtra/ordena por el child — ahí `$lookup`. `.lean()` pierde `.save()`/virtuals.
39
+ - **Soft delete**: con `mongoose-delete`, `find` ya excluye `deleted: true`; borra con `doc.delete()` (no `findByIdAndDelete`), restaura con `doc.restore()`.
40
+
41
+ ## Reglas duras
42
+
43
+ 1. **Ops de Mongoose nunca en la ruta** — siempre dentro de un controller method.
44
+ 2. **Null-guard tras `findById`/`findOne`** — `if (!doc) throw new NotFoundError(...)`.
45
+ 3. **`.lean()` cuando no necesitas mutar**; comparar ObjectId con `.equals()`, nunca `==`.
46
+ 4. **Nunca `Model.find(req.query)` crudo** — filtro campo por campo o `sanitizeFilter`.
47
+ 5. **Respeta el soft delete del repo**; escrituras relacionadas en `connection.transaction`.
48
+
49
+ ## Tabla rápida
50
+
51
+ | Necesito | Cómo |
52
+ |---|---|
53
+ | Buscar por id | `findById(id)` + null-guard → `NotFoundError` |
54
+ | Cast string → ObjectId | `new Types.ObjectId(id)` |
55
+ | Query read-only | `.find(filter).lean()` |
56
+ | Paginar | `.paginate(...)` o `skip().limit()` + `countDocuments` |
57
+ | Borrar con soft delete | `doc.delete()` (no `findByIdAndDelete`) |
58
+ | Update devolviendo el nuevo | `findByIdAndUpdate(id, { $set }, { returnDocument: 'after', runValidators: true })` |
59
+ | Escrituras relacionadas | `connection.transaction(async (session) => …)` |
60
+
61
+ ## Antes de declarar listo
62
+
63
+ - Cada `findById`/`findOne` tiene null-guard → `NotFoundError`; read-only con `.lean()`.
64
+ - Ningún filtro arma con `req.query`/`req.body` crudo; ObjectId comparado con `.equals()`.
65
+ - Borrados respetan soft delete; escrituras relacionadas van en transacción.
66
+ - El quality gate del repo (`qualityGate.fast` en `argos.config.json`) en verde.
@@ -0,0 +1,144 @@
1
+ ---
2
+ name: nextjs-15
3
+ description: Next.js 15 App Router patterns. Trigger: When working with Next.js - routing, Server Actions, data fetching.
4
+ ---
5
+
6
+ ## App Router File Conventions
7
+
8
+ ```
9
+ app/
10
+ ├── layout.tsx # Root layout (required)
11
+ ├── page.tsx # Home page (/)
12
+ ├── loading.tsx # Loading UI (Suspense)
13
+ ├── error.tsx # Error boundary
14
+ ├── not-found.tsx # 404 page
15
+ ├── (auth)/ # Route group (no URL impact)
16
+ │ ├── login/page.tsx # /login
17
+ │ └── signup/page.tsx # /signup
18
+ ├── api/
19
+ │ └── route.ts # API handler
20
+ └── _components/ # Private folder (not routed)
21
+ ```
22
+
23
+ ## Server Components (Default)
24
+
25
+ ```typescript
26
+ // No directive needed - async by default
27
+ export default async function Page() {
28
+ const data = await db.query();
29
+ return <Component data={data} />;
30
+ }
31
+ ```
32
+
33
+ ## Server Actions
34
+
35
+ ```typescript
36
+ // app/actions.ts
37
+ "use server";
38
+
39
+ import { revalidatePath } from "next/cache";
40
+ import { redirect } from "next/navigation";
41
+
42
+ export async function createUser(formData: FormData) {
43
+ const name = formData.get("name") as string;
44
+
45
+ await db.users.create({ data: { name } });
46
+
47
+ revalidatePath("/users");
48
+ redirect("/users");
49
+ }
50
+
51
+ // Usage
52
+ <form action={createUser}>
53
+ <input name="name" required />
54
+ <button type="submit">Create</button>
55
+ </form>
56
+ ```
57
+
58
+ ## Data Fetching
59
+
60
+ ```typescript
61
+ // Parallel
62
+ async function Page() {
63
+ const [users, posts] = await Promise.all([
64
+ getUsers(),
65
+ getPosts(),
66
+ ]);
67
+ return <Dashboard users={users} posts={posts} />;
68
+ }
69
+
70
+ // Streaming with Suspense
71
+ <Suspense fallback={<Loading />}>
72
+ <SlowComponent />
73
+ </Suspense>
74
+ ```
75
+
76
+ ## Route Handlers (API)
77
+
78
+ ```typescript
79
+ // app/api/users/route.ts
80
+ import { NextRequest, NextResponse } from "next/server";
81
+
82
+ export async function GET(request: NextRequest) {
83
+ const users = await db.users.findMany();
84
+ return NextResponse.json(users);
85
+ }
86
+
87
+ export async function POST(request: NextRequest) {
88
+ const body = await request.json();
89
+ const user = await db.users.create({ data: body });
90
+ return NextResponse.json(user, { status: 201 });
91
+ }
92
+ ```
93
+
94
+ ## Middleware
95
+
96
+ ```typescript
97
+ // middleware.ts (root level)
98
+ import { NextResponse } from "next/server";
99
+ import type { NextRequest } from "next/server";
100
+
101
+ export function middleware(request: NextRequest) {
102
+ const token = request.cookies.get("token");
103
+
104
+ if (!token && request.nextUrl.pathname.startsWith("/dashboard")) {
105
+ return NextResponse.redirect(new URL("/login", request.url));
106
+ }
107
+
108
+ return NextResponse.next();
109
+ }
110
+
111
+ export const config = {
112
+ matcher: ["/dashboard/:path*"],
113
+ };
114
+ ```
115
+
116
+ ## Metadata
117
+
118
+ ```typescript
119
+ // Static
120
+ export const metadata = {
121
+ title: "My App",
122
+ description: "Description",
123
+ };
124
+
125
+ // Dynamic
126
+ export async function generateMetadata({ params }) {
127
+ const product = await getProduct(params.id);
128
+ return { title: product.name };
129
+ }
130
+ ```
131
+
132
+ ## server-only Package
133
+
134
+ ```typescript
135
+ import "server-only";
136
+
137
+ // This will error if imported in client component
138
+ export async function getSecretData() {
139
+ return db.secrets.findMany();
140
+ }
141
+ ```
142
+
143
+ ## Keywords
144
+ nextjs, next.js, app router, server components, server actions, streaming