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.
- package/LICENSE +21 -0
- package/README.md +21 -0
- package/assets/agents/auditor.md +140 -0
- package/assets/agents/commit-pr-pilot.md +154 -0
- package/assets/agents/explorer.md +93 -0
- package/assets/agents/implementer.md +117 -0
- package/assets/agents/leader.md +149 -0
- package/assets/agents/researcher.md +89 -0
- package/assets/agents/review-readability.md +87 -0
- package/assets/agents/review-reliability.md +100 -0
- package/assets/agents/review-resilience.md +87 -0
- package/assets/agents/review-risk.md +87 -0
- package/assets/agents/reviewer.md +167 -0
- package/assets/agents/ticket-audit.md +129 -0
- package/assets/hooks/argos-guard-destructive.sh +127 -0
- package/assets/hooks/argos-quality-gate.sh +129 -0
- package/assets/managed/aterrizaje.md +24 -0
- package/assets/managed/formato-respuesta.md +21 -0
- package/assets/managed/identidad.md +48 -0
- package/assets/managed/operaciones-seguras.md +14 -0
- package/assets/managed/orquestacion.md +169 -0
- package/assets/output-styles/argos.md +71 -0
- package/assets/skills/ai-sdk-5/SKILL.md +230 -0
- package/assets/skills/angular/SKILL.md +19 -0
- package/assets/skills/angular/references/architecture.md +137 -0
- package/assets/skills/angular/references/core.md +197 -0
- package/assets/skills/angular/references/forms.md +115 -0
- package/assets/skills/angular/references/performance.md +124 -0
- package/assets/skills/apollo-client/SKILL.md +61 -0
- package/assets/skills/app-blueprint/SKILL.md +45 -0
- package/assets/skills/app-blueprint/assets/module-template.md +48 -0
- package/assets/skills/app-blueprint/assets/system-template.md +38 -0
- package/assets/skills/app-blueprint/references/workflow.md +101 -0
- package/assets/skills/app-builder/SKILL.md +43 -0
- package/assets/skills/app-builder/phases/0-product.md +58 -0
- package/assets/skills/app-builder/phases/1-scaffold.md +33 -0
- package/assets/skills/app-builder/phases/10-store.md +40 -0
- package/assets/skills/app-builder/phases/2-data.md +34 -0
- package/assets/skills/app-builder/phases/3-domain.md +33 -0
- package/assets/skills/app-builder/phases/4-ui-nav.md +32 -0
- package/assets/skills/app-builder/phases/5-identity.md +30 -0
- package/assets/skills/app-builder/phases/6-polish.md +34 -0
- package/assets/skills/app-builder/phases/7-brand.md +31 -0
- package/assets/skills/app-builder/phases/8-web.md +30 -0
- package/assets/skills/app-builder/phases/9-docs.md +31 -0
- package/assets/skills/app-ia/SKILL.md +54 -0
- package/assets/skills/astro/SKILL.md +39 -0
- package/assets/skills/axios/SKILL.md +61 -0
- package/assets/skills/branch-pr/SKILL.md +200 -0
- package/assets/skills/bullmq/SKILL.md +55 -0
- package/assets/skills/chained-pr/SKILL.md +48 -0
- package/assets/skills/chained-pr/references/chaining-details.md +99 -0
- package/assets/skills/cognitive-doc-design/SKILL.md +81 -0
- package/assets/skills/comment-writer/SKILL.md +74 -0
- package/assets/skills/dashboard-ia/SKILL.md +54 -0
- package/assets/skills/django-drf/SKILL.md +180 -0
- package/assets/skills/go-testing/SKILL.md +47 -0
- package/assets/skills/go-testing/references/examples.md +89 -0
- package/assets/skills/issue-creation/SKILL.md +223 -0
- package/assets/skills/jira-epic/SKILL.md +306 -0
- package/assets/skills/jira-task/SKILL.md +382 -0
- package/assets/skills/judgment-day/SKILL.md +52 -0
- package/assets/skills/judgment-day/references/prompts-and-formats.md +98 -0
- package/assets/skills/lightsail-deploy/SKILL.md +44 -0
- package/assets/skills/lightsail-deploy/references/runbook.md +102 -0
- package/assets/skills/loop-back-debug/SKILL.md +102 -0
- package/assets/skills/mantine-form/SKILL.md +57 -0
- package/assets/skills/mongoose/SKILL.md +66 -0
- package/assets/skills/nextjs-15/SKILL.md +144 -0
- package/assets/skills/not-boring-mobile/SKILL.md +43 -0
- package/assets/skills/not-boring-mobile/references/not-boring-playbook.md +65 -0
- package/assets/skills/playwright/SKILL.md +315 -0
- package/assets/skills/pr-comments/SKILL.md +93 -0
- package/assets/skills/pr-create/SKILL.md +64 -0
- package/assets/skills/promo-video/SKILL.md +52 -0
- package/assets/skills/promo-video/assets/package.template.json +22 -0
- package/assets/skills/promo-video/assets/promo.template.tsx +469 -0
- package/assets/skills/promo-video/assets/theme.template.ts +27 -0
- package/assets/skills/promo-video/references/pipeline.md +119 -0
- package/assets/skills/promo-video-web/SKILL.md +51 -0
- package/assets/skills/promo-video-web/assets/browser-promo.template.tsx +385 -0
- package/assets/skills/promo-video-web/assets/capture.template.ts +70 -0
- package/assets/skills/promo-video-web/assets/package.template.json +26 -0
- package/assets/skills/promo-video-web/references/pipeline.md +84 -0
- package/assets/skills/pytest/SKILL.md +180 -0
- package/assets/skills/react-19/SKILL.md +118 -0
- package/assets/skills/react-hook-form/SKILL.md +59 -0
- package/assets/skills/react-router/SKILL.md +60 -0
- package/assets/skills/redux-toolkit/SKILL.md +60 -0
- package/assets/skills/review-diff/SKILL.md +101 -0
- package/assets/skills/ship-docs/SKILL.md +45 -0
- package/assets/skills/ship-docs/references/ship-docs-playbook.md +31 -0
- package/assets/skills/skill-creator/SKILL.md +97 -0
- package/assets/skills/skill-creator/assets/SKILL-TEMPLATE.md +68 -0
- package/assets/skills/skill-creator/references/skill-style-guide.md +79 -0
- package/assets/skills/skill-improver/SKILL.md +50 -0
- package/assets/skills/skill-improver/references/skill-style-guide.md +79 -0
- package/assets/skills/socketio/SKILL.md +58 -0
- package/assets/skills/spec-bootstrap/SKILL.md +62 -0
- package/assets/skills/store-ship/SKILL.md +52 -0
- package/assets/skills/store-ship/assets/android-supply.template.md +22 -0
- package/assets/skills/store-ship/assets/eas.template.json +32 -0
- package/assets/skills/store-ship/assets/maestro-flow.template.yaml +27 -0
- package/assets/skills/store-ship/assets/store.config.template.json +28 -0
- package/assets/skills/store-ship/references/pipeline.md +198 -0
- package/assets/skills/stripe/SKILL.md +83 -0
- package/assets/skills/tailwind-4/SKILL.md +193 -0
- package/assets/skills/tamagui/SKILL.md +60 -0
- package/assets/skills/tanstack-query/SKILL.md +58 -0
- package/assets/skills/ticket-intake/SKILL.md +55 -0
- package/assets/skills/typescript/SKILL.md +134 -0
- package/assets/skills/verify-before-done/SKILL.md +111 -0
- package/assets/skills/webapp-rebuilder/SKILL.md +48 -0
- package/assets/skills/webapp-rebuilder/assets/charter-template.md +46 -0
- package/assets/skills/webapp-rebuilder/references/workflow.md +47 -0
- package/assets/skills/winston-logging/SKILL.md +61 -0
- package/assets/skills/work-unit-commits/SKILL.md +84 -0
- package/assets/skills/zod-4/SKILL.md +210 -0
- package/assets/skills/zustand-5/SKILL.md +216 -0
- package/bin/argos.js +6 -0
- package/dist/commands/adopt.d.ts +35 -0
- package/dist/commands/adopt.d.ts.map +1 -0
- package/dist/commands/adopt.js +347 -0
- package/dist/commands/adopt.js.map +1 -0
- package/dist/commands/doctor.d.ts +20 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +478 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/init.d.ts +32 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +358 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/remove.d.ts +62 -0
- package/dist/commands/remove.d.ts.map +1 -0
- package/dist/commands/remove.js +487 -0
- package/dist/commands/remove.js.map +1 -0
- package/dist/commands/workspace.d.ts +73 -0
- package/dist/commands/workspace.d.ts.map +1 -0
- package/dist/commands/workspace.js +354 -0
- package/dist/commands/workspace.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/assets.d.ts +29 -0
- package/dist/lib/assets.d.ts.map +1 -0
- package/dist/lib/assets.js +57 -0
- package/dist/lib/assets.js.map +1 -0
- package/dist/lib/atomic-write.d.ts +17 -0
- package/dist/lib/atomic-write.d.ts.map +1 -0
- package/dist/lib/atomic-write.js +41 -0
- package/dist/lib/atomic-write.js.map +1 -0
- package/dist/lib/backup.d.ts +11 -0
- package/dist/lib/backup.d.ts.map +1 -0
- package/dist/lib/backup.js +42 -0
- package/dist/lib/backup.js.map +1 -0
- package/dist/lib/config.d.ts +36 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +52 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/detect.d.ts +58 -0
- package/dist/lib/detect.d.ts.map +1 -0
- package/dist/lib/detect.js +330 -0
- package/dist/lib/detect.js.map +1 -0
- package/dist/lib/ficha.d.ts +10 -0
- package/dist/lib/ficha.d.ts.map +1 -0
- package/dist/lib/ficha.js +38 -0
- package/dist/lib/ficha.js.map +1 -0
- package/dist/lib/git.d.ts +27 -0
- package/dist/lib/git.d.ts.map +1 -0
- package/dist/lib/git.js +79 -0
- package/dist/lib/git.js.map +1 -0
- package/dist/lib/managed-files.d.ts +35 -0
- package/dist/lib/managed-files.d.ts.map +1 -0
- package/dist/lib/managed-files.js +97 -0
- package/dist/lib/managed-files.js.map +1 -0
- package/dist/lib/markers.d.ts +64 -0
- package/dist/lib/markers.d.ts.map +1 -0
- package/dist/lib/markers.js +157 -0
- package/dist/lib/markers.js.map +1 -0
- package/dist/lib/navori-import.d.ts +42 -0
- package/dist/lib/navori-import.d.ts.map +1 -0
- package/dist/lib/navori-import.js +65 -0
- package/dist/lib/navori-import.js.map +1 -0
- package/dist/lib/openclaw-agents.d.ts +53 -0
- package/dist/lib/openclaw-agents.d.ts.map +1 -0
- package/dist/lib/openclaw-agents.js +118 -0
- package/dist/lib/openclaw-agents.js.map +1 -0
- package/dist/lib/package-root.d.ts +11 -0
- package/dist/lib/package-root.d.ts.map +1 -0
- package/dist/lib/package-root.js +23 -0
- package/dist/lib/package-root.js.map +1 -0
- package/dist/lib/paths.d.ts +11 -0
- package/dist/lib/paths.d.ts.map +1 -0
- package/dist/lib/paths.js +16 -0
- package/dist/lib/paths.js.map +1 -0
- package/dist/lib/settings-merge.d.ts +125 -0
- package/dist/lib/settings-merge.d.ts.map +1 -0
- package/dist/lib/settings-merge.js +373 -0
- package/dist/lib/settings-merge.js.map +1 -0
- package/dist/lib/version.d.ts +3 -0
- package/dist/lib/version.d.ts.map +1 -0
- package/dist/lib/version.js +13 -0
- package/dist/lib/version.js.map +1 -0
- package/dist/lib/which.d.ts +8 -0
- package/dist/lib/which.d.ts.map +1 -0
- package/dist/lib/which.js +30 -0
- package/dist/lib/which.js.map +1 -0
- package/dist/lib/workspaces.d.ts +133 -0
- package/dist/lib/workspaces.d.ts.map +1 -0
- package/dist/lib/workspaces.js +241 -0
- package/dist/lib/workspaces.js.map +1 -0
- package/dist/lib/zod-messages.d.ts +4 -0
- package/dist/lib/zod-messages.d.ts.map +1 -0
- package/dist/lib/zod-messages.js +28 -0
- package/dist/lib/zod-messages.js.map +1 -0
- 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
|