[autoresearch] baseline: system prompt quality tests (5/5 passing)
This commit is contained in:
1 parent
05cc036f6d
commit
60b0101717
142 files changed
+18589
-9947
No files matched your search
@@ -0,0 +1,91 @@
|
||||
---
|
||||
name: adr
|
||||
description: Architecture Decision Record skill for documenting significant architectural decisions with context, options, and consequences. Use during the Research phase when choosing between approaches, or whenever the user asks to document an architectural decision.
|
||||
---
|
||||
|
||||
# Architecture Decision Record (ADR) Skill
|
||||
|
||||
## Purpose
|
||||
Document significant architectural decisions so institutional knowledge persists across conversations and team members. ADRs capture the **why**, not just the **what**.
|
||||
|
||||
## When to Invoke
|
||||
- During Research phase (`/1-research`) when a significant architecture decision is identified
|
||||
- When user explicitly asks to document a decision
|
||||
- When choosing between 2+ viable approaches
|
||||
- When introducing a new dependency or pattern
|
||||
- When changing existing architecture
|
||||
|
||||
## ADR Storage
|
||||
ADRs are stored in `docs/decisions/` as numbered files:
|
||||
```
|
||||
docs/decisions/
|
||||
├── 0001-use-postgresql-for-storage.md
|
||||
├── 0002-adopt-feature-based-structure.md
|
||||
├── 0003-use-testcontainers-for-integration.md
|
||||
└── NNNN-short-title.md
|
||||
```
|
||||
|
||||
## ADR Template
|
||||
|
||||
Create the ADR file at `docs/decisions/NNNN-short-title.md`:
|
||||
|
||||
```markdown
|
||||
# NNNN. Short Title
|
||||
|
||||
**Date:** YYYY-MM-DD
|
||||
**Status:** Proposed | Accepted | Deprecated | Superseded by NNNN
|
||||
|
||||
## Context
|
||||
What is the issue that we're seeing that is motivating this decision?
|
||||
Include technical constraints, business requirements, and relevant context.
|
||||
|
||||
## Options Considered
|
||||
|
||||
### Option A: {name}
|
||||
- **Pros:** ...
|
||||
- **Cons:** ...
|
||||
- **Effort:** Low/Medium/High
|
||||
|
||||
### Option B: {name}
|
||||
- **Pros:** ...
|
||||
- **Cons:** ...
|
||||
- **Effort:** Low/Medium/High
|
||||
|
||||
### Option C: {name} (if applicable)
|
||||
- **Pros:** ...
|
||||
- **Cons:** ...
|
||||
- **Effort:** Low/Medium/High
|
||||
|
||||
## Decision
|
||||
We chose **Option X** because...
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
- What becomes easier or possible as a result
|
||||
|
||||
### Negative
|
||||
- What becomes harder as a result
|
||||
- Technical debt introduced (if any)
|
||||
|
||||
### Risks
|
||||
- What could go wrong with this decision
|
||||
- Mitigation strategies
|
||||
|
||||
## Related
|
||||
- Links to relevant rules, previous ADRs, or external resources
|
||||
```
|
||||
|
||||
## Process Guidelines
|
||||
|
||||
1. **Number sequentially** — check existing ADRs in `docs/decisions/` for the next number
|
||||
2. **Keep titles short** — descriptive enough to identify the decision at a glance
|
||||
3. **Status lifecycle:** `Proposed` → `Accepted` (after approval) → optionally `Deprecated` or `Superseded`
|
||||
4. **Never delete ADRs** — if a decision is reversed, mark as `Superseded by NNNN` and create a new ADR
|
||||
5. **Use Sequential Thinking skill** if the trade-off analysis is complex
|
||||
|
||||
## Rule Compliance
|
||||
ADRs should reference applicable rules:
|
||||
- Architectural Patterns @architectural-pattern.md
|
||||
- Core Design Principles @core-design-principles.md
|
||||
- Project Structure @project-structure.md
|
||||
@@ -0,0 +1,140 @@
|
||||
---
|
||||
name: code-review
|
||||
description: Structured code review protocol for inspecting code quality against the full rule set. Use when auditing code written by yourself or another agent, during the /audit workflow, or when the user asks for a code review.
|
||||
---
|
||||
|
||||
# Code Review Skill
|
||||
|
||||
## Purpose
|
||||
Systematically review code against the full antigravity rule set. Catches issues that linters miss: architectural violations, missing observability, business logic errors, pattern inconsistencies.
|
||||
|
||||
## When to Invoke
|
||||
- During the `/audit` workflow (Phase 1: Code Review)
|
||||
- When user asks for a code review outside any workflow
|
||||
- **Best practice:** Invoke in a fresh conversation (not the same one that authored the code) to avoid confirmation bias
|
||||
|
||||
## Review Process
|
||||
|
||||
### 1. Scope the Review
|
||||
Identify the files/features to review. Determine the review scope:
|
||||
- **Feature review** — all files in a feature directory
|
||||
- **PR review** — only changed files
|
||||
- **Full codebase audit** — all features
|
||||
|
||||
### 2. Load the Rule Set
|
||||
Read all applicable rules from `.agents/rules/`. Use `rule-priority.md` for severity classification.
|
||||
|
||||
### 3. Review Categories (Priority Order)
|
||||
|
||||
Review each file/feature against these categories, in order from `rule-priority.md`:
|
||||
|
||||
#### Critical (Must Fix)
|
||||
- **Security** — injection, hardcoded secrets, broken auth
|
||||
- **Data loss** — missing error handling on writes, no transaction boundaries
|
||||
- **Resource leaks** — unclosed connections, missing cleanup
|
||||
|
||||
#### Major (Should Fix)
|
||||
- **Testability** — I/O not behind interfaces, untested error paths
|
||||
- **Observability** — missing logging on operations, no correlation IDs
|
||||
- **Error handling** — empty catch blocks, swallowed errors
|
||||
- **Architecture** — circular dependencies, wrong layer access
|
||||
|
||||
#### Minor (Nice to Fix)
|
||||
- **Pattern consistency** — deviation from established codebase patterns
|
||||
- **Naming** — unclear variable/function names
|
||||
- **Code organization** — functions too long, mixed responsibilities
|
||||
|
||||
#### Nit (Optional)
|
||||
- **Style** — formatting issues the linter would catch
|
||||
- **Documentation** — missing comments on complex logic
|
||||
|
||||
### 4. Produce Findings
|
||||
|
||||
Output a structured findings document:
|
||||
|
||||
```markdown
|
||||
# Code Review: {Feature/Module Name}
|
||||
Date: {date}
|
||||
Reviewer: AI Agent (fresh context)
|
||||
|
||||
## Summary
|
||||
- **Files reviewed:** N
|
||||
- **Issues found:** N (X critical, Y major, Z minor, W nit)
|
||||
|
||||
## Critical Issues
|
||||
- [ ] **[SEC]** {description} — [{file}:{line}](file:///path)
|
||||
- [ ] **[DATA]** {description} — [{file}:{line}](file:///path)
|
||||
|
||||
## Major Issues
|
||||
- [ ] **[TEST]** {description} — [{file}:{line}](file:///path)
|
||||
- [ ] **[OBS]** {description} — [{file}:{line}](file:///path)
|
||||
|
||||
## Minor Issues
|
||||
- [ ] **[PAT]** {description} — [{file}:{line}](file:///path)
|
||||
|
||||
## Nit
|
||||
- [ ] {description} — [{file}:{line}](file:///path)
|
||||
|
||||
## Rules Applied
|
||||
List of rules referenced during this review.
|
||||
```
|
||||
|
||||
### 5. Save the Report
|
||||
|
||||
When invoked via the `/audit` workflow, you **MUST** persist the findings to the repo:
|
||||
|
||||
**Path:** `docs/audits/review-findings-{feature}-{YYYY-MM-DD}-{HHmm}.md`
|
||||
|
||||
1. Create `docs/audits/` if it doesn't exist
|
||||
2. Write the findings document to that path
|
||||
3. This makes the report accessible from other conversations and agents
|
||||
|
||||
When invoked as a standalone review (not via `/audit`), saving to `docs/audits/` is recommended but optional.
|
||||
|
||||
### 6. Severity Tags
|
||||
|
||||
| Tag | Category | Rule Source |
|
||||
| -------- | -------------------- | -------------------------------------------------- |
|
||||
| `[SEC]` | Security | `security-principles.md` |
|
||||
| `[DATA]` | Data integrity | `error-handling-principles.md` |
|
||||
| `[RES]` | Resource leak | `resources-and-memory-management-principles.md` |
|
||||
| `[TEST]` | Testability | `architectural-pattern.md`, `testing-strategy.md` |
|
||||
| `[OBS]` | Observability | `logging-and-observability-mandate.md` |
|
||||
| `[ERR]` | Error handling | `error-handling-principles.md` |
|
||||
| `[ARCH]` | Architecture | `architectural-pattern.md`, `project-structure.md` |
|
||||
| `[PAT]` | Pattern consistency | `code-organization-principles.md` |
|
||||
| `[INT]` | Integration contract | `api-design-principles.md` |
|
||||
| `[DB]` | Database design | `database-design-principles.md` |
|
||||
| `[CFG]` | Configuration | `configuration-management-principles.md` |
|
||||
|
||||
### 7. Language-Specific Anti-Patterns
|
||||
|
||||
Load the anti-pattern checklist for the language(s) under review:
|
||||
|
||||
| Language | Anti-Patterns |
|
||||
|---|---|
|
||||
| **Go** | `languages/go.md` |
|
||||
| **TypeScript** | `languages/typescript.md` *(placeholder — create when needed)* |
|
||||
| **Flutter/Dart** | `languages/flutter.md` *(placeholder — create when needed)* |
|
||||
| **Rust** | `languages/rust.md` *(placeholder — create when needed)* |
|
||||
|
||||
> Anti-patterns listed in language files are **auto-fail** — they require no judgment call. If the pattern exists in the code, it is a finding.
|
||||
|
||||
### 8. Cross-Boundary Checks
|
||||
|
||||
For full audits, cross-boundary concerns (integration contracts, database schema, configuration hygiene, dependency health, test coverage gaps) are checked via the dedicated dimension checklist in the `/audit` workflow — **Phase 1.5: Cross-Boundary Review**.
|
||||
|
||||
When invoking this skill standalone (outside `/audit`), apply the applicable dimensions from that checklist manually and tag findings with `[INT]`, `[DB]`, or `[CFG]` as appropriate.
|
||||
|
||||
**Zero-Findings Guard:** If this review produces fewer than 3 findings, you MUST produce a "Dimensions Covered" attestation section in the findings document, listing each cross-boundary dimension and the specific files or queries you examined. Only then may you declare a clean result.
|
||||
|
||||
---
|
||||
|
||||
## Rule Compliance
|
||||
This skill enforces all rules in `.agents/rules/`. Key references:
|
||||
- Rule Priority @rule-priority.md (severity classification)
|
||||
- Security Principles @security-principles.md
|
||||
- Architectural Patterns @architectural-pattern.md
|
||||
- Testing Strategy @testing-strategy.md
|
||||
- Logging and Observability Mandate @logging-and-observability-mandate.md
|
||||
- Error Handling Principles @error-handling-principles.md
|
||||
@@ -0,0 +1,89 @@
|
||||
# Go Anti-Patterns (Auto-Fail)
|
||||
|
||||
> **Load this file** during code reviews of Go code. Patterns listed here are **immediate findings** — no judgment needed. If the pattern exists, it is a finding.
|
||||
|
||||
---
|
||||
|
||||
## Auto-Fail Table
|
||||
|
||||
| Pattern | Severity | Tag | Correct Fix |
|
||||
|---|---|---|---|
|
||||
| `defer X.Close()` (bare) | Critical | `[RES]` | Error-checked closure with `slog.Warn` |
|
||||
| `//nolint:errcheck` | Critical | `[ERR]` | Handle the error — use error-checked closure |
|
||||
| `defer tx.Rollback()` (bare) | Critical | `[RES]` | Error-checked closure with `sql.ErrTxDone` guard |
|
||||
| `fmt.Printf` / `fmt.Println` | Major | `[OBS]` | Replace with `slog` structured logging |
|
||||
| `log.Printf` / `log.Println` | Major | `[OBS]` | Replace with `slog` structured logging |
|
||||
| `resp.Body.Close()` without drain | Major | `[RES]` | `io.Copy(io.Discard, resp.Body)` then close |
|
||||
| Empty `if err != nil {}` block | Critical | `[ERR]` | Handle, return, or log the error |
|
||||
| `_ = someFunc()` (discarded error) | Major | `[ERR]` | Handle the error explicitly |
|
||||
|
||||
---
|
||||
|
||||
## Detection Commands
|
||||
|
||||
Use these grep patterns to scan for anti-patterns before manual review:
|
||||
|
||||
```bash
|
||||
# Bare defer Close/Rollback (no error check)
|
||||
grep -rn 'defer.*\.Close()' --include='*.go'
|
||||
grep -rn 'defer.*\.Rollback()' --include='*.go'
|
||||
|
||||
# nolint:errcheck suppression
|
||||
grep -rn 'nolint:errcheck' --include='*.go'
|
||||
|
||||
# Unstructured logging in production code
|
||||
grep -rn 'fmt\.Printf\|fmt\.Println\|log\.Printf\|log\.Println' --include='*.go' | grep -v '_test.go'
|
||||
|
||||
# Discarded errors
|
||||
grep -rn '_ = ' --include='*.go' | grep -v '_test.go'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Correct Patterns (Reference)
|
||||
|
||||
### Resource Cleanup
|
||||
|
||||
```go
|
||||
// ✅ Rows / Statements / Connections
|
||||
defer func() {
|
||||
if err := rows.Close(); err != nil {
|
||||
slog.Warn("failed to close rows", "error", err, "operation", "<operation>")
|
||||
}
|
||||
}()
|
||||
|
||||
// ✅ Transaction Rollback
|
||||
defer func() {
|
||||
if err := tx.Rollback(); err != nil && !errors.Is(err, sql.ErrTxDone) {
|
||||
slog.Error("failed to rollback transaction", "error", err, "operation", "<operation>")
|
||||
}
|
||||
}()
|
||||
|
||||
// ✅ HTTP Response Body
|
||||
defer func() {
|
||||
if _, err := io.Copy(io.Discard, resp.Body); err != nil {
|
||||
slog.Warn("failed to drain response body", "error", err)
|
||||
}
|
||||
if err := resp.Body.Close(); err != nil {
|
||||
slog.Warn("failed to close response body", "error", err)
|
||||
}
|
||||
}()
|
||||
```
|
||||
|
||||
### Structured Logging
|
||||
|
||||
```go
|
||||
// ❌ fmt.Printf("User %s logged in from %s\n", userID, ip)
|
||||
// ❌ log.Printf("User %s logged in from %s", userID, ip)
|
||||
|
||||
// ✅
|
||||
slog.Info("user login", "userId", userID, "ip", ip)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
- Go Idioms and Patterns @go-idioms-and-patterns.md
|
||||
- Logging and Observability Principles @logging-and-observability-principles.md
|
||||
- Error Handling Principles @error-handling-principles.md
|
||||
- Resources and Memory Management Principles @resources-and-memory-management-principles.md
|
||||
@@ -0,0 +1,93 @@
|
||||
---
|
||||
name: debugging-protocol
|
||||
description: Comprehensive protocol for validating root causes of software issues. Use when you need to systematically debug a complex bug, flaky test, or unknown system behavior by forming hypotheses and validating them with specific tasks.
|
||||
---
|
||||
|
||||
# Debugging Protocol
|
||||
|
||||
## Overview
|
||||
|
||||
This skill provides a rigorous framework for debugging complex software issues. It moves beyond ad-hoc troubleshooting to a structured process of hypothesis generation and validation.
|
||||
|
||||
Use this skill to:
|
||||
1. Formalize a debugging session.
|
||||
2. Systematically eliminate potential root causes.
|
||||
3. Document findings for future reference or team communication.
|
||||
|
||||
## Protocol Workflow
|
||||
|
||||
To run a structured debugging session, follow these steps:
|
||||
|
||||
### 1. Initialize the Session
|
||||
Create a new debugging document using the provided template. This serves as the "source of truth" for the investigation.
|
||||
|
||||
**Template location:** `assets/debugging-session-template.md`
|
||||
|
||||
**Save to:** `docs/debugging/{issue-name}-{YYYY-MM-DD}-{HHmm}.md`
|
||||
|
||||
1. Create `docs/debugging/` if it doesn't exist
|
||||
2. Copy the template and fill in the issue details
|
||||
3. This makes the session accessible from other conversations and agents (e.g., when handing off to a `/quick-fix` or `/orchestrator` workflow)
|
||||
|
||||
### 2. Define the Problem
|
||||
Clearly articulate the **System Context** and **Problem Statement**.
|
||||
* **Symptom**: What is the observable behavior? How does it differ from expected behavior?
|
||||
* **Scope**: Which components are involved?
|
||||
|
||||
### 3. Formulate Hypotheses
|
||||
List distinct, testable hypotheses.
|
||||
* Avoid vague guesses.
|
||||
* Differentiate between layers (e.g., "Frontend Hypothesis" vs "Backend Hypothesis").
|
||||
* Example: "Race condition in UI state update" vs "Database schema misconfiguration".
|
||||
|
||||
### 4. Design Validation Tasks
|
||||
For each hypothesis, design a specific validation task.
|
||||
* **Objective**: What are you trying to prove or disprove?
|
||||
* **Steps**: Precise, reproducible actions.
|
||||
* **Code Pattern**: Provide the exact code or command to run (e.g., a specific SQL query, a Python script using the client library, a `curl` command).
|
||||
* **Success Criteria**: Explicitly state what output confirms the hypothesis.
|
||||
|
||||
### 5. Execute and Document
|
||||
Run the tasks in order. For each task, record:
|
||||
* **Status**: ✅ VALIDATED, ❌ FAILED, or ⚠️ INCONCLUSIVE.
|
||||
* **Findings**: Key observations and raw evidence (logs, screenshots).
|
||||
* **Conclusion**: Does this support or refute the hypothesis?
|
||||
|
||||
### 6. Determine Root Cause
|
||||
Synthesize the findings into a **Root Cause Analysis**.
|
||||
* Identify the Primary Root Cause.
|
||||
* Assign a Confidence Level.
|
||||
* Propose specific fixes.
|
||||
|
||||
## Best Practices
|
||||
|
||||
* **Be Specific**: Don't just say "check the logs." Say "grep for 'Error 500' in `/var/log/nginx/access.log`".
|
||||
* **Isolate Variables**: Change one thing at a time.
|
||||
* **Validate Assumptions**: Verify configuration and versions first (e.g., "Task 1: Validate Current Schema").
|
||||
* **Preserve Evidence**: Keep the specific trace IDs, log timestamps, or reproduction scripts.
|
||||
|
||||
## Language-Specific Modules
|
||||
|
||||
The `languages/` directory contains **modular, language-specific debugging guides**. When debugging a project, load the relevant language module to augment this protocol with language-specific tools, hypothesis categories, and validation strategies.
|
||||
|
||||
**Convention:** Each module is a standalone markdown file at `languages/{language}.md`.
|
||||
|
||||
**How to use:**
|
||||
1. Identify the primary language of the codebase being debugged
|
||||
2. Load the corresponding module from `languages/`
|
||||
3. Integrate its toolchain, hypothesis categories, and validation tasks into your debugging session
|
||||
|
||||
**Available modules:**
|
||||
|
||||
| Module | Languages/Runtimes |
|
||||
|---|---|
|
||||
| [Rust](languages/rust.md) | Rust (cargo, rustc, tokio) |
|
||||
| [Frontend](languages/frontend.md) | Vue 3, React, browser, Vite |
|
||||
|
||||
> **Contributing new modules:** To add support for a new language, create `languages/{language}.md` following the structure of existing modules. Each module should include: a toolchain reference table, language-specific hypothesis categories, validation task patterns, and an error-type-to-first-action quick reference.
|
||||
|
||||
## Rule Compliance
|
||||
When debugging, verify against:
|
||||
- Error Handling Principles @error-handling-principles.md (proper error propagation)
|
||||
- Logging and Observability Principles @logging-and-observability-principles.md (structured logging for diagnostics)
|
||||
- Testing Strategy @testing-strategy.md (regression test for the fix)
|
||||
@@ -0,0 +1,82 @@
|
||||
# Debugging Session: [Issue Name]
|
||||
|
||||
## SYSTEM CONTEXT
|
||||
[Describe the system components involved, relevant technologies, and the general environment.]
|
||||
|
||||
## PROBLEM STATEMENT
|
||||
|
||||
**Symptom**:
|
||||
[Describe the observable issue. What is happening vs what should happen?]
|
||||
|
||||
**Hypotheses**:
|
||||
1. **[Layer/Component] Hypothesis**: [Description of potential cause]
|
||||
2. **[Layer/Component] Hypothesis**: [Description of potential cause]
|
||||
|
||||
## VALIDATION TASKS
|
||||
|
||||
### Task 1: [Task Name]
|
||||
|
||||
**Objective**: [What specific fact or behavior are we verifying?]
|
||||
|
||||
**Steps**:
|
||||
1. [Step 1]
|
||||
2. [Step 2]
|
||||
3. ...
|
||||
|
||||
**Required Code Pattern/Command**:
|
||||
```[language]
|
||||
[Code or command to execute]
|
||||
```
|
||||
|
||||
**Success Criteria**: [What specific output confirms or denies the hypothesis?]
|
||||
|
||||
---
|
||||
|
||||
### Task 2: [Task Name]
|
||||
|
||||
**Objective**: ...
|
||||
|
||||
...
|
||||
|
||||
## OUTPUT FORMAT (for each task)
|
||||
|
||||
```markdown
|
||||
### Task N: [Task Name]
|
||||
|
||||
**Status**: ✅ VALIDATED / ❌ FAILED / ⚠️ INCONCLUSIVE
|
||||
|
||||
**Findings**:
|
||||
- [Observation 1]
|
||||
- [Observation 2]
|
||||
|
||||
**Evidence**:
|
||||
[Logs, screenshots, output]
|
||||
|
||||
**Conclusion**: [Impact on hypotheses]
|
||||
```
|
||||
|
||||
## FINAL ROOT CAUSE DETERMINATION
|
||||
|
||||
```markdown
|
||||
## ROOT CAUSE ANALYSIS SUMMARY
|
||||
|
||||
**Primary Root Cause**: [Component/Reason]
|
||||
|
||||
**Confidence Level**: [High / Medium / Low]
|
||||
|
||||
**Supporting Evidence**:
|
||||
1. [Evidence 1]
|
||||
2. [Evidence 2]
|
||||
|
||||
**Recommended Fix Priority**:
|
||||
1. [Fix A]
|
||||
2. [Fix B]
|
||||
|
||||
**Risks if Wrong**:
|
||||
- [Risk 1]
|
||||
```
|
||||
|
||||
## EXECUTION GUIDELINES
|
||||
- Execute tasks in order.
|
||||
- Document all commands and outputs.
|
||||
- Adapt the plan if new information invalidates later tasks.
|
||||
@@ -0,0 +1,342 @@
|
||||
# Frontend Debugging Module
|
||||
|
||||
Language-specific debugging guide for frontend projects (Vue 3, React, browser-based SPAs). Use alongside the main [Debugging Protocol](../SKILL.md).
|
||||
|
||||
---
|
||||
|
||||
## Toolchain Reference
|
||||
|
||||
| Tool | Purpose | When to Use |
|
||||
|---|---|---|
|
||||
| Chrome DevTools — **Console** | Runtime errors, warnings, `console.debug` traces | **Always first** — fastest signal |
|
||||
| Chrome DevTools — **Network** | Request waterfall, response status, payload shape | Data not rendering, 401/403/500, CORS |
|
||||
| Chrome DevTools — **Elements** | Computed styles, DOM structure, CSS specificity | Styling / layout issues |
|
||||
| Chrome DevTools — **Performance** | Frame rate, scripting time, rendering phases | Jank, slow interactions |
|
||||
| Chrome DevTools — **Application** | localStorage, sessionStorage, cookies, service workers | Auth state, cached flags, stale SW |
|
||||
| Vue DevTools (browser extension) | Component tree, Pinia state, router history, event timeline | Vue-specific state/lifecycle issues |
|
||||
| `router.afterEach` / `beforeEach` logging | Navigation guard execution trace | Routing bugs, blank screens, redirect loops |
|
||||
| `onMounted` / `useEffect` logging | Component lifecycle confirmation | Component "never mounts" symptoms |
|
||||
| `vite dev --force` | Force-clear Vite module cache | Stale build artifacts after dependency changes |
|
||||
| `npx vue-tsc -b` | Full template type checking | Type errors that only surface at build time |
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Initialize Session — Frontend Context
|
||||
|
||||
Add these fields to the debugging session document:
|
||||
|
||||
```markdown
|
||||
### Frontend Context
|
||||
- **Framework version:** (e.g. Vue 3.5.x, React 19.x)
|
||||
- **Bundler:** (e.g. Vite 7.x)
|
||||
- **CSS framework:** (e.g. Tailwind v4, none)
|
||||
- **State management:** (e.g. Pinia 3.x, Redux, Zustand)
|
||||
- **Router:** (e.g. Vue Router 5.x, React Router 7.x)
|
||||
- **Browser:** (exact version — behavior varies across engines)
|
||||
- **PWA/Service Worker:** yes / no (stale caches are a common red herring)
|
||||
- **Auth provider:** (e.g. Supabase Auth, Firebase Auth, custom JWT)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Hypothesis Categories
|
||||
|
||||
Common frontend-specific hypothesis categories:
|
||||
|
||||
| Category | Example Hypotheses |
|
||||
|---|---|
|
||||
| **Routing & Navigation** | Guard blocks route; redirect loop between auth/onboarding; `router.replace()` resolves but component never mounts; stale `meta` on route definition |
|
||||
| **State Management** | Pinia store holds stale data after auth change; double-fetch from co-mounted components; reactive loss from `const { x } = store` without `storeToRefs`; store initialized before auth resolves |
|
||||
| **CSS × Animation** | `transitionend` event never fires due to `@layer` cascade; `mode="out-in"` blocks entering component indefinitely; animation stuck in leaving state; blank screen with zero console errors |
|
||||
| **Data & Rendering** | Backend payload shape mismatch (missing field → `undefined` → silent `v-if` failure); wrong reactive access pattern (`v-if="fn()"` instead of `computed`); component renders before async data resolves |
|
||||
| **Auth & Session** | Token expired but UI doesn't redirect; `localStorage` holds stale session after logout; race between auth state resolution and guard evaluation; middleware applies to wrong routes |
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Validation Task Patterns
|
||||
|
||||
### Routing & Navigation — Blank Screen After Navigation
|
||||
|
||||
**Symptom:** User navigates, router reports success, destination component never renders. Page is blank with zero console errors.
|
||||
|
||||
**Validation steps:**
|
||||
|
||||
1. **Confirm no component mount** — add lifecycle logging to the destination:
|
||||
```javascript
|
||||
// Vue 3
|
||||
onMounted(() => console.debug('[TargetView] onMounted fired'));
|
||||
// React
|
||||
useEffect(() => console.debug('[TargetView] mounted'), []);
|
||||
```
|
||||
If the mount log never fires but the router reports navigation complete → this is NOT a routing logic bug.
|
||||
|
||||
2. **Trace the guard chain** — add diagnostic logging:
|
||||
```typescript
|
||||
router.beforeEach((to, from) => {
|
||||
console.debug(`[Router] beforeEach: ${from.path} → ${to.path}`);
|
||||
});
|
||||
router.afterEach((to) => {
|
||||
console.debug(`[Router] afterEach: arrived at ${to.path}`);
|
||||
});
|
||||
```
|
||||
If `afterEach` fires but `onMounted` does not → suspect the **transition mechanism** (see CSS × Animation below).
|
||||
|
||||
3. **Check for redirect loops** — watch the URL bar or router logs for oscillating paths (e.g. `/login` → `/home` → `/onboarding` → `/login`).
|
||||
|
||||
4. **Verify auth state at guard time** — log the auth store's state inside each guard to confirm it's resolved (not still loading).
|
||||
|
||||
---
|
||||
|
||||
### State Management — Stale or Missing Data
|
||||
|
||||
**Symptom:** Component renders but shows stale data, or renders empty despite successful API call.
|
||||
|
||||
**Validation steps:**
|
||||
|
||||
1. **Inspect Pinia state via Vue DevTools** — check the store's current values. Is the data present?
|
||||
|
||||
2. **Check for destructuring reactivity loss:**
|
||||
```typescript
|
||||
// ❌ Loses reactivity — x is a plain value snapshot
|
||||
const { tasks } = useTaskStore();
|
||||
// ✅ Preserves reactivity
|
||||
const { tasks } = storeToRefs(useTaskStore());
|
||||
```
|
||||
|
||||
3. **Check for race conditions with auth:**
|
||||
```typescript
|
||||
// The store action fires before auth resolves → API returns 401 → store stays empty
|
||||
// Fix: await auth resolution before first store fetch
|
||||
```
|
||||
|
||||
4. **Check for duplicate fetches from co-mounted components:**
|
||||
- Open Network tab → filter by XHR → navigate to the page
|
||||
- If two identical requests appear → the store needs a loading-state guard
|
||||
|
||||
---
|
||||
|
||||
### CSS × Animation — Transition Stuck (Blank Screen)
|
||||
|
||||
> **This section subsumes the former `css-transition-pitfalls` skill.**
|
||||
|
||||
**Symptom:** Component "never mounts" after a route change. Blank screen with zero console errors. Works on hard refresh but recurs on SPA navigation. `router.afterEach` fires but `onMounted` does not.
|
||||
|
||||
#### The Anti-Pattern
|
||||
|
||||
```
|
||||
User clicks link → Router navigates → Old component should leave → New component should enter
|
||||
↓
|
||||
CSS framework's @layer rules
|
||||
override transition properties
|
||||
↓
|
||||
transitionend event NEVER fires
|
||||
↓
|
||||
SPA framework waits forever
|
||||
↓
|
||||
New component NEVER mounts
|
||||
↓
|
||||
BLANK SCREEN (no errors)
|
||||
```
|
||||
|
||||
#### Why It's Hard to Catch
|
||||
|
||||
1. **No errors** — everything completes from the router's perspective
|
||||
2. **Works on refresh** — hard refresh bypasses the SPA transition entirely
|
||||
3. **Looks like a logic bug** — the natural instinct is to debug auth state, guards, or template conditionals
|
||||
4. **Intermittent** — may only trigger on specific route pairs where the leaving component has conflicting animations
|
||||
|
||||
#### Affected SPA Frameworks
|
||||
|
||||
| SPA Framework | Transition Mechanism | Vulnerable Mode |
|
||||
|---|---|---|
|
||||
| **Vue 3** | `<Transition mode="out-in">` | `mode="out-in"` blocks enter until leave completes |
|
||||
| **React** (Framer Motion) | `AnimatePresence mode="wait"` | `mode="wait"` blocks enter until exit completes |
|
||||
| **React** (React Transition Group) | `SwitchTransition mode="out-in"` | Same pattern |
|
||||
| **Svelte** | `transition:` directive with `|local` | Less common but possible |
|
||||
|
||||
#### CSS Frameworks That Can Cause This
|
||||
|
||||
| CSS Framework | Why |
|
||||
|---|---|
|
||||
| **Tailwind v4** | Uses `@layer theme, base, components, utilities` — unlayered styles (like Vue's transition classes) can be unexpectedly deprioritized |
|
||||
| **Open Props** | Uses `@layer` for custom property organization |
|
||||
| **Any framework using `@layer`** | The CSS Cascade spec gives layered styles lower priority than unlayered, BUT framework bundlers may re-layer your styles |
|
||||
|
||||
#### Validation Steps
|
||||
|
||||
1. **Confirm the symptom class** — add `onMounted` logging to the destination component. If mount log never fires but `afterEach` does → transition stuck.
|
||||
|
||||
2. **Rule out logic issues (time-box: 15 minutes):**
|
||||
- Navigation guard allows the route (check `afterEach` logging)
|
||||
- Auth state is correct at navigation time
|
||||
- No redirect loops
|
||||
|
||||
3. **Inspect the transition mechanism** — check your root layout for sequential transition modes:
|
||||
```html
|
||||
<!-- Vue: look for mode="out-in" -->
|
||||
<Transition name="fade" mode="out-in">
|
||||
<!-- React Framer Motion: look for mode="wait" -->
|
||||
<AnimatePresence mode="wait">
|
||||
<!-- React Transition Group: look for mode="out-in" -->
|
||||
<SwitchTransition mode="out-in">
|
||||
```
|
||||
If found → this is almost certainly the cause.
|
||||
|
||||
4. **Verify CSS layer interference:**
|
||||
```bash
|
||||
# Check for @layer in built CSS
|
||||
grep -r "@layer" node_modules/tailwindcss/ | head -5
|
||||
grep -r "@import.*tailwindcss" src/
|
||||
# In browser DevTools: inspect the leaving element during transition
|
||||
# Look for: computed opacity, computed transition property
|
||||
```
|
||||
|
||||
5. **Apply the fix — remove sequential transition mode:**
|
||||
```html
|
||||
<!-- BEFORE (broken): sequential, depends on transitionend -->
|
||||
<Transition name="fade" mode="out-in">
|
||||
<component :is="Component" />
|
||||
</Transition>
|
||||
|
||||
<!-- AFTER (fixed): simultaneous, always mounts new component -->
|
||||
<Transition name="fade">
|
||||
<component :is="Component" :key="$route.path" />
|
||||
</Transition>
|
||||
```
|
||||
|
||||
Update CSS to handle simultaneous leave/enter:
|
||||
```css
|
||||
.fade-enter-active {
|
||||
transition: opacity 0.15s ease-in !important;
|
||||
}
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.15s ease-out !important;
|
||||
position: absolute !important;
|
||||
width: 100% !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
}
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0 !important;
|
||||
}
|
||||
/* Parent needs position:relative to contain the absolute element */
|
||||
#main-content {
|
||||
position: relative;
|
||||
}
|
||||
```
|
||||
|
||||
> **Why `!important`?** CSS `@layer` cascade can override your transition classes. `!important` guarantees your transition properties apply regardless of layer ordering.
|
||||
|
||||
#### Key Insight
|
||||
|
||||
> **When a component doesn't mount after navigation: suspect the transition mechanism FIRST, not the routing logic.** The router will report success even when the component never renders. This is counter-intuitive because the blank screen looks like a routing/auth problem, but it's actually a CSS/animation problem.
|
||||
|
||||
---
|
||||
|
||||
### Data & Rendering — Silent Template Failures
|
||||
|
||||
**Symptom:** Page renders but expected content is missing. No console errors. Data is present in the store.
|
||||
|
||||
**Validation steps:**
|
||||
|
||||
1. **Check for `v-if` on optional properties:**
|
||||
```html
|
||||
<!-- Silent failure if exercise.options is undefined — v-if produces no error -->
|
||||
<div v-if="exercise.options.length > 0">
|
||||
<!-- Fix: optional chaining -->
|
||||
<div v-if="exercise.options?.length">
|
||||
```
|
||||
|
||||
2. **Check for plain function calls in templates:**
|
||||
```html
|
||||
<!-- ❌ Re-evaluates on every render cycle; may return inconsistent results -->
|
||||
<div v-if="isVisible()">
|
||||
<!-- ✅ Cached, reactive -->
|
||||
<div v-if="isVisibleComputed">
|
||||
```
|
||||
|
||||
3. **Check for backend payload mismatches** — log the raw API response and compare field names/types against what the component expects. Common: `snake_case` from backend vs `camelCase` in frontend.
|
||||
|
||||
4. **Check for async rendering issues:**
|
||||
```html
|
||||
<!-- Component renders before data arrives — shows empty state permanently -->
|
||||
<TaskCard :task="task" />
|
||||
<!-- Fix: guard with v-if -->
|
||||
<TaskCard v-if="task" :task="task" />
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Auth & Session — Redirect Loops and Ghost Sessions
|
||||
|
||||
**Symptom:** User gets stuck in a redirect loop between login, onboarding, and home pages. Or: user appears logged out despite having valid credentials.
|
||||
|
||||
**Validation steps:**
|
||||
|
||||
1. **Check localStorage for stale auth tokens:**
|
||||
```javascript
|
||||
// In console
|
||||
Object.keys(localStorage).filter(k => k.includes('auth') || k.includes('supabase') || k.includes('token'));
|
||||
```
|
||||
|
||||
2. **Verify auth state is resolved before guards run:**
|
||||
```typescript
|
||||
router.beforeEach(async (to) => {
|
||||
console.debug('[Guard] authStore.isAuthenticated:', authStore.isAuthenticated);
|
||||
console.debug('[Guard] authStore.isLoading:', authStore.isLoading);
|
||||
// If isLoading is true here, the guard is racing the auth resolution
|
||||
});
|
||||
```
|
||||
|
||||
3. **Check for circular guard logic:**
|
||||
- Guard A: "if not authenticated → redirect to /login"
|
||||
- Guard B: "if authenticated and not onboarded → redirect to /onboarding"
|
||||
- Guard C: "if on /onboarding and already onboarded → redirect to /home"
|
||||
- If the onboarding flag is stale or the database read is async → loop
|
||||
|
||||
4. **Test by clearing all storage and hard-refreshing:**
|
||||
```javascript
|
||||
localStorage.clear(); sessionStorage.clear(); location.reload();
|
||||
```
|
||||
If the issue resolves → the problem is stale cached state, not logic.
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: Confidence Adjustments
|
||||
|
||||
| Evidence | Confidence Impact |
|
||||
|---|---|
|
||||
| `onMounted` log never fires but `afterEach` does | **High** — definitively a transition/rendering issue, not routing |
|
||||
| DevTools Network shows no request fired | **High** — data never fetched (guard blocked, or fetch never called) |
|
||||
| Vue DevTools shows correct Pinia state but UI is empty | **High** — template rendering bug (wrong `v-if`, missing reactivity) |
|
||||
| Issue resolves on hard refresh but recurs on SPA nav | **High** — transition-stuck, service worker cache, or stale SPA state |
|
||||
| `@layer` found in CSS framework and `mode="out-in"` is used | **Definitive** for transition-stuck diagnosis |
|
||||
| Issue only appears on specific route pairs | **Medium-High** — likely CSS/animation interaction specific to the leaving component |
|
||||
| Console shows no errors at all | **Medium** — rules out JS exceptions; narrows to CSS/template/state issues |
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference: Symptom → First Action
|
||||
|
||||
| Symptom | First Action |
|
||||
|---|---|
|
||||
| Blank screen after navigation (no errors) | Add `onMounted` logging → if never fires, check `<Transition mode>` |
|
||||
| Blank screen after navigation (console errors) | Read the error — usually a runtime exception in the entering component |
|
||||
| Redirect loop (URL oscillates) | Log all guards with `beforeEach` → trace the loop pattern |
|
||||
| Data present in store but not rendered | Check `v-if` conditions for undefined/null access; check for destructuring without `storeToRefs` |
|
||||
| Component renders then immediately disappears | Check for a `watch` or `watchEffect` that re-triggers a navigation or state reset |
|
||||
| API call fires twice | Check for co-mounted components both calling the same store action; add loading-state guard |
|
||||
| Works locally but not in production | Check service worker cache; run `vite build` locally to reproduce; check env variables |
|
||||
| Works on refresh but fails on SPA navigation | Suspect transition mechanism or stale component state from previous route |
|
||||
| Auth state incorrect in guard | Log auth store state inside the guard; check if auth initialization is awaited before router starts |
|
||||
| Styles not applying | Check CSS specificity vs `@layer` cascade; inspect computed styles in DevTools Elements tab |
|
||||
|
||||
---
|
||||
|
||||
## Related Principles
|
||||
- Vue Idioms and Patterns @vue-idioms-and-patterns.md
|
||||
- TypeScript Idioms and Patterns @typescript-idioms-and-patterns.md
|
||||
- Error Handling Principles @error-handling-principles.md
|
||||
- Logging and Observability Principles @logging-and-observability-principles.md
|
||||
- Testing Strategy @testing-strategy.md
|
||||
@@ -0,0 +1,136 @@
|
||||
# Rust Debugging Module
|
||||
|
||||
Language-specific debugging guide for Rust projects. Use alongside the main [Debugging Protocol](../SKILL.md).
|
||||
|
||||
---
|
||||
|
||||
## Toolchain Reference
|
||||
|
||||
| Tool | Purpose | When to Use |
|
||||
| --------------------- | ----------------------------------------- | --------------------------------------------------- |
|
||||
| `cargo check` | Fast type/borrow checking (no codegen) | **Always first** — 2-5× faster than `cargo build` |
|
||||
| `cargo clippy` | Lint for idiomatic issues and subtle bugs | After `cargo check` passes — catches logical errors |
|
||||
| `cargo test` | Run test suite | Validate fixes, reproduce failures |
|
||||
| `cargo expand` | Expand macros to see generated code | When a macro-generated error is unclear |
|
||||
| `cargo tree` | Visualize dependency graph | Dependency conflicts, version mismatches |
|
||||
| `cargo audit` | Check for known vulnerabilities | Security-related investigations |
|
||||
| `RUST_BACKTRACE=1` | Full backtrace on panic | Runtime panics — always enable first |
|
||||
| `RUST_BACKTRACE=full` | Backtrace with line numbers | When `=1` doesn't give enough detail |
|
||||
| `miri` | Detect undefined behavior in unsafe code | Suspected UB, memory safety issues |
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Initialize Session — Rust Context
|
||||
|
||||
Add these fields to the debugging session document:
|
||||
|
||||
```markdown
|
||||
### Rust Context
|
||||
- **Rust toolchain version:** (output of `rustc --version`)
|
||||
- **Cargo.toml key dependencies:** (list with versions)
|
||||
- **Build profile:** debug / release
|
||||
- **Target:** default / cross-compile target
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Hypothesis Categories
|
||||
|
||||
Common Rust-specific hypothesis categories:
|
||||
|
||||
| Category | Example Hypotheses |
|
||||
| ------------------- | -------------------------------------------------------------------------- |
|
||||
| **Borrow Checker** | "Function borrows `self` mutably while an immutable borrow is still live" |
|
||||
| **Lifetime** | "Returned reference outlives the data it points to" |
|
||||
| **Async/Runtime** | "Blocking call inside async context starves the Tokio runtime" |
|
||||
| **Type Mismatch** | "Trait bound not satisfied due to missing `Send`/`Sync` on spawned future" |
|
||||
| **FFI/Unsafe** | "Raw pointer from C FFI is null but not checked before dereference" |
|
||||
| **Macro Expansion** | "Procedural macro generates code that conflicts with local type names" |
|
||||
| **Dependency** | "Two crates depend on different major versions of the same crate" |
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Validation Task Patterns
|
||||
|
||||
### Borrow Checker Errors — Reading Strategy
|
||||
|
||||
When the compiler emits a borrow error:
|
||||
|
||||
1. **Read the error bottom-up** — the last line shows *where* the conflict is, not *why*
|
||||
2. **Find the two conflicting borrows** — the error always names them
|
||||
3. **Check the lifetime of each borrow** — does one live longer than expected?
|
||||
4. **Common fixes:**
|
||||
- Clone the data (only if cheap — document with `// CLONE:`)
|
||||
- Restructure to avoid overlapping borrows (split struct into components)
|
||||
- Use `Cell`/`RefCell` for interior mutability (single-threaded only)
|
||||
- Use scoping braces `{ }` to limit borrow lifetime
|
||||
|
||||
### Async Debugging
|
||||
|
||||
```bash
|
||||
# Enable Tokio's runtime tracing
|
||||
RUSTFLAGS="--cfg tokio_unstable" cargo build
|
||||
# Then use tokio-console for live runtime inspection
|
||||
tokio-console
|
||||
```
|
||||
|
||||
Validation steps for async issues:
|
||||
1. Check for `spawn_blocking` around any blocking I/O
|
||||
2. Verify all `.await` points — are any holding locks across them?
|
||||
3. Look for `select!` branches that aren't cancellation-safe
|
||||
4. Run with `tokio::time::pause()` in tests for deterministic timing
|
||||
|
||||
### Macro Debugging
|
||||
|
||||
```bash
|
||||
# Expand all macros in a specific file
|
||||
cargo expand --lib path::to::module
|
||||
|
||||
# Expand only derive macros
|
||||
cargo expand --lib path::to::module 2>&1 | grep -A 50 "impl"
|
||||
```
|
||||
|
||||
### Unsafe / UB Detection
|
||||
|
||||
```bash
|
||||
# Run tests under Miri (detects UB, use-after-free, unaligned access)
|
||||
cargo +nightly miri test
|
||||
|
||||
# Common Miri flags
|
||||
MIRIFLAGS="-Zmiri-backtrace=full" cargo +nightly miri test
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: Confidence Adjustments
|
||||
|
||||
| Evidence | Confidence Impact |
|
||||
| ---------------------------------------------- | -------------------------------------------- |
|
||||
| `cargo check` error points exactly at the line | High — compiler errors are precise |
|
||||
| `cargo clippy` lint matches the symptom | Medium-High — Clippy catches many real bugs |
|
||||
| `miri` reports UB | **Definitive** for unsafe code paths |
|
||||
| `cargo expand` shows unexpected generated code | High — confirms macro-related issues |
|
||||
| "works in debug, fails in release" pattern | Likely UB or optimization-sensitive ordering |
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference: Error Code → First Action
|
||||
|
||||
| Error Type | First Action |
|
||||
| ----------------------------- | ------------------------------------------------------------ |
|
||||
| `E0382` (use of moved value) | Check if value can be borrowed instead of moved |
|
||||
| `E0502` (conflicting borrows) | Identify the two borrows, scope them apart |
|
||||
| `E0106` (missing lifetime) | Add lifetime parameter, check if owned type is better |
|
||||
| `E0277` (trait not satisfied) | Check `Send`/`Sync` bounds, look for non-Send types in async |
|
||||
| `E0308` (type mismatch) | Check `From`/`Into` impls, verify generic constraints |
|
||||
| Runtime panic | `RUST_BACKTRACE=1 cargo test` — read the backtrace |
|
||||
| Segfault / UB | `cargo +nightly miri test` — Miri will find it |
|
||||
| Macro error | `cargo expand` the module — read the generated code |
|
||||
|
||||
---
|
||||
|
||||
## Related Principles
|
||||
- Rust Idioms and Patterns @rust-idioms-and-patterns.md
|
||||
- Error Handling Principles @error-handling-principles.md
|
||||
- Concurrency and Threading Principles @concurrency-and-threading-principles.md
|
||||
- Testing Strategy @testing-strategy.md
|
||||
@@ -0,0 +1,50 @@
|
||||
---
|
||||
name: frontend-design
|
||||
description: Generates distinctive, production-grade frontend interfaces and artifacts (React, Vue, HTML/CSS). Prioritizes bold aesthetics, unique typography, and motion to avoid generic designs. Use when building websites, landing pages, dashboards, posters, or when the user requests to style, beautify, or create visually striking UI.
|
||||
---
|
||||
|
||||
This skill guides creation of distinctive, production-grade frontend interfaces that avoid generic "AI slop" aesthetics. Implement real working code with exceptional attention to aesthetic details and creative choices.
|
||||
|
||||
The user provides frontend requirements: a component, page, application, or interface to build. They may include context about the purpose, audience, or technical constraints.
|
||||
|
||||
## Design Thinking
|
||||
|
||||
Before coding, understand the context and commit to a BOLD aesthetic direction:
|
||||
- **Purpose**: What problem does this interface solve? Who uses it?
|
||||
- **Tone**: Pick an extreme: brutally minimal, maximalist chaos, retro-futuristic, organic/natural, luxury/refined, playful/toy-like, editorial/magazine, brutalist/raw, art deco/geometric, soft/pastel, industrial/utilitarian, etc. There are so many flavors to choose from. Use these for inspiration but design one that is true to the aesthetic direction.
|
||||
- **Constraints**: Technical requirements (framework, performance, accessibility).
|
||||
- **Differentiation**: What makes this UNFORGETTABLE? What's the one thing someone will remember?
|
||||
|
||||
**CRITICAL**: Choose a clear conceptual direction and execute it with precision. Bold maximalism and refined minimalism both work - the key is intentionality, not intensity.
|
||||
|
||||
Then implement working code (HTML/CSS/JS, React, Vue, etc.) that is:
|
||||
- Production-grade and functional
|
||||
- Visually striking and memorable
|
||||
- Cohesive with a clear aesthetic point-of-view
|
||||
- Meticulously refined in every detail
|
||||
|
||||
## Frontend Aesthetics Guidelines
|
||||
|
||||
Focus on:
|
||||
- **Typography**: Choose fonts that are beautiful, unique, and interesting. Avoid generic fonts like Arial and Inter; opt instead for distinctive choices that elevate the frontend's aesthetics; unexpected, characterful font choices. Pair a distinctive display font with a refined body font.
|
||||
- **Color & Theme**: Commit to a cohesive aesthetic. Use CSS variables for consistency. Dominant colors with sharp accents outperform timid, evenly-distributed palettes.
|
||||
- **Motion**: Use animations for effects and micro-interactions. Prioritize CSS-only solutions for HTML. Use Motion library for React when available. Focus on high-impact moments: one well-orchestrated page load with staggered reveals (animation-delay) creates more delight than scattered micro-interactions. Use scroll-triggering and hover states that surprise.
|
||||
- **Spatial Composition**: Unexpected layouts. Asymmetry. Overlap. Diagonal flow. Grid-breaking elements. Generous negative space OR controlled density.
|
||||
- **Backgrounds & Visual Details**: Create atmosphere and depth rather than defaulting to solid colors. Add contextual effects and textures that match the overall aesthetic. Apply creative forms like gradient meshes, noise textures, geometric patterns, layered transparencies, dramatic shadows, decorative borders, custom cursors, and grain overlays.
|
||||
|
||||
NEVER use generic AI-generated aesthetics like overused font families (Inter, Roboto, Arial, system fonts), cliched color schemes (particularly purple gradients on white backgrounds), predictable layouts and component patterns, and cookie-cutter design that lacks context-specific character.
|
||||
|
||||
Interpret creatively and make unexpected choices that feel genuinely designed for the context. No design should be the same. Vary between light and dark themes, different fonts, different aesthetics. NEVER converge on common choices (Space Grotesk, for example) across generations.
|
||||
|
||||
9. All implementations must also pass through the agent's standard rule checks — this skill enhances visual output, but does not override engineering rules.
|
||||
|
||||
## Rule Compliance
|
||||
Before implementing, verify against:
|
||||
- Project Structure @project-structure.md (component organization)
|
||||
- Testing Strategy @testing-strategy.md (component tests)
|
||||
- Security Principles @security-principles.md (XSS prevention)
|
||||
- Accessibility Principles @accessibility-principles.md (WCAG compliance)
|
||||
|
||||
**IMPORTANT**: Match implementation complexity to the aesthetic vision. Maximalist designs need elaborate code with extensive animations and effects. Minimalist or refined designs need restraint, precision, and careful attention to spacing, typography, and subtle details. Elegance comes from executing the vision well.
|
||||
|
||||
Remember: You are capable of extraordinary creative work. Don't hold back, show what can truly be created when thinking outside the box and committing fully to a distinctive vision.
|
||||
@@ -0,0 +1,90 @@
|
||||
---
|
||||
name: guardrails
|
||||
description: Pre-flight checklist and post-implementation self-review protocol. Use before generating any code (pre-flight) and after writing code but before verification (self-review) to catch issues early.
|
||||
---
|
||||
|
||||
# Agent Guardrails Skill
|
||||
|
||||
## Purpose
|
||||
Structured checklists that ensure the agent considers all applicable rules before and after writing code. Catches issues that would otherwise only surface during verification.
|
||||
|
||||
## When to Invoke
|
||||
- **Pre-Flight:** At the start of Phase 2 (Implement), before writing any code
|
||||
- **Self-Review:** At the end of Phase 2 (Implement), after writing code but before Phase 3/4
|
||||
|
||||
---
|
||||
|
||||
## Pre-Flight Checklist
|
||||
|
||||
Run through this checklist **before writing any code**:
|
||||
|
||||
- [ ] Identified all applicable rules from `.agents/rules/`
|
||||
- [ ] Searched for existing patterns in codebase (Pattern Discovery Protocol from `architectural-pattern.md`)
|
||||
- [ ] Confirmed project structure alignment (`project-structure.md`)
|
||||
- [ ] Identified I/O boundaries that need abstraction
|
||||
- [ ] Determined test strategy (unit/integration/E2E)
|
||||
- [ ] Reviewed `rule-priority.md` for any potential conflicts
|
||||
|
||||
If any item cannot be checked, **stop and resolve** before proceeding.
|
||||
|
||||
---
|
||||
|
||||
## Post-Implementation Self-Review
|
||||
|
||||
Run through this checklist **after writing code, before verification**:
|
||||
|
||||
### Security
|
||||
- [ ] No hardcoded secrets or configuration values
|
||||
- [ ] All user input validated at system boundaries
|
||||
- [ ] Parameterized queries (no string concatenation for SQL)
|
||||
|
||||
### Testability
|
||||
- [ ] All I/O operations behind interfaces/abstractions
|
||||
- [ ] Business logic is pure (no side effects in calculations)
|
||||
- [ ] Dependencies injected, not hardcoded
|
||||
|
||||
### Observability
|
||||
- [ ] All public operation entry points logged (start/success/failure)
|
||||
- [ ] Structured logging with correlation IDs
|
||||
- [ ] Appropriate log levels (not everything is INFO)
|
||||
|
||||
### Error Handling
|
||||
- [ ] Error paths handled explicitly (no empty catch blocks)
|
||||
- [ ] Errors provide context (wrapped with additional info)
|
||||
- [ ] Resources cleaned up in error paths (defer/finally)
|
||||
|
||||
### Testing
|
||||
- [ ] Tests cover happy path
|
||||
- [ ] Tests cover at least 2 error paths
|
||||
- [ ] Tests cover edge cases relevant to the domain
|
||||
- [ ] If I/O adapters were modified: integration tests exist and pass
|
||||
- [ ] If UI was modified: E2E tests exist (or Phase 3.5 is planned)
|
||||
|
||||
### Consistency
|
||||
- [ ] Follows existing codebase patterns (>80% consistency)
|
||||
- [ ] Naming conventions match the codebase
|
||||
- [ ] File organization matches `project-structure.md`
|
||||
|
||||
---
|
||||
|
||||
## Language-Specific Self-Review
|
||||
|
||||
After completing the universal checklist above, load the relevant language-specific checklist:
|
||||
|
||||
| Language | Checklist |
|
||||
|---|---|
|
||||
| **Go** | `languages/go.md` |
|
||||
| **TypeScript** | `languages/typescript.md` *(placeholder — create when needed)* |
|
||||
| **Flutter/Dart** | `languages/flutter.md` *(placeholder — create when needed)* |
|
||||
| **Rust** | `languages/rust.md` *(placeholder — create when needed)* |
|
||||
|
||||
> Only load the file for languages you are actively writing. If the file doesn't exist yet, skip — but flag its absence so it can be created.
|
||||
|
||||
---
|
||||
|
||||
## Rule Compliance
|
||||
This skill enforces:
|
||||
- All mandates (always-on rules)
|
||||
- Architectural Patterns @architectural-pattern.md
|
||||
- Testing Strategy @testing-strategy.md
|
||||
- Rule Priority @rule-priority.md
|
||||
@@ -0,0 +1,71 @@
|
||||
# Go Self-Review Checklist
|
||||
|
||||
> **Load this file** after completing the universal self-review in `SKILL.md`, when the code under review is Go.
|
||||
|
||||
---
|
||||
|
||||
## Resource Cleanup
|
||||
|
||||
- [ ] **No bare `defer X.Close()`** — all deferred closers use error-checked closures with `slog`
|
||||
- [ ] **No `//nolint:errcheck`** directives anywhere in new code
|
||||
- [ ] **Transaction rollbacks** check for `sql.ErrTxDone` before logging
|
||||
- [ ] **HTTP response bodies** are drained (`io.Copy(io.Discard, ...)`) before closing
|
||||
|
||||
### Correct Patterns
|
||||
|
||||
```go
|
||||
// ✅ Row/statement/connection Close
|
||||
defer func() {
|
||||
if err := rows.Close(); err != nil {
|
||||
slog.Warn("failed to close rows", "error", err, "operation", "<operation>")
|
||||
}
|
||||
}()
|
||||
|
||||
// ✅ Transaction Rollback
|
||||
defer func() {
|
||||
if err := tx.Rollback(); err != nil && !errors.Is(err, sql.ErrTxDone) {
|
||||
slog.Error("failed to rollback transaction", "error", err, "operation", "<operation>")
|
||||
}
|
||||
}()
|
||||
|
||||
// ✅ HTTP Response Body
|
||||
defer func() {
|
||||
if _, err := io.Copy(io.Discard, resp.Body); err != nil {
|
||||
slog.Warn("failed to drain response body", "error", err)
|
||||
}
|
||||
if err := resp.Body.Close(); err != nil {
|
||||
slog.Warn("failed to close response body", "error", err)
|
||||
}
|
||||
}()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Logging
|
||||
|
||||
- [ ] **No `fmt.Printf` / `fmt.Println` / `log.Printf`** — only `slog` in production code
|
||||
- [ ] All log calls use **structured key-value pairs** (no `fmt.Sprintf` inside log messages)
|
||||
- [ ] Error logs include `"error", err` as a key-value pair
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
- [ ] All errors are wrapped with context using `%w`: `fmt.Errorf("doing X: %w", err)`
|
||||
- [ ] No duplicate wrapping of the same error
|
||||
- [ ] Sentinel errors used for expected conditions (`ErrNotFound`, `ErrUnauthorized`)
|
||||
|
||||
---
|
||||
|
||||
## Static Analysis
|
||||
|
||||
- [ ] `go vet ./...` passes with zero issues
|
||||
- [ ] No new `//nolint:` directives without a `// NOLINT:` rationale comment
|
||||
- [ ] `//nolint:errcheck` specifically is **never** used — handle the error instead
|
||||
|
||||
---
|
||||
|
||||
## References
|
||||
- Go Idioms and Patterns @go-idioms-and-patterns.md (§ Idiomatic Patterns, § Formatting and Static Analysis)
|
||||
- Logging and Observability Principles @logging-and-observability-principles.md (§ Go/slog)
|
||||
- Error Handling Principles @error-handling-principles.md (§ Resource Cleanup)
|
||||
@@ -0,0 +1,93 @@
|
||||
---
|
||||
name: mobile-design
|
||||
description: Generates distinctive, production-grade mobile interfaces for Flutter and React Native. Prioritizes platform-native patterns, adaptive layouts, and fluid motion. Use when building mobile apps, screens, widgets, or when the user requests to style or create visually striking mobile UI.
|
||||
---
|
||||
|
||||
# Mobile Design Skill
|
||||
|
||||
This skill guides creation of distinctive, production-grade mobile interfaces that feel native to the platform while maintaining a unique aesthetic identity. Implement real working code with exceptional attention to platform conventions and creative design choices.
|
||||
|
||||
The user provides mobile UI requirements: a screen, widget, feature, or complete app interface. They may include context about the platform (iOS/Android), audience, or technical constraints.
|
||||
|
||||
## Design Thinking
|
||||
|
||||
Before coding, understand the context and commit to a platform-aware aesthetic direction:
|
||||
- **Platform**: iOS (Cupertino), Android (Material 3), or cross-platform? Each has distinct expectations.
|
||||
- **Tone**: Pick a direction — refined luxury, playful/energetic, utilitarian/efficient, editorial/content-first, organic/warm, bold/geometric, soft/pastel, etc.
|
||||
- **Constraints**: Performance on low-end devices, offline capability, accessibility.
|
||||
- **Differentiation**: What makes this app memorable? What's the signature interaction?
|
||||
|
||||
**CRITICAL**: Mobile users have muscle memory. Respect platform conventions (navigation patterns, gesture zones, system UI) while injecting personality through typography, color, motion, and layout composition.
|
||||
|
||||
## Mobile Design Guidelines
|
||||
|
||||
### Platform Conventions
|
||||
- **iOS**: Bottom tab bar, large title navigation, swipe-to-go-back, Cupertino widgets
|
||||
- **Android**: Material 3, navigation rail/drawer, FAB, top app bar
|
||||
- **Cross-platform**: Use adaptive widgets that render platform-appropriate UI
|
||||
|
||||
### Typography
|
||||
- Choose distinctive but highly readable fonts at mobile sizes (16px+ body)
|
||||
- Respect dynamic type / font scaling — never hardcode font sizes
|
||||
- Use `TextTheme` in Flutter, keep a consistent type scale
|
||||
- Pair a characterful display font with a supremely legible body font
|
||||
|
||||
### Color & Theme
|
||||
- Support **both light and dark themes** — always
|
||||
- Use `ColorScheme` / `ThemeData` for consistency
|
||||
- Ensure contrast ratios work on OLED (pure black) and LCD displays
|
||||
- Use color purposefully — primary actions, status indicators, brand identity
|
||||
|
||||
### Motion & Animation
|
||||
- Use **meaningful motion** — animations should communicate state changes, not decorate
|
||||
- Keep animations under 300ms for interactions, up to 500ms for transitions
|
||||
- Use `Hero` animations for shared element transitions
|
||||
- Stagger list item animations for visual polish
|
||||
- Respect `reduceMotion` / accessibility settings — always provide a static fallback
|
||||
|
||||
### Layout & Spacing
|
||||
- Design for **one-handed use** — critical actions in thumb-reach zone (bottom 60% of screen)
|
||||
- Use safe area insets — never overlap system UI (notch, home indicator, status bar)
|
||||
- Design for varying screen sizes — phones, tablets, foldables
|
||||
- Use responsive breakpoints: compact (<600dp), medium (600-840dp), expanded (>840dp)
|
||||
- Generous touch targets: minimum 48x48dp
|
||||
|
||||
### Performance
|
||||
- Avoid rebuilding expensive widget trees — use `const` constructors
|
||||
- Lazy-load lists with `ListView.builder` (never `ListView` for large lists)
|
||||
- Optimize images — use `cached_network_image`, proper resolution variants
|
||||
- Profile on real devices, not just emulators
|
||||
|
||||
## Flutter-Specific Patterns
|
||||
|
||||
```dart
|
||||
// ✅ Good: Adaptive widget that respects platform
|
||||
Widget buildButton(BuildContext context) {
|
||||
return Platform.isIOS
|
||||
? CupertinoButton(child: text, onPressed: onTap)
|
||||
: ElevatedButton(child: text, onPressed: onTap);
|
||||
}
|
||||
|
||||
// ✅ Good: Responsive layout with breakpoints
|
||||
Widget build(BuildContext context) {
|
||||
final width = MediaQuery.of(context).size.width;
|
||||
if (width >= 840) return _expandedLayout();
|
||||
if (width >= 600) return _mediumLayout();
|
||||
return _compactLayout();
|
||||
}
|
||||
|
||||
// ❌ Bad: Hardcoded sizes that break on different devices
|
||||
Container(width: 375, height: 812) // iPhone X only!
|
||||
```
|
||||
|
||||
## Rule Compliance
|
||||
Before implementing, verify against:
|
||||
- Project Structure @project-structure.md (Flutter/Mobile layout, feature organization)
|
||||
- Testing Strategy @testing-strategy.md (widget tests, integration tests)
|
||||
- Security Principles @security-principles.md (secure storage, API key handling)
|
||||
- Accessibility Principles @accessibility-principles.md (screen reader, dynamic type, contrast)
|
||||
- Architectural Patterns @architectural-pattern.md (Riverpod 3 Notifier, repository pattern)
|
||||
|
||||
**IMPORTANT**: Mobile has unique constraints — battery life, network variability, and user context (one-handed, on-the-go). Every design choice should respect these realities. Beautiful UI that drains battery or stutters on scroll is a failure.
|
||||
|
||||
Remember: The best mobile apps feel like they were designed specifically for the device in your hand. Make every pixel and every interaction intentional.
|
||||
@@ -0,0 +1,227 @@
|
||||
---
|
||||
name: perf-optimization
|
||||
description: >
|
||||
Profile-driven performance optimization protocol. Use when profiling data (CPU, heap, trace)
|
||||
is available or when the user requests performance analysis. Covers methodology, pattern catalog,
|
||||
safety invariants, and when-to-stop heuristics. Language-specific tooling is in languages/*.md.
|
||||
---
|
||||
|
||||
# Performance Optimization Skill
|
||||
|
||||
## When to Use
|
||||
|
||||
- User provides profiling data (pprof, flamegraph, py-spy, Chrome DevTools, Dart DevTools)
|
||||
- User asks to analyze or optimize performance of a specific component
|
||||
- A benchmark regression is detected
|
||||
- After deploying a new feature that touches a hot path
|
||||
|
||||
## Core Methodology
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
P1[Profile] --> P2[Analyze]
|
||||
P2 --> P3[Prioritize]
|
||||
P3 --> P4[Optimize]
|
||||
P4 --> P5[Benchmark]
|
||||
P5 --> P6{Improvement?}
|
||||
P6 -->|Yes| P7[Verify & Ship]
|
||||
P6 -->|No| P3
|
||||
```
|
||||
|
||||
### Step 1: Profile
|
||||
|
||||
Collect profiling data using the language-appropriate tool. Load the relevant `languages/*.md` module for exact commands.
|
||||
|
||||
**Output:** Raw profiling data (CPU profile, heap profile, or trace).
|
||||
|
||||
### Step 2: Analyze
|
||||
|
||||
Read the profile. Focus on these principles (universal across all runtimes):
|
||||
|
||||
1. **Focus on `cum` (cumulative):** The total resources consumed by a function AND everything it called. This finds the expensive architectural flows.
|
||||
2. **Contextualize `flat`:** Resources consumed by the function itself only. If a runtime function (GC, malloc, syscall) has high flat time, trace it UP the call chain to find the user-land code that triggered it.
|
||||
3. **Ignore runtime noise:** Scheduler overhead (`runtime.mcall`, `runtime.systemstack`, GC workers) will always appear. Note if GC pressure is high, but don't try to "fix" the scheduler.
|
||||
4. **Separate benchmark artifacts from production cost:** Test harness allocations (e.g., `httptest.NewRequest`, `ResponseRecorder`) inflate heap profiles but don't exist in production.
|
||||
|
||||
**Output:** Structured analysis document in `docs/research_logs/{component}-perf-analysis.md`.
|
||||
|
||||
### Step 3: Prioritize
|
||||
|
||||
Rank fixes by **impact/risk ratio**:
|
||||
|
||||
| Priority | Criteria |
|
||||
|---|---|
|
||||
| Do first | Low risk, high impact (caching, pre-allocation, fast-reject) |
|
||||
| Do second | Medium risk, high impact (library swap, algorithm change) |
|
||||
| Do last | High risk, high impact (major refactor, custom implementation) |
|
||||
| Skip | Any risk, low impact (micro-optimization below noise floor) |
|
||||
|
||||
**Rule:** If a fix requires more than 1 day AND saves < 20% on the hot path, defer it.
|
||||
|
||||
### Step 4: Optimize
|
||||
|
||||
Implement one fix at a time. For each fix:
|
||||
1. Write tests FIRST (TDD — Red → Green → Refactor)
|
||||
2. Implement the fix
|
||||
3. Run all existing tests to verify no regression
|
||||
4. Benchmark immediately
|
||||
|
||||
**Never batch multiple optimizations into one commit.** Each fix must be independently verifiable and revertable.
|
||||
|
||||
### Step 5: Benchmark
|
||||
|
||||
Compare before/after with the **exact same benchmark configuration** (same `-benchtime`, same `-count`, same machine load). Report:
|
||||
- `ns/op` (latency)
|
||||
- `B/op` (memory per operation)
|
||||
- `allocs/op` (heap allocations per operation)
|
||||
|
||||
### Step 6: When to Stop
|
||||
|
||||
**Stop optimizing when any of these are true:**
|
||||
- Remaining CPU is in hardware-optimized assembly (AES-NI, P-256, SIMD) — you cannot beat the hardware
|
||||
- Remaining allocations are from the language runtime itself (GC, goroutine stacks, HTTP server internals)
|
||||
- The fix requires a custom implementation of a well-audited library — the security/maintenance risk outweighs the perf gain
|
||||
- The measured improvement is < 5% and within benchmark noise
|
||||
|
||||
---
|
||||
|
||||
## Optimization Pattern Catalog
|
||||
|
||||
These are generic, language-agnostic patterns. Apply them when the profiling data shows the corresponding symptom.
|
||||
|
||||
### Pattern: Result Caching
|
||||
|
||||
**Symptom:** Same expensive computation repeated with identical inputs (crypto verification, JSON parsing, regex compilation).
|
||||
|
||||
**Fix:** Cache results keyed by input hash. Use bounded LRU with TTL to prevent memory exhaustion.
|
||||
|
||||
**Safety invariant:** When caching security-sensitive results (auth tokens, permission checks):
|
||||
- ALWAYS re-validate expiry/revocation on cache hit
|
||||
- ALWAYS bound cache size (DoS protection)
|
||||
- ALWAYS set TTL shorter than the security credential's validity period
|
||||
|
||||
### Pattern: Pre-allocation
|
||||
|
||||
**Symptom:** High `allocs/op` from repeatedly constructing the same objects (option structs, config slices, header maps).
|
||||
|
||||
**Fix:** Build the object once at init time, share it read-only across requests. Safe for concurrent use if the object is immutable after construction.
|
||||
|
||||
### Pattern: Fast-Reject / Short-Circuit
|
||||
|
||||
**Symptom:** Expensive validation path runs even for clearly invalid inputs.
|
||||
|
||||
**Fix:** Add a cheap structural pre-check before the expensive path. Examples: check string length before regex, count delimiters before parsing, check content-type before deserialization.
|
||||
|
||||
### Pattern: Library Swap
|
||||
|
||||
**Symptom:** High allocation count or CPU in a third-party library's internal parsing/serialization.
|
||||
|
||||
**Fix:** Replace with a library that uses lower-allocation strategies (manual scanners vs `encoding/json.Decoder`, zero-copy parsing, arena allocation).
|
||||
|
||||
**Safety invariant:** When swapping security-critical libraries (JWT, TLS, crypto):
|
||||
- Explicitly restrict accepted algorithms (prevent algorithm confusion attacks)
|
||||
- Verify the replacement library is well-audited and actively maintained
|
||||
- Run the full existing test suite — no behavioral change allowed
|
||||
|
||||
### Pattern: Pooling
|
||||
|
||||
**Symptom:** High GC pressure from many short-lived objects of the same type being allocated and discarded rapidly.
|
||||
|
||||
**Fix:** Use an object pool (sync.Pool in Go, object pool in Java, arena in Rust) to reuse allocations.
|
||||
|
||||
**Caveat:** Only effective when objects are uniform in size and have a clear acquire/release lifecycle. Misuse creates subtle bugs.
|
||||
|
||||
### Pattern: Batching
|
||||
|
||||
**Symptom:** Many small I/O operations (DB queries, HTTP calls, file writes) dominating wall-clock time.
|
||||
|
||||
**Fix:** Batch operations into fewer, larger calls. Examples: batch INSERT, pipeline Redis commands, buffer writes.
|
||||
|
||||
### Pattern: Artifact Partitioning by Change Frequency
|
||||
|
||||
**Symptom:** Deploying a small change invalidates a large cached artifact (JS bundle, Docker image, compiled binary), forcing consumers to re-download/rebuild the entire thing.
|
||||
|
||||
**Fix:** Partition build artifacts by change frequency so that stable layers survive volatile deploys:
|
||||
- **Stable layer**: dependencies, vendor libraries, base images — changes rarely
|
||||
- **Volatile layer**: application code — changes on every deploy
|
||||
|
||||
**Examples across stacks:**
|
||||
- **JS/Bundler**: Vite `manualChunks` / Webpack `splitChunks` to isolate vendor libraries into separate chunks
|
||||
- **Docker**: multi-stage builds with `COPY go.mod` + `RUN go mod download` BEFORE `COPY . .` — dependency layer caches across builds
|
||||
- **Monorepo**: separate packages by change frequency so CI only rebuilds what changed
|
||||
|
||||
**Safety invariant:** Total artifact size stays the same or slightly increases (chunk overhead). The benefit is on **repeat consumption** — stable layers serve from cache.
|
||||
|
||||
**When NOT to apply:** One-shot artifacts with no caching benefit (single-use CI, ephemeral environments).
|
||||
|
||||
### Pattern: Dependency Discovery Parallelization
|
||||
|
||||
**Symptom:** Sequential resource discovery creates waterfalls — each resource is discovered only after the previous one completes (download → parse → discover next → download → ...).
|
||||
|
||||
**Fix:** Declare dependencies as early as possible so the system can fetch them in parallel:
|
||||
- Move resource declarations upstream (earlier in the boot/parse sequence)
|
||||
- Use explicit hints to bypass sequential discovery chains
|
||||
|
||||
**Examples across stacks:**
|
||||
- **Browser**: `<link rel="preconnect">` to establish connections before CSS/JS requests them; move CSS `@import` to HTML `<link>` for parallel discovery
|
||||
- **Go**: `go mod download` before build to prefetch modules
|
||||
- **DB**: connection pool warm-up at startup instead of on first query
|
||||
- **DNS**: `dns-prefetch` hints for domains the app will contact
|
||||
|
||||
**Safety invariant:** Only pre-declare resources you WILL use. Unused preconnects/prefetches waste resources (TCP connections, DNS queries, module downloads).
|
||||
|
||||
### Pattern: Concurrent-Fetch Dedup
|
||||
|
||||
**Symptom:** Network tab shows two identical API calls fired at the same time. Multiple UI components mount simultaneously and each independently calls the same fetch function.
|
||||
|
||||
**Fix:** Add a loading-state guard (semaphore) at the store/service layer:
|
||||
|
||||
```
|
||||
async function fetchData() {
|
||||
if (isLoading) return // ← drop duplicate in-flight request
|
||||
isLoading = true
|
||||
try { data = await api.getData() }
|
||||
finally { isLoading = false }
|
||||
}
|
||||
```
|
||||
|
||||
**When to apply:** When the same data store is used by multiple co-mounted components (e.g., a navigation bar and a page view both calling `fetchProfile()` on mount).
|
||||
|
||||
**Caveat:** This is a simple semaphore, not request dedup. If the data needs refreshing after the in-flight call completes, the caller should retry. For advanced use cases, consider a proper request dedup cache (e.g., TanStack Query's `staleTime`).
|
||||
|
||||
---
|
||||
|
||||
## Anti-Patterns (Things NOT to Do)
|
||||
|
||||
1. **Don't optimize runtime internals.** If `runtime.mallocgc` or `runtime.gcBgMarkWorker` is high, fix the USER CODE that triggers allocations — don't try to tune the GC directly.
|
||||
2. **Don't replace battle-tested crypto with custom implementations.** The performance ceiling of ECDSA/RSA is in the math. Accept it.
|
||||
3. **Don't optimize based on gut feeling.** Always profile first. Premature optimization is the root of all evil.
|
||||
4. **Don't combine multiple optimizations into one commit.** If a combined commit causes a regression, you can't isolate which fix is at fault.
|
||||
5. **Don't disable security features for performance.** Algorithm restriction, input validation, and expiry checks are non-negotiable.
|
||||
6. **Don't profile without a stable baseline.** Run benchmarks with fixed parameters (`-benchtime`, `-count`, same machine load). Without a reproducible baseline, before/after comparisons are meaningless noise.
|
||||
|
||||
---
|
||||
|
||||
## Language Modules
|
||||
|
||||
Load the relevant language module when working with a specific runtime:
|
||||
|
||||
| Module | Use when |
|
||||
|---|---|
|
||||
| [Go](languages/go.md) | Go services, APIs, CLI tools |
|
||||
| [Rust](languages/rust.md) | Rust binaries, libraries |
|
||||
| [Python](languages/python.md) | Python services, CLI, data pipelines |
|
||||
| [Frontend](languages/frontend.md) | Web frontends (JS/TS bundle, rendering, network) |
|
||||
|
||||
> **Contributing:** After completing a perf optimization session, extract generalizable patterns
|
||||
> from your `docs/research_logs/` findings into this catalog. Project-specific details stay in
|
||||
> the research log; reusable patterns belong here.
|
||||
|
||||
## Profiling Scripts
|
||||
|
||||
Language-specific data extraction scripts live in `scripts/`:
|
||||
|
||||
| Script | Purpose |
|
||||
|---|---|
|
||||
| [go-pprof.sh](scripts/go-pprof.sh) | Extract Go pprof CPU/heap profiles into agent-readable markdown |
|
||||
| [frontend-lighthouse.sh](scripts/frontend-lighthouse.sh) | Two modes: `lighthouse` (Core Web Vitals, needs Chrome) or `bundle` (Vite chunk analysis, always works) |
|
||||
@@ -0,0 +1,347 @@
|
||||
# Frontend Performance Profiling — Vue 3 + Vite + PWA
|
||||
|
||||
## Stack Context
|
||||
|
||||
| Layer | Tool | Version |
|
||||
|---|---|---|
|
||||
| Framework | Vue 3 (SFC + Composition API) | ^3.5 |
|
||||
| State | Pinia | ^3.0 |
|
||||
| Router | Vue Router | ^5.0 |
|
||||
| Bundler | Vite | ^7.3 |
|
||||
| CSS | Tailwind CSS (Vite plugin) | ^4.2 |
|
||||
| PWA | vite-plugin-pwa (Workbox) | ^1.2 |
|
||||
| Backend client | Supabase JS | ^2.99 |
|
||||
| Build | vue-tsc + vite build | — |
|
||||
| Tests | Vitest + jsdom | ^4.1 |
|
||||
|
||||
---
|
||||
|
||||
## Profiling Toolchain
|
||||
|
||||
### Build-Time Analysis
|
||||
|
||||
| Tool | Purpose | Command |
|
||||
|---|---|---|
|
||||
| Vite bundle visualizer | Chunk sizes, tree-shaking effectiveness | `npx vite-bundle-visualizer` (generates `stats.html`) |
|
||||
| `vite build --report` | Build timing and chunk summary | `npx vite build 2>&1` |
|
||||
| `source-map-explorer` | Trace bytes to source files | `npx source-map-explorer dist/assets/*.js` |
|
||||
| `bundlephobia` | Check dependency size before adding | `npx bundlephobia <package-name>` |
|
||||
|
||||
**Quick bundle size check (no extra dependency needed):**
|
||||
|
||||
```bash
|
||||
# Build and report chunk sizes
|
||||
cd apps/frontend && npx vite build 2>&1 | grep -E "(dist/|\.js|\.css)" | sort -t'│' -k2 -rn
|
||||
```
|
||||
|
||||
**Detailed analysis with rollup-plugin-visualizer:**
|
||||
|
||||
```bash
|
||||
# Install temporarily and run
|
||||
cd apps/frontend && npx vite-bundle-visualizer
|
||||
# Opens stats.html in browser — shows treemap of every module
|
||||
```
|
||||
|
||||
### Runtime Analysis (Chrome DevTools)
|
||||
|
||||
| Tab | What it measures | When to use |
|
||||
|---|---|---|
|
||||
| **Performance** | Frame rate, scripting, rendering, painting | Janky animations, slow interactions |
|
||||
| **Lighthouse** | LCP, FID/INP, CLS, TTI, bundle size | Overall page health score |
|
||||
| **Network** | Waterfall, asset sizes, cache status | Slow loads, unnecessary requests |
|
||||
| **Memory** | Heap snapshots, allocation timeline | Memory leaks, growing detached DOM |
|
||||
| **Coverage** | Used vs unused JS/CSS bytes | Dead code, over-imported libraries |
|
||||
|
||||
### Core Web Vitals
|
||||
|
||||
| Metric | Target | What it measures |
|
||||
|---|---|---|
|
||||
| **LCP** (Largest Contentful Paint) | < 2.5s | How fast the main content appears |
|
||||
| **INP** (Interaction to Next Paint) | < 200ms | How fast interactions respond |
|
||||
| **CLS** (Cumulative Layout Shift) | < 0.1 | How much the layout shifts during load |
|
||||
| **TTFB** (Time to First Byte) | < 800ms | Server response time |
|
||||
| **FCP** (First Contentful Paint) | < 1.8s | How fast anything renders |
|
||||
|
||||
### Lighthouse CLI (Headless — for Scripted Analysis)
|
||||
|
||||
```bash
|
||||
# Run Lighthouse from CLI (no browser GUI needed)
|
||||
npx lighthouse http://localhost:4173 \
|
||||
--output=json \
|
||||
--output-path=./lighthouse-report.json \
|
||||
--chrome-flags="--headless --no-sandbox" \
|
||||
--only-categories=performance \
|
||||
--preset=desktop
|
||||
|
||||
# For mobile (default):
|
||||
npx lighthouse http://localhost:4173 \
|
||||
--output=json \
|
||||
--output-path=./lighthouse-report.json \
|
||||
--chrome-flags="--headless --no-sandbox" \
|
||||
--only-categories=performance
|
||||
```
|
||||
|
||||
**Agent-readable extraction:**
|
||||
|
||||
```bash
|
||||
# Extract key metrics from Lighthouse JSON
|
||||
cat lighthouse-report.json | node -e "
|
||||
const r = JSON.parse(require('fs').readFileSync('/dev/stdin','utf8'));
|
||||
const a = r.audits;
|
||||
console.log('| Metric | Score | Value |');
|
||||
console.log('|---|---|---|');
|
||||
['first-contentful-paint','largest-contentful-paint','total-blocking-time',
|
||||
'cumulative-layout-shift','speed-index','interactive'].forEach(k => {
|
||||
if(a[k]) console.log('| ' + a[k].title + ' | ' + (a[k].score*100) + ' | ' + a[k].displayValue + ' |');
|
||||
});
|
||||
"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Vue/Vite-Specific Optimization Patterns
|
||||
|
||||
### Pattern: Vendor Chunk Splitting (Vite)
|
||||
|
||||
**Symptom:** Every deploy invalidates the entire JS bundle cache, including stable vendor libs (Vue, Supabase JS). See agnostic principle: *Artifact Partitioning by Change Frequency* in SKILL.md.
|
||||
|
||||
**Fix:** Add `manualChunks` to `vite.config.ts`:
|
||||
|
||||
```typescript
|
||||
build: {
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks: {
|
||||
'vendor-vue': ['vue', 'vue-router', 'pinia'],
|
||||
'vendor-supabase': ['@supabase/supabase-js'],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
```
|
||||
|
||||
**Group by change frequency:**
|
||||
- `vendor-vue`: framework runtime — changes only on major upgrades
|
||||
- `vendor-supabase`: SDK/client — changes infrequently
|
||||
- App code (auto): changes every deploy
|
||||
|
||||
**Verification:** After `vite build`, check `dist/assets/` for `vendor-vue-*.js` and `vendor-supabase-*.js` chunks.
|
||||
|
||||
### Pattern: Resource Hint Pipeline (HTML)
|
||||
|
||||
**Symptom:** Fonts load late because browser discovers them only after parsing CSS. See agnostic principle: *Dependency Discovery Parallelization* in SKILL.md.
|
||||
|
||||
**Fix:** Replace CSS `@import` with HTML `<link>` tags and add `preconnect` hints:
|
||||
|
||||
```html
|
||||
<!-- In index.html <head> — BEFORE the CSS link -->
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin>
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=...&display=swap">
|
||||
```
|
||||
|
||||
Then remove the `@import url(...)` from your CSS file.
|
||||
|
||||
**Safety:** Only `preconnect` to domains used on every page load. For runtime-determined domains (e.g., Supabase URL from env vars), skip the hint — hardcoding would break across environments.
|
||||
|
||||
### Pattern: Route-Level Code Splitting
|
||||
|
||||
**Symptom:** Lighthouse reports a large initial JS bundle with low Coverage score. All routes are in a single chunk.
|
||||
|
||||
**Fix:** Use dynamic imports for route components in Vue Router:
|
||||
|
||||
```typescript
|
||||
// ❌ Eager import — everything in one chunk
|
||||
import SiraatView from '@/views/SiraatView.vue'
|
||||
|
||||
// ✅ Lazy route — separate chunk, loaded on navigation
|
||||
const SiraatView = () => import('@/views/SiraatView.vue')
|
||||
```
|
||||
|
||||
**Verification:** After build, check `dist/assets/` — each lazy route should produce a separate `.js` chunk.
|
||||
|
||||
### Pattern: Component Lazy Loading
|
||||
|
||||
**Symptom:** Heavy components (charts, editors, modals) are loaded even when not visible.
|
||||
|
||||
**Fix:** Use `defineAsyncComponent` for below-the-fold or conditionally-rendered components:
|
||||
|
||||
```typescript
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
|
||||
const HeavyChart = defineAsyncComponent(() =>
|
||||
import('@/features/analytics/HeavyChart.vue')
|
||||
)
|
||||
```
|
||||
|
||||
### Pattern: Pinia Store Hydration
|
||||
|
||||
**Symptom:** Multiple API calls for the same data on page load. Network waterfall shows redundant fetches.
|
||||
|
||||
**Fix:** Deduplicate fetches in Pinia stores with loading-state guards:
|
||||
|
||||
```typescript
|
||||
const store = defineStore('lesson', () => {
|
||||
const data = ref<Lesson | null>(null)
|
||||
const loading = ref(false)
|
||||
|
||||
async function fetch(id: string) {
|
||||
if (loading.value) return // ← prevent double-fetch
|
||||
if (data.value?.id === id) return // ← already have it
|
||||
loading.value = true
|
||||
try {
|
||||
data.value = await api.getLesson(id)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
return { data, loading, fetch }
|
||||
})
|
||||
```
|
||||
|
||||
### Pattern: Image Optimization
|
||||
|
||||
**Symptom:** Lighthouse flags large images (LCP degradation). Network shows uncompressed PNGs.
|
||||
|
||||
**Fix checklist:**
|
||||
1. Use `<img loading="lazy">` for below-the-fold images
|
||||
2. Use WebP/AVIF formats (Vite handles this with plugins)
|
||||
3. Specify explicit `width` and `height` attributes (prevents CLS)
|
||||
4. Use `<picture>` with `srcset` for responsive images
|
||||
|
||||
### Pattern: Watchers / Computed Overuse
|
||||
|
||||
**Symptom:** DevTools Performance tab shows repeated "Scripting" blocks during reactivity updates.
|
||||
|
||||
**Fix:**
|
||||
- Prefer `computed` over `watch` — computed values are lazy and cached
|
||||
- Use `watchEffect` only when side effects are truly needed
|
||||
- Use `shallowRef` / `shallowReactive` for large objects that don't need deep reactivity
|
||||
|
||||
```typescript
|
||||
// ❌ Deep reactive on a 1000-item array — Vue tracks every nested property
|
||||
const items = ref<Item[]>([])
|
||||
|
||||
// ✅ Shallow — Vue only tracks the array reference, not individual items
|
||||
const items = shallowRef<Item[]>([])
|
||||
```
|
||||
|
||||
### Pattern: Virtual Scrolling for Long Lists
|
||||
|
||||
**Symptom:** Rendering > 100 DOM nodes causes jank. DevTools shows long "Rendering" and "Painting" phases.
|
||||
|
||||
**Fix:** Use virtual scrolling (only render visible items):
|
||||
|
||||
```bash
|
||||
npm install @tanstack/vue-virtual
|
||||
```
|
||||
|
||||
### Pattern: PWA Cache Strategy Audit
|
||||
|
||||
**Symptom:** Repeat visits are slow despite service worker. Network tab shows cache misses.
|
||||
|
||||
**Audit checklist:**
|
||||
1. Verify `navigator.serviceWorker.controller` is active (DevTools → Application → Service Workers)
|
||||
2. Check `Cache Storage` in DevTools — are the expected caches populated?
|
||||
3. Verify `runtimeCaching` patterns in `vite.config.ts` match actual API URL patterns
|
||||
4. Check `networkTimeoutSeconds` — too high means slow fallback to cache
|
||||
|
||||
### Pattern: Tailwind CSS Purging
|
||||
|
||||
**Symptom:** Large CSS bundle despite using Tailwind. Build output shows > 50KB CSS.
|
||||
|
||||
**Fix:** Tailwind v4 (your version) uses automatic content detection. Verify:
|
||||
1. No `@import` of the full Tailwind CSS build in production
|
||||
2. The `@tailwindcss/vite` plugin is used (it is — confirmed in your config)
|
||||
3. No dynamic class names that prevent tree-shaking (e.g., `class={`text-${color}-500`}`)
|
||||
|
||||
---
|
||||
|
||||
## Vue/Vite-Specific Anti-Patterns
|
||||
|
||||
1. **Don't eagerly import all routes.** This defeats Vite's code splitting. Always use dynamic `() => import()` for route components.
|
||||
2. **Don't use `v-if` + `v-for` on the same element.** `v-if` has higher priority in Vue 3 — use a `<template>` wrapper.
|
||||
3. **Don't deep-watch large arrays.** Use `shallowRef` or `watch(() => arr.length)` to avoid O(n) reactivity tracking.
|
||||
4. **Don't ignore `key` in `v-for`.** Missing keys force Vue to re-render entire lists instead of patching individual items.
|
||||
5. **Don't inline large SVGs when they could be `<img>` references.** Inline SVGs increase HTML parse time and bundle size.
|
||||
6. **Don't load Supabase JS in every component.** Import the singleton client from `supabaseClient.ts` — avoid creating multiple GoTrue instances.
|
||||
7. **Don't use plain functions for reactive template expressions.** If `v-if="isVisible()"` calls a function, Vue re-executes it on every render cycle. Use `computed` instead — Vue caches the result and only re-runs when dependencies change.
|
||||
|
||||
---
|
||||
|
||||
## Irreducible Floors in Vue/Vite/Browser
|
||||
|
||||
| Cost | What it is | Why it's irreducible |
|
||||
|---|---|---|
|
||||
| Vue runtime | Reactivity system, virtual DOM diffing | Framework overhead — ~30KB min gzipped |
|
||||
| Supabase JS | GoTrue + PostgREST client | Auth + API client — ~40KB min gzipped |
|
||||
| TLS handshake | HTTPS connection setup | Network latency — mitigated by HTTP/2, preconnect |
|
||||
| Font loading | WOFF2 download + layout shift | Use `font-display: swap` and preload critical fonts |
|
||||
| Service Worker registration | SW parse + install on first visit | One-time cost per SW update |
|
||||
| DNS resolution | Supabase API domain lookup | Use `<link rel="dns-prefetch">` |
|
||||
|
||||
---
|
||||
|
||||
## Data Extraction Scripts
|
||||
|
||||
Use the scripts in `scripts/` for automated profiling:
|
||||
|
||||
```bash
|
||||
# Always-works bundle analysis (no Chrome needed) — start here
|
||||
scripts/frontend-lighthouse.sh bundle ./apps/frontend
|
||||
|
||||
# Lighthouse Core Web Vitals (needs Chrome/Chromium)
|
||||
scripts/frontend-lighthouse.sh lighthouse http://localhost:4173 desktop
|
||||
|
||||
# With custom timeout (default: 120s)
|
||||
LIGHTHOUSE_TIMEOUT=60 scripts/frontend-lighthouse.sh lighthouse http://localhost:4173
|
||||
```
|
||||
|
||||
> **Agent tip:** If Lighthouse hangs (common in headless/CI environments), the script
|
||||
> auto-kills after the timeout and suggests bundle mode. Always start with `bundle` mode
|
||||
> since it works everywhere and provides the most actionable data (chunk sizes, vendor
|
||||
> splitting status, oversized chunk flags).
|
||||
|
||||
## Recommended Profiling Workflow
|
||||
|
||||
1. **Bundle analysis first:** Run `scripts/frontend-lighthouse.sh bundle ./apps/frontend` to check chunk sizes
|
||||
2. **Lighthouse desktop + mobile:** Run `scripts/frontend-lighthouse.sh lighthouse <url> desktop` and default (mobile) — compare scores
|
||||
3. **Runtime profiling:** Use Chrome DevTools Performance tab to record user flows (navigation, exercise completion)
|
||||
4. **Memory:** Take heap snapshots before and after a user flow — diff for leaks (detached DOM, event listeners)
|
||||
5. **Coverage:** Run Coverage tool — identify dead JS/CSS shipped to the client
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Post-Optimization Clean Sweep Checklist
|
||||
|
||||
After applying the main fixes, run this checklist to catch leftover issues.
|
||||
This was codified from the FATH frontend session (2026-03-23).
|
||||
|
||||
### Template Reactivity
|
||||
- [ ] All `v-if` / `v-show` conditions use `computed` refs, not plain function calls
|
||||
- [ ] `v-for` lists use stable `:key` values (not array index)
|
||||
- [ ] No plain functions in template expressions that could be `computed`
|
||||
|
||||
### Component Loading
|
||||
- [ ] All routes use `() => import()` (lazy routes)
|
||||
- [ ] Modals, drawers, and sheets use `defineAsyncComponent` (they are never visible on initial render)
|
||||
- [ ] Heavy sub-components within lazy routes use `defineAsyncComponent` if they are conditionally rendered
|
||||
|
||||
### Store Health
|
||||
- [ ] Stores used by multiple co-mounted views have a concurrent-fetch guard (`if (isLoading) return`)
|
||||
- [ ] No `deep: true` watchers on large arrays — use `shallowRef` + explicit trigger
|
||||
- [ ] Computed getters are used instead of methods for derived store state
|
||||
|
||||
### Bundle Health
|
||||
- [ ] Vendor libraries are isolated into stable chunks (`manualChunks`)
|
||||
- [ ] CSS `@import` statements for external stylesheets (fonts, etc.) are replaced with HTML `<link>` tags
|
||||
- [ ] `<link rel="preconnect">` hints exist for all statically-known third-party origins
|
||||
- [ ] Supabase JS is imported only once from a singleton module, not in individual components
|
||||
|
||||
### Build Verification
|
||||
- [ ] `npx vite build` — clean output, no warnings
|
||||
- [ ] `npx vitest run` — all tests pass
|
||||
- [ ] `npx vue-tsc -b` — no type errors
|
||||
- [ ] Build output diff — no unexpected chunk size regressions
|
||||
@@ -0,0 +1,133 @@
|
||||
# Go Performance Profiling
|
||||
|
||||
## Toolchain
|
||||
|
||||
| Tool | Purpose | Command |
|
||||
|---|---|---|
|
||||
| `go tool pprof` | CPU / heap / block / mutex profiles | `go tool pprof -text -cum -nodecount=30 profile.pb.gz` |
|
||||
| `go test -bench` | Micro-benchmarks | `go test -bench=BenchmarkX -benchtime=3s -benchmem -count=3` |
|
||||
| `go test -cpuprofile` | Capture CPU profile during benchmarks | `go test -bench=BenchmarkX -cpuprofile=cpu.prof` |
|
||||
| `go test -memprofile` | Capture heap profile during benchmarks | `go test -bench=BenchmarkX -memprofile=mem.prof` |
|
||||
| `go build -gcflags="-m"` | Escape analysis (what allocates on heap) | `go build -gcflags="-m" ./path/to/pkg 2>&1 \| grep "escapes to heap"` |
|
||||
| `go test -trace` | Execution trace (goroutine scheduling) | `go test -bench=BenchmarkX -trace=trace.out` |
|
||||
| `benchstat` | Statistical comparison of benchmarks | `benchstat old.txt new.txt` |
|
||||
|
||||
## Data Extraction Script
|
||||
|
||||
Use `scripts/go-pprof.sh` to extract profiles into agent-readable markdown.
|
||||
|
||||
```bash
|
||||
# CPU profile from a .prof file
|
||||
.agents/skills/perf-optimization/scripts/go-pprof.sh cpu path/to/cpu.prof
|
||||
|
||||
# Heap profile from a live endpoint
|
||||
.agents/skills/perf-optimization/scripts/go-pprof.sh heap http://localhost:6060/debug/pprof/heap
|
||||
|
||||
# CPU profile from a benchmark (generates and analyzes in one step)
|
||||
.agents/skills/perf-optimization/scripts/go-pprof.sh bench ./internal/platform/server/... BenchmarkRequireAuth
|
||||
```
|
||||
|
||||
## Go-Specific Optimization Patterns
|
||||
|
||||
### Escape Analysis
|
||||
|
||||
Before optimizing allocations, run escape analysis to understand WHY values escape to heap:
|
||||
|
||||
```bash
|
||||
go build -gcflags="-m" ./internal/platform/server/ 2>&1 | grep "escapes to heap"
|
||||
```
|
||||
|
||||
Common escape reasons:
|
||||
- Returning a pointer to a local variable
|
||||
- Storing a value in an interface (boxing)
|
||||
- Closure captures
|
||||
- Slice/map growth beyond initial capacity
|
||||
|
||||
### `sync.Pool` for Hot-Path Allocations
|
||||
|
||||
**When to use:** The profiler shows a specific type allocated/freed thousands of times per second on the same hot path.
|
||||
|
||||
**When NOT to use:** Objects that are long-lived, vary wildly in size, or have cleanup requirements.
|
||||
|
||||
```go
|
||||
var bufPool = sync.Pool{
|
||||
New: func() interface{} { return new(bytes.Buffer) },
|
||||
}
|
||||
|
||||
func process() {
|
||||
buf := bufPool.Get().(*bytes.Buffer)
|
||||
defer func() { buf.Reset(); bufPool.Put(buf) }()
|
||||
// use buf...
|
||||
}
|
||||
```
|
||||
|
||||
### Pre-sized Slices and Maps
|
||||
|
||||
**Symptom:** `runtime.growslice` or `runtime.mapassign` in the profile — the runtime is repeatedly growing backing arrays.
|
||||
|
||||
```go
|
||||
// ❌ Grows 3 times: cap 0 → 1 → 2 → 4
|
||||
items := []Item{}
|
||||
for _, raw := range data { items = append(items, parse(raw)) }
|
||||
|
||||
// ✅ One allocation
|
||||
items := make([]Item, 0, len(data))
|
||||
for _, raw := range data { items = append(items, parse(raw)) }
|
||||
```
|
||||
|
||||
### `strings.Builder` Over `fmt.Sprintf` in Loops
|
||||
|
||||
**Symptom:** `fmt.Sprintf` or `runtime.convTstring` in allocation-heavy loops.
|
||||
|
||||
```go
|
||||
var b strings.Builder
|
||||
b.Grow(estimatedLen)
|
||||
for _, part := range parts { b.WriteString(part) }
|
||||
result := b.String()
|
||||
```
|
||||
|
||||
### Benchmark Best Practices
|
||||
|
||||
```go
|
||||
func BenchmarkX(b *testing.B) {
|
||||
// Setup OUTSIDE the loop — don't measure init cost
|
||||
fixture := buildFixture(b)
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
result := doWork(fixture)
|
||||
// Prevent compiler from eliminating the call
|
||||
_ = result
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Statistical validity:** Always run with `-count=5` minimum and compare with `benchstat`:
|
||||
|
||||
```bash
|
||||
go test -bench=BenchmarkX -benchmem -count=5 > old.txt
|
||||
# ... make changes ...
|
||||
go test -bench=BenchmarkX -benchmem -count=5 > new.txt
|
||||
benchstat old.txt new.txt
|
||||
```
|
||||
|
||||
## Go-Specific Anti-Patterns
|
||||
|
||||
1. **Don't use `sync.Pool` for everything.** It's only effective for truly hot paths with uniform objects. Misuse adds complexity without measurable gain.
|
||||
2. **Don't fight the GC.** Go's GC is tuned for latency. If throughput matters more, tune `GOGC` before rewriting code.
|
||||
3. **Don't use `unsafe` for performance.** The compiler's escape analysis and inlining are usually sufficient. `unsafe` breaks memory safety guarantees.
|
||||
4. **Don't ignore `-race` when benchmarking.** Always verify correctness with `-race` before measuring performance without it.
|
||||
|
||||
## Irreducible Floors in Go
|
||||
|
||||
These are performance costs you cannot eliminate — recognize them and stop:
|
||||
|
||||
| Function | What it is | Why it's irreducible |
|
||||
|---|---|---|
|
||||
| `p256MulInternal` | P-256 scalar multiplication (ECDSA) | Hardware-optimized assembly |
|
||||
| `runtime.mallocgc` | Go memory allocator | Symptom, not cause — fix the caller |
|
||||
| `runtime.gcBgMarkWorker` | GC mark phase | Proportional to allocation rate — fix allocs |
|
||||
| `runtime.futex` / `runtime.usleep` | OS synchronization | Scheduling overhead |
|
||||
| `syscall.Syscall` | Kernel calls (I/O) | Irreducible for actual I/O ops |
|
||||
| `crypto/aes` | AES encryption | Hardware AES-NI, cannot improve |
|
||||
@@ -0,0 +1,12 @@
|
||||
# Python Performance Profiling
|
||||
|
||||
> **Stub:** This module will be populated when Python profiling is needed in a project.
|
||||
> Follow the pattern established in [go.md](go.md).
|
||||
|
||||
## Expected Content
|
||||
|
||||
- Toolchain: `py-spy`, `cProfile`, `memory_profiler`, `line_profiler`, `scalene`
|
||||
- Patterns: `__slots__`, generator expressions, `collections.deque`, `numpy` vectorization
|
||||
- Benchmarking: `timeit`, `pytest-benchmark`
|
||||
- Irreducible floors: GIL contention, interpreter overhead, C extension boundaries
|
||||
- Script: `scripts/python-pyspy.sh`
|
||||
@@ -0,0 +1,12 @@
|
||||
# Rust Performance Profiling
|
||||
|
||||
> **Stub:** This module will be populated when Rust profiling is needed in a project.
|
||||
> Follow the pattern established in [go.md](go.md).
|
||||
|
||||
## Expected Content
|
||||
|
||||
- Toolchain: `perf`, `flamegraph`, `criterion`, `cargo-flamegraph`, `dhat`
|
||||
- Patterns: Arena allocation, `#[inline]`, zero-copy parsing, `Box` vs stack
|
||||
- Benchmarking: `criterion` setup, statistical comparison
|
||||
- Irreducible floors: syscall overhead, TLS handshake, memory-mapped I/O
|
||||
- Script: `scripts/rust-flamegraph.sh`
|
||||
@@ -0,0 +1,374 @@
|
||||
#!/usr/bin/env bash
|
||||
# frontend-lighthouse.sh — Robust frontend performance profiling for AI agents.
|
||||
#
|
||||
# Two modes:
|
||||
# 1. `lighthouse` — Run headless Lighthouse with timeout and binary discovery
|
||||
# 2. `bundle` — Analyze Vite build output (always works, no Chrome needed)
|
||||
#
|
||||
# Usage:
|
||||
# frontend-lighthouse.sh lighthouse <url> [preset]
|
||||
# frontend-lighthouse.sh bundle <frontend-dir>
|
||||
#
|
||||
# Arguments:
|
||||
# lighthouse mode:
|
||||
# url — The URL to audit (e.g., http://localhost:4173)
|
||||
# preset — "desktop" or "mobile" (default: mobile)
|
||||
#
|
||||
# bundle mode:
|
||||
# frontend-dir — Path to the frontend project root (must have vite.config.*)
|
||||
#
|
||||
# Examples:
|
||||
# frontend-lighthouse.sh lighthouse http://localhost:4173 desktop
|
||||
# frontend-lighthouse.sh bundle ./apps/frontend
|
||||
# frontend-lighthouse.sh bundle /home/user/project/apps/frontend
|
||||
#
|
||||
# Prerequisites:
|
||||
# lighthouse mode: Node.js + Chrome/Chromium (or globally installed lighthouse)
|
||||
# bundle mode: Node.js + Vite project (no Chrome needed)
|
||||
#
|
||||
# Robustness features (learned from real-world agent sessions):
|
||||
# - Binary discovery: checks PATH, npx, ~/.npm-global/bin, common install locations
|
||||
# - Timeout: kills Lighthouse after LIGHTHOUSE_TIMEOUT seconds (default: 120)
|
||||
# - Fallback: if Lighthouse fails/hangs, suggests running bundle mode instead
|
||||
# - Error reporting disabled: prevents interactive consent prompts
|
||||
# - Bundle mode: always works, extracts chunk sizes from vite build output
|
||||
#
|
||||
# Output: Structured markdown tables suitable for LLM consumption.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
MODE="${1:-}"
|
||||
LIGHTHOUSE_TIMEOUT="${LIGHTHOUSE_TIMEOUT:-120}"
|
||||
|
||||
# ─── Binary Discovery ──────────────────────────────────────────────────────────
|
||||
# Lighthouse can be installed globally via npm, in ~/.npm-global, or via npx.
|
||||
# Try multiple locations to find a working binary.
|
||||
find_lighthouse() {
|
||||
# 1. Check PATH
|
||||
if command -v lighthouse &>/dev/null; then
|
||||
echo "lighthouse"
|
||||
return
|
||||
fi
|
||||
|
||||
# 2. Check common global install locations
|
||||
local candidates=(
|
||||
"$HOME/.npm-global/bin/lighthouse"
|
||||
"/usr/local/bin/lighthouse"
|
||||
"/usr/bin/lighthouse"
|
||||
)
|
||||
for candidate in "${candidates[@]}"; do
|
||||
if [[ -x "$candidate" ]]; then
|
||||
echo "$candidate"
|
||||
return
|
||||
fi
|
||||
done
|
||||
|
||||
# 3. Fall back to npx (will auto-install if needed)
|
||||
echo "npx --yes lighthouse"
|
||||
}
|
||||
|
||||
# ─── Lighthouse Mode ───────────────────────────────────────────────────────────
|
||||
run_lighthouse() {
|
||||
local url="$1"
|
||||
local preset="${2:-mobile}"
|
||||
|
||||
local lh_cmd
|
||||
lh_cmd=$(find_lighthouse)
|
||||
|
||||
local preset_flag=""
|
||||
if [[ "$preset" == "desktop" ]]; then
|
||||
preset_flag="--preset=desktop"
|
||||
fi
|
||||
|
||||
local tmpfile
|
||||
tmpfile=$(mktemp /tmp/lighthouse-XXXXXX.json)
|
||||
trap 'rm -f "$tmpfile"' EXIT
|
||||
|
||||
echo "# Lighthouse Report: ${url} (${preset})"
|
||||
echo ""
|
||||
echo "Binary: ${lh_cmd}"
|
||||
echo "Timeout: ${LIGHTHOUSE_TIMEOUT}s"
|
||||
echo ""
|
||||
|
||||
# Run Lighthouse with timeout to prevent infinite hangs in headless environments.
|
||||
# --enable-error-reporting=false prevents interactive consent prompts.
|
||||
# --no-sandbox is required in containerized/CI environments.
|
||||
local lh_pid
|
||||
local lh_exit=0
|
||||
|
||||
$lh_cmd "$url" \
|
||||
--output=json \
|
||||
--output-path="$tmpfile" \
|
||||
--chrome-flags="--headless --no-sandbox --disable-gpu --disable-dev-shm-usage" \
|
||||
--only-categories=performance \
|
||||
$preset_flag \
|
||||
--enable-error-reporting=false \
|
||||
--quiet 2>/dev/null &
|
||||
lh_pid=$!
|
||||
|
||||
# Wait with timeout — kill if Lighthouse hangs (common in headless/CI)
|
||||
local elapsed=0
|
||||
while kill -0 "$lh_pid" 2>/dev/null; do
|
||||
if (( elapsed >= LIGHTHOUSE_TIMEOUT )); then
|
||||
kill -9 "$lh_pid" 2>/dev/null || true
|
||||
wait "$lh_pid" 2>/dev/null || true
|
||||
echo "⚠️ Lighthouse timed out after ${LIGHTHOUSE_TIMEOUT}s."
|
||||
echo ""
|
||||
echo "This usually means Chrome/Chromium is not available or the headless"
|
||||
echo "environment doesn't support it. Try bundle mode instead:"
|
||||
echo ""
|
||||
echo " $0 bundle <frontend-dir>"
|
||||
echo ""
|
||||
return 1
|
||||
fi
|
||||
sleep 2
|
||||
elapsed=$((elapsed + 2))
|
||||
done
|
||||
|
||||
wait "$lh_pid" || lh_exit=$?
|
||||
|
||||
if [[ $lh_exit -ne 0 ]]; then
|
||||
echo "⚠️ Lighthouse exited with code ${lh_exit}."
|
||||
echo "Try bundle mode instead: $0 bundle <frontend-dir>"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ ! -s "$tmpfile" ]]; then
|
||||
echo "⚠️ Lighthouse produced no output."
|
||||
echo "Try bundle mode instead: $0 bundle <frontend-dir>"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Extract metrics into structured markdown
|
||||
node -e "
|
||||
const fs = require('fs');
|
||||
const r = JSON.parse(fs.readFileSync('$tmpfile', 'utf8'));
|
||||
const a = r.audits;
|
||||
const score = Math.round((r.categories.performance.score || 0) * 100);
|
||||
|
||||
console.log('## Performance Score: ' + score + '/100');
|
||||
console.log('');
|
||||
console.log('## Core Web Vitals');
|
||||
console.log('');
|
||||
console.log('| Metric | Value | Score |');
|
||||
console.log('|---|---|---|');
|
||||
|
||||
const vitals = [
|
||||
'first-contentful-paint',
|
||||
'largest-contentful-paint',
|
||||
'total-blocking-time',
|
||||
'cumulative-layout-shift',
|
||||
'speed-index',
|
||||
'interactive',
|
||||
];
|
||||
|
||||
vitals.forEach(k => {
|
||||
if (a[k]) {
|
||||
const s = a[k].score !== null ? Math.round(a[k].score * 100) : 'N/A';
|
||||
console.log('| ' + a[k].title + ' | ' + (a[k].displayValue || 'N/A') + ' | ' + s + ' |');
|
||||
}
|
||||
});
|
||||
|
||||
console.log('');
|
||||
console.log('## Opportunities');
|
||||
console.log('');
|
||||
|
||||
const opportunities = Object.values(a)
|
||||
.filter(audit => audit.details && audit.details.type === 'opportunity' && audit.details.overallSavingsMs > 0)
|
||||
.sort((x, y) => (y.details.overallSavingsMs || 0) - (x.details.overallSavingsMs || 0));
|
||||
|
||||
if (opportunities.length > 0) {
|
||||
console.log('| Opportunity | Potential Savings |');
|
||||
console.log('|---|---|');
|
||||
opportunities.forEach(o => {
|
||||
console.log('| ' + o.title + ' | ' + Math.round(o.details.overallSavingsMs) + ' ms |');
|
||||
});
|
||||
} else {
|
||||
console.log('No significant opportunities found.');
|
||||
}
|
||||
|
||||
console.log('');
|
||||
console.log('## Diagnostics');
|
||||
console.log('');
|
||||
|
||||
const diagnostics = Object.values(a)
|
||||
.filter(audit => audit.details && audit.details.type === 'table' && audit.score !== null && audit.score < 0.9)
|
||||
.slice(0, 10);
|
||||
|
||||
if (diagnostics.length > 0) {
|
||||
console.log('| Diagnostic | Score |');
|
||||
console.log('|---|---|');
|
||||
diagnostics.forEach(d => {
|
||||
console.log('| ' + d.title + ' | ' + Math.round(d.score * 100) + ' |');
|
||||
});
|
||||
} else {
|
||||
console.log('All diagnostics passed.');
|
||||
}
|
||||
"
|
||||
}
|
||||
|
||||
# ─── Bundle Analysis Mode ──────────────────────────────────────────────────────
|
||||
# Always works — no Chrome needed. Extracts chunk sizes from vite build output.
|
||||
# This was the most useful data source during the FATH frontend session: it
|
||||
# revealed the 297KB monolithic index chunk and guided all 6 fixes.
|
||||
run_bundle() {
|
||||
local frontend_dir="$1"
|
||||
|
||||
if [[ ! -d "$frontend_dir" ]]; then
|
||||
echo "Error: directory '$frontend_dir' does not exist."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Verify it's a Vite project
|
||||
if ! ls "$frontend_dir"/vite.config.* &>/dev/null; then
|
||||
echo "Error: no vite.config.* found in '$frontend_dir'. Is this a Vite project?"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "# Bundle Analysis: ${frontend_dir}"
|
||||
echo ""
|
||||
echo "Running \`vite build\`..."
|
||||
echo ""
|
||||
|
||||
local build_output
|
||||
build_output=$(cd "$frontend_dir" && npx vite build 2>&1)
|
||||
local build_exit=$?
|
||||
|
||||
if [[ $build_exit -ne 0 ]]; then
|
||||
echo "⚠️ Build failed (exit code $build_exit):"
|
||||
echo ""
|
||||
echo '```'
|
||||
echo "$build_output"
|
||||
echo '```'
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Parse build output into structured tables
|
||||
node -e "
|
||||
const output = \`$build_output\`;
|
||||
const lines = output.split('\n');
|
||||
|
||||
const jsChunks = [];
|
||||
const cssChunks = [];
|
||||
|
||||
// Parse Vite build output lines like:
|
||||
// dist/assets/index-BExof8y3.js 20.04 kB │ gzip: 6.90 kB
|
||||
for (const line of lines) {
|
||||
const match = line.match(/dist\/assets\/(.+?)\s+([\d.]+)\s*kB\s*│\s*gzip:\s*([\d.]+)\s*kB/);
|
||||
if (match) {
|
||||
const [, name, rawKB, gzipKB] = match;
|
||||
const entry = { name, raw: parseFloat(rawKB), gzip: parseFloat(gzipKB) };
|
||||
if (name.endsWith('.js')) jsChunks.push(entry);
|
||||
else if (name.endsWith('.css')) cssChunks.push(entry);
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by gzip size descending
|
||||
jsChunks.sort((a, b) => b.gzip - a.gzip);
|
||||
cssChunks.sort((a, b) => b.gzip - a.gzip);
|
||||
|
||||
const totalJsRaw = jsChunks.reduce((s, c) => s + c.raw, 0);
|
||||
const totalJsGzip = jsChunks.reduce((s, c) => s + c.gzip, 0);
|
||||
const totalCssRaw = cssChunks.reduce((s, c) => s + c.raw, 0);
|
||||
const totalCssGzip = cssChunks.reduce((s, c) => s + c.gzip, 0);
|
||||
|
||||
console.log('## JS Chunks (sorted by gzip size)');
|
||||
console.log('');
|
||||
console.log('| Chunk | Raw (KB) | Gzip (KB) |');
|
||||
console.log('|---|---|---|');
|
||||
jsChunks.forEach(c => {
|
||||
// Clean chunk name: remove hash suffix for readability
|
||||
const clean = c.name.replace(/-[A-Za-z0-9_-]{8}\\.js$/, '.js');
|
||||
console.log('| ' + clean + ' | ' + c.raw.toFixed(2) + ' | ' + c.gzip.toFixed(2) + ' |');
|
||||
});
|
||||
console.log('| **Total JS** | **' + totalJsRaw.toFixed(2) + '** | **' + totalJsGzip.toFixed(2) + '** |');
|
||||
|
||||
if (cssChunks.length > 0) {
|
||||
console.log('');
|
||||
console.log('## CSS Chunks');
|
||||
console.log('');
|
||||
console.log('| Chunk | Raw (KB) | Gzip (KB) |');
|
||||
console.log('|---|---|---|');
|
||||
cssChunks.forEach(c => {
|
||||
const clean = c.name.replace(/-[A-Za-z0-9_-]{8}\\.css$/, '.css');
|
||||
console.log('| ' + clean + ' | ' + c.raw.toFixed(2) + ' | ' + c.gzip.toFixed(2) + ' |');
|
||||
});
|
||||
console.log('| **Total CSS** | **' + totalCssRaw.toFixed(2) + '** | **' + totalCssGzip.toFixed(2) + '** |');
|
||||
}
|
||||
|
||||
console.log('');
|
||||
console.log('## Summary');
|
||||
console.log('');
|
||||
console.log('| Metric | Value |');
|
||||
console.log('|---|---|');
|
||||
console.log('| JS chunks | ' + jsChunks.length + ' |');
|
||||
console.log('| CSS chunks | ' + cssChunks.length + ' |');
|
||||
console.log('| Total JS (gzip) | ' + totalJsGzip.toFixed(2) + ' KB |');
|
||||
console.log('| Total CSS (gzip) | ' + totalCssGzip.toFixed(2) + ' KB |');
|
||||
console.log('| Largest JS chunk | ' + (jsChunks[0]?.name || 'N/A') + ' (' + (jsChunks[0]?.gzip.toFixed(2) || '0') + ' KB gzip) |');
|
||||
|
||||
// Flag potential issues
|
||||
console.log('');
|
||||
console.log('## Flags');
|
||||
console.log('');
|
||||
const bigChunks = jsChunks.filter(c => c.gzip > 50);
|
||||
if (bigChunks.length > 0) {
|
||||
console.log('⚠️ **Large chunks (>50 KB gzip):**');
|
||||
bigChunks.forEach(c => {
|
||||
console.log(' - ' + c.name + ': ' + c.gzip.toFixed(2) + ' KB gzip');
|
||||
});
|
||||
console.log('');
|
||||
console.log('Consider splitting vendor libraries into separate chunks (see: Artifact Partitioning pattern in SKILL.md).');
|
||||
} else {
|
||||
console.log('✅ No oversized chunks detected.');
|
||||
}
|
||||
|
||||
const vendorChunks = jsChunks.filter(c => c.name.startsWith('vendor-'));
|
||||
if (vendorChunks.length > 0) {
|
||||
console.log('');
|
||||
console.log('✅ Vendor chunk splitting detected: ' + vendorChunks.map(c => c.name).join(', '));
|
||||
} else if (jsChunks.some(c => c.gzip > 40)) {
|
||||
console.log('');
|
||||
console.log('⚠️ No vendor chunk splitting detected. The largest chunk may contain framework + app code bundled together.');
|
||||
}
|
||||
"
|
||||
}
|
||||
|
||||
# ─── Main ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
case "${MODE}" in
|
||||
lighthouse|lh)
|
||||
URL="${2:-}"
|
||||
PRESET="${3:-mobile}"
|
||||
if [[ -z "$URL" ]]; then
|
||||
echo "Usage: $0 lighthouse <url> [desktop|mobile]"
|
||||
echo " $0 bundle <frontend-dir>"
|
||||
exit 1
|
||||
fi
|
||||
run_lighthouse "$URL" "$PRESET"
|
||||
;;
|
||||
bundle|build)
|
||||
DIR="${2:-}"
|
||||
if [[ -z "$DIR" ]]; then
|
||||
echo "Usage: $0 bundle <frontend-dir>"
|
||||
echo " $0 lighthouse <url> [desktop|mobile]"
|
||||
exit 1
|
||||
fi
|
||||
run_bundle "$DIR"
|
||||
;;
|
||||
*)
|
||||
echo "Frontend Performance Profiling Script"
|
||||
echo ""
|
||||
echo "Usage:"
|
||||
echo " $0 lighthouse <url> [desktop|mobile] — Run Lighthouse (needs Chrome)"
|
||||
echo " $0 bundle <frontend-dir> — Analyze Vite build chunks (no Chrome)"
|
||||
echo ""
|
||||
echo "Examples:"
|
||||
echo " $0 lighthouse http://localhost:4173 desktop"
|
||||
echo " $0 bundle ./apps/frontend"
|
||||
echo ""
|
||||
echo "Environment:"
|
||||
echo " LIGHTHOUSE_TIMEOUT=120 — Seconds before killing a hung Lighthouse (default: 120)"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -0,0 +1,112 @@
|
||||
#!/usr/bin/env bash
|
||||
# go-pprof.sh — Extract Go pprof profiles into agent-readable markdown.
|
||||
#
|
||||
# Usage:
|
||||
# go-pprof.sh cpu <profile-path-or-url> [top-n]
|
||||
# go-pprof.sh heap <profile-path-or-url> [top-n]
|
||||
# go-pprof.sh bench <package-path> <bench-regex> [top-n]
|
||||
#
|
||||
# Examples:
|
||||
# go-pprof.sh cpu cpu.prof
|
||||
# go-pprof.sh heap http://localhost:6060/debug/pprof/heap
|
||||
# go-pprof.sh bench ./internal/platform/server/... BenchmarkRequireAuth 40
|
||||
#
|
||||
# Output: Structured markdown suitable for LLM consumption.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
PROFILE_TYPE="${1:-}"
|
||||
SOURCE="${2:-}"
|
||||
TOP_N="${3:-30}"
|
||||
|
||||
usage() {
|
||||
echo "Usage:"
|
||||
echo " $0 cpu <profile-path-or-url> [top-n]"
|
||||
echo " $0 heap <profile-path-or-url> [top-n]"
|
||||
echo " $0 bench <package-path> <bench-regex> [top-n]"
|
||||
exit 1
|
||||
}
|
||||
|
||||
if [[ -z "$PROFILE_TYPE" || -z "$SOURCE" ]]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
extract_pprof() {
|
||||
local profile_file="$1"
|
||||
local label="$2"
|
||||
local top="$3"
|
||||
|
||||
echo ""
|
||||
echo "--- PPROF ${label} PROFILE (Top ${top} by Cumulative) ---"
|
||||
echo ""
|
||||
echo '```'
|
||||
go tool pprof -text -cum -nodecount="$top" "$profile_file" 2>/dev/null \
|
||||
| grep -v "^Showing nodes" \
|
||||
| sed '/^$/d'
|
||||
echo '```'
|
||||
echo ""
|
||||
}
|
||||
|
||||
case "$PROFILE_TYPE" in
|
||||
cpu|heap|block|mutex)
|
||||
extract_pprof "$SOURCE" "$(echo "$PROFILE_TYPE" | tr '[:lower:]' '[:upper:]')" "$TOP_N"
|
||||
;;
|
||||
bench)
|
||||
BENCH_REGEX="${3:-}"
|
||||
TOP_N="${4:-30}"
|
||||
|
||||
if [[ -z "$BENCH_REGEX" ]]; then
|
||||
echo "Error: bench mode requires <package-path> and <bench-regex>"
|
||||
usage
|
||||
fi
|
||||
|
||||
TMPDIR=$(mktemp -d)
|
||||
trap 'rm -rf "$TMPDIR"' EXIT
|
||||
|
||||
echo "# Benchmark Profile: ${BENCH_REGEX}"
|
||||
echo ""
|
||||
|
||||
# Run benchmark and capture results + profiles
|
||||
echo "## Benchmark Results"
|
||||
echo ""
|
||||
echo '```'
|
||||
go test "$SOURCE" \
|
||||
-bench="$BENCH_REGEX" \
|
||||
-benchtime=2s \
|
||||
-benchmem \
|
||||
-count=1 \
|
||||
-run='^$' \
|
||||
-cpuprofile="$TMPDIR/cpu.prof" \
|
||||
-memprofile="$TMPDIR/mem.prof" \
|
||||
-timeout=120s 2>&1
|
||||
echo '```'
|
||||
|
||||
# Extract CPU profile
|
||||
if [[ -f "$TMPDIR/cpu.prof" ]]; then
|
||||
# Find the test binary (go test leaves it next to the profile)
|
||||
TEST_BIN=$(find "$TMPDIR" -name '*.test' 2>/dev/null | head -1 || true)
|
||||
if [[ -n "$TEST_BIN" ]]; then
|
||||
extract_pprof "$TMPDIR/cpu.prof" "CPU" "$TOP_N"
|
||||
else
|
||||
extract_pprof "$TMPDIR/cpu.prof" "CPU" "$TOP_N"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Extract heap profile
|
||||
if [[ -f "$TMPDIR/mem.prof" ]]; then
|
||||
echo ""
|
||||
echo "--- PPROF HEAP PROFILE (Top ${TOP_N} by Cumulative, alloc_space) ---"
|
||||
echo ""
|
||||
echo '```'
|
||||
go tool pprof -text -cum -nodecount="$TOP_N" -alloc_space "$TMPDIR/mem.prof" 2>/dev/null \
|
||||
| grep -v "^Showing nodes" \
|
||||
| sed '/^$/d'
|
||||
echo '```'
|
||||
echo ""
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "Error: unknown profile type '$PROFILE_TYPE'"
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
@@ -0,0 +1,129 @@
|
||||
---
|
||||
name: sequential-thinking
|
||||
description: Performs dynamic, reflective problem-solving through iterative thought chains. Use for complex planning requiring revision, branching, backtracking, or hypothesis verification. Ideal for multi-step analysis where context maintenance is required or the full scope isn't initially clear.
|
||||
---
|
||||
|
||||
# Sequential Thinking
|
||||
|
||||
A structured approach to complex problem-solving that breaks down challenges into iterative thought steps with built-in flexibility for revision and course correction.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
- Breaking down complex problems into manageable steps
|
||||
- Planning and design requiring iterative refinement
|
||||
- Analysis that might need course correction mid-stream
|
||||
- Problems where the full scope emerges during analysis
|
||||
- Multi-step solutions requiring context across steps
|
||||
- Filtering out irrelevant information
|
||||
- Hypothesis generation and verification workflows
|
||||
|
||||
## Core Methodology
|
||||
|
||||
Sequential thinking follows a dynamic process:
|
||||
|
||||
1. **Initial estimation**: Start with an estimate of thoughts needed, but remain flexible
|
||||
2. **Iterative analysis**: Work through thoughts sequentially while building context
|
||||
3. **Revision capability**: Question or revise previous thoughts as understanding deepens
|
||||
4. **Branch exploration**: Explore alternative approaches when needed
|
||||
5. **Hypothesis cycle**: Generate hypotheses, verify against thought chain, repeat
|
||||
6. **Convergence**: Continue until reaching a satisfactory solution
|
||||
|
||||
## Instructions
|
||||
|
||||
### Thought Structure
|
||||
|
||||
Each thought in the sequence should include:
|
||||
|
||||
- **thought**: Current thinking step content
|
||||
- **thoughtNumber**: Position in sequence (1, 2, 3, ...)
|
||||
- **totalThoughts**: Current estimate of total thoughts needed (adjustable)
|
||||
- **nextThoughtNeeded**: Whether another thought step is required
|
||||
|
||||
Optional revision/branching metadata:
|
||||
- **isRevision**: Boolean indicating if reconsidering previous thinking
|
||||
- **revisesThought**: Which thought number is being revised
|
||||
- **branchFromThought**: Branching point thought number
|
||||
- **branchId**: Identifier for current branch
|
||||
- **needsMoreThoughts**: Flag when reaching end but requiring more analysis
|
||||
|
||||
### Process Guidelines
|
||||
|
||||
**Starting out:**
|
||||
- Estimate initial thoughts needed based on problem complexity
|
||||
- Begin with thought 1, establishing context and approach
|
||||
- Set totalThoughts conservatively; you can adjust later
|
||||
|
||||
**During analysis:**
|
||||
- Build on previous thoughts while maintaining context
|
||||
- Filter out irrelevant information at each step
|
||||
- Express uncertainty when present
|
||||
- Don't hesitate to revise if you spot errors or better approaches
|
||||
- Adjust totalThoughts up/down as the problem's scope becomes clearer
|
||||
|
||||
**Revision pattern:**
|
||||
When reconsidering previous thinking:
|
||||
```json
|
||||
{
|
||||
"thought": "On reflection, thought 3's assumption about X was incorrect because Y...",
|
||||
"thoughtNumber": 6,
|
||||
"totalThoughts": 10,
|
||||
"isRevision": True,
|
||||
"revisesThought": 3,
|
||||
"nextThoughtNeeded": True
|
||||
}
|
||||
```
|
||||
|
||||
**Hypothesis cycle:**
|
||||
1. Generate hypothesis based on current understanding
|
||||
2. Verify against previous thought chain
|
||||
3. If verification fails, revise or branch
|
||||
4. Repeat until hypothesis is validated
|
||||
|
||||
**Completion:**
|
||||
- Only set `nextThoughtNeeded: False` when truly satisfied with the solution
|
||||
- Provide a single, clear final answer
|
||||
- Ensure the answer directly addresses the original problem
|
||||
|
||||
### Working with Context
|
||||
|
||||
**Maintain continuity:**
|
||||
- Reference specific previous thoughts by number
|
||||
- Build logical connections between thoughts
|
||||
- Track which thoughts are still valid vs. revised
|
||||
|
||||
**Filter information:**
|
||||
- Ignore details irrelevant to current thought step
|
||||
- Focus on information that advances understanding
|
||||
- Re-evaluate relevance as context evolves
|
||||
|
||||
**Manage complexity:**
|
||||
- If a thought becomes too complex, break it into multiple thoughts
|
||||
- Increase totalThoughts estimate accordingly
|
||||
- Keep each individual thought focused
|
||||
|
||||
### Output Format
|
||||
|
||||
Present your sequential thinking in a structured format:
|
||||
```
|
||||
Thought [N/Total]: [Current thought content]
|
||||
[If revision: "This revises thought X because..."]
|
||||
[If branching: "Branching from thought X to explore..."]
|
||||
|
||||
[Continue with next thought when nextThoughtNeeded is True]
|
||||
|
||||
Final output after all thoughts complete:
|
||||
Solution: [Clear, direct answer to the original problem]
|
||||
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
For concrete examples of sequential thinking in action, see `resources/examples.md`.
|
||||
|
||||
## Key Principles
|
||||
|
||||
- **Flexibility over rigidity**: Adjust your approach as understanding deepens
|
||||
- **Revision is strength**: Correcting course shows good reasoning
|
||||
- **Hypothesis-driven**: Generate and test hypotheses iteratively
|
||||
- **Context-aware**: Maintain awareness of previous thoughts while progressing
|
||||
- **Clarity at completion**: Deliver a single, clear final answer
|
||||
@@ -0,0 +1,65 @@
|
||||
# Sequential Thinking Examples
|
||||
|
||||
## Example 1: Simple Math Problem
|
||||
|
||||
**Problem:** If a train travels 120 km in 2 hours, then 180 km in the next 3 hours, what's the average speed?
|
||||
|
||||
**Thought 1/5:** Breaking down the problem - we have two segments of travel with different distances and times. Need to calculate total distance and total time first.
|
||||
|
||||
**Thought 2/5:** First segment: 120 km in 2 hours. Second segment: 180 km in 3 hours. Total distance = 120 + 180 = 300 km. Total time = 2 + 3 = 5 hours.
|
||||
|
||||
**Thought 3/5:** Average speed formula = Total distance / Total time = 300 km / 5 hours = 60 km/h.
|
||||
|
||||
**Thought 4/5:** Verifying: Does this make sense? First segment speed = 60 km/h, second segment = 60 km/h. Wait, that can't be right.
|
||||
|
||||
**Thought 5/6 (Revision of Thought 4):** Let me recalculate segment speeds. First: 120/2 = 60 km/h. Second: 180/3 = 60 km/h. Actually, both segments are at 60 km/h, so average is correctly 60 km/h. My suspicion was unfounded.
|
||||
|
||||
**Solution:** The average speed is 60 km/h.
|
||||
|
||||
## Example 2: Complex Design Decision with Branching
|
||||
|
||||
**Problem:** Design a caching strategy for a high-traffic API with both user-specific and global data.
|
||||
|
||||
**Thought 1/8:** Initial analysis - need to consider cache hit rate, memory usage, invalidation complexity, and latency requirements. Two data types suggest a multi-layered approach.
|
||||
|
||||
**Thought 2/8:** Hypothesis: Use Redis with separate key namespaces for user-specific (user:*) and global (global:*) data. User data could have shorter TTL due to personalization.
|
||||
|
||||
**Thought 3/8:** Considering TTL strategy - global data changes infrequently, could use 1-hour TTL. User-specific data needs 5-15 minute TTL to stay fresh.
|
||||
|
||||
**Thought 4/8:** Wait, need to think about invalidation. What happens when global data updates? Broadcasting invalidation to all instances could be expensive.
|
||||
|
||||
**Thought 5/10 (Branching from Thought 4):** Alternative approach - use cache versioning. Include version number in global cache keys. When data updates, increment version. Old cached entries naturally expire.
|
||||
|
||||
**Thought 6/10:** This versioning approach adds complexity but eliminates broadcast invalidation. Trade-off: some stale data until TTL expires vs. system complexity.
|
||||
|
||||
**Thought 7/10:** For user-specific data, simple TTL should work since each user's data is independent. No cross-user invalidation needed.
|
||||
|
||||
**Thought 8/10:** Hypothesis verification - versioned global cache + TTL-based user cache provides good balance. Testing memory usage: assume 100K users, 10KB user cache, 50MB global = ~1GB total. Acceptable.
|
||||
|
||||
**Thought 9/10:** Edge case consideration - what if version wraps around? Use timestamp-based versioning (YYYYMMDDHHMMSS) to prevent collisions.
|
||||
|
||||
**Thought 10/10:** Final design validated. Global cache with timestamp versioning, user cache with 10-minute TTL. Monitoring on cache hit rates and memory usage post-deployment.
|
||||
|
||||
**Solution:** Implement a two-tier Redis caching strategy with timestamp-versioned keys for global data (1-hour TTL) and simple key-value with 10-minute TTL for user-specific data. Monitor hit rates and memory usage after deployment.
|
||||
|
||||
## Example 3: Debugging with Multiple Revisions
|
||||
|
||||
**Problem:** API returning 500 errors intermittently, logs show database timeout.
|
||||
|
||||
**Thought 1/6:** Initial hypothesis - database connection pool exhaustion. Check current pool size and active connections.
|
||||
|
||||
**Thought 2/6:** Pool size = 20, monitoring shows peak usage of 18 connections. Not exhausted. Need to look elsewhere.
|
||||
|
||||
**Thought 3/7 (Revised estimate):** Query performance issue? Check slow query log. Found several queries taking 5-10 seconds during error periods.
|
||||
|
||||
**Thought 4/7:** Slow queries all involve joins on large tables without proper indexes. Hypothesis: missing indexes causing table scans.
|
||||
|
||||
**Thought 5/7:** Wait, these queries worked fine last week. What changed? Checking deployment history... no schema changes, no new queries.
|
||||
|
||||
**Thought 6/8 (Revision of Thought 4):** The queries themselves haven't changed, but data volume has. Tables grew 300% last week due to marketing campaign. Indexes exist but statistics are stale.
|
||||
|
||||
**Thought 7/8:** Hypothesis: Stale query planner statistics causing poor query plans. Solution: Run ANALYZE on affected tables to update statistics.
|
||||
|
||||
**Thought 8/8:** Verified - after ANALYZE, query plans improved and execution times dropped to <500ms. Root cause was stale statistics, not missing indexes.
|
||||
|
||||
**Solution:** Run ANALYZE on the affected database tables to update query planner statistics. Implement automated ANALYZE job to run after bulk data imports to prevent future occurrences.
|
||||
Reference in new issue
Block a user