start-vibing-stacks 2.14.0 → 2.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/stacks/_shared/commands/feature.md +21 -9
- package/stacks/_shared/commands/fix.md +20 -6
- package/stacks/_shared/commands/research.md +18 -7
- package/stacks/_shared/commands/validate.md +31 -4
- package/stacks/_shared/skills/codebase-knowledge/SKILL.md +46 -49
- package/stacks/_shared/skills/codebase-knowledge/TEMPLATE.md +35 -14
- package/stacks/_shared/skills/docker-patterns/SKILL.md +184 -31
- package/stacks/_shared/skills/docs-tracker/SKILL.md +83 -35
- package/stacks/_shared/skills/git-workflow/SKILL.md +140 -17
- package/stacks/_shared/skills/hook-development/SKILL.md +230 -52
- package/stacks/_shared/skills/observability/SKILL.md +72 -2
- package/stacks/_shared/skills/openapi-design/SKILL.md +111 -3
- package/stacks/_shared/skills/playwright-automation/SKILL.md +173 -27
- package/stacks/_shared/skills/postgres-patterns/SKILL.md +85 -4
- package/stacks/_shared/skills/research-cache/SKILL.md +112 -35
- package/stacks/_shared/skills/secrets-management/SKILL.md +98 -12
- package/stacks/_shared/skills/security-baseline/SKILL.md +115 -18
- package/stacks/_shared/skills/ui-ux-audit/SKILL.md +94 -35
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: observability
|
|
3
|
-
version:
|
|
4
|
-
description: Structured logging, correlation IDs, OpenTelemetry tracing, error tracking (Sentry), metrics,
|
|
3
|
+
version: 2.0.0
|
|
4
|
+
description: "Structured logging, correlation IDs, OpenTelemetry tracing (semconv 1.41+, including GenAI conventions for Anthropic / OpenAI / AWS Bedrock / Azure AI / MCP), error tracking (Sentry), metrics, PII redaction, audit log separation. Invoke when adding logging, instrumentation, AI/LLM observability, or debugging production issues."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Observability — Logs, Traces, Metrics
|
|
@@ -285,6 +285,75 @@ Cardinality rule: never tag with user_id, email, or unbounded values — explode
|
|
|
285
285
|
|
|
286
286
|
---
|
|
287
287
|
|
|
288
|
+
## 6.5. AI / LLM Observability — OTel GenAI Semantic Conventions *(2026)*
|
|
289
|
+
|
|
290
|
+
OpenTelemetry GenAI conventions (semconv ≥ 1.37, current ≥ 1.41) standardize how LLM calls are traced. They cover: **model spans**, **agent spans**, **events** (inputs/outputs), and provider-specific conventions for **Anthropic, OpenAI, AWS Bedrock, Azure AI Inference, and MCP (Model Context Protocol)**.
|
|
291
|
+
|
|
292
|
+
> Status: Development (not yet stable). Set `OTEL_SEMCONV_STABILITY_OPT_IN=gen_ai_latest_experimental` to opt in to current attribute names. **Do not use deprecated `gen_ai.prompt` / `gen_ai.completion` attributes** — they were removed in 1.28 in favor of log-based events.
|
|
293
|
+
|
|
294
|
+
### Required attributes on every LLM span
|
|
295
|
+
|
|
296
|
+
| Attribute | Example |
|
|
297
|
+
|---|---|
|
|
298
|
+
| `gen_ai.system` | `"anthropic"`, `"openai"`, `"aws.bedrock"`, `"azure.ai.inference"` |
|
|
299
|
+
| `gen_ai.operation.name` | `"chat"`, `"text_completion"`, `"embeddings"` |
|
|
300
|
+
| `gen_ai.request.model` | `"claude-opus-4-5"`, `"gpt-5"`, `"sonnet-4-5"` |
|
|
301
|
+
| `gen_ai.response.model` | The actual model that served the request (may differ on Bedrock/Gateway) |
|
|
302
|
+
| `gen_ai.usage.input_tokens` | Numeric |
|
|
303
|
+
| `gen_ai.usage.output_tokens` | Numeric |
|
|
304
|
+
| `gen_ai.response.finish_reasons` | `["stop"]`, `["length"]`, `["tool_calls"]` |
|
|
305
|
+
|
|
306
|
+
### Recording inputs/outputs as **events** (not attributes)
|
|
307
|
+
|
|
308
|
+
```ts
|
|
309
|
+
import { trace, SpanKind } from '@opentelemetry/api';
|
|
310
|
+
const tracer = trace.getTracer('llm');
|
|
311
|
+
|
|
312
|
+
await tracer.startActiveSpan('chat anthropic', { kind: SpanKind.CLIENT }, async (span) => {
|
|
313
|
+
span.setAttributes({
|
|
314
|
+
'gen_ai.system': 'anthropic',
|
|
315
|
+
'gen_ai.operation.name': 'chat',
|
|
316
|
+
'gen_ai.request.model': 'claude-opus-4-5',
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
// Inputs/outputs go as EVENTS, not attributes — keeps spans cheap, allows redaction
|
|
320
|
+
span.addEvent('gen_ai.user.message', { 'gen_ai.message.content': redact(userMsg) });
|
|
321
|
+
|
|
322
|
+
const res = await anthropic.messages.create({ /* ... */ });
|
|
323
|
+
|
|
324
|
+
span.setAttributes({
|
|
325
|
+
'gen_ai.response.model': res.model,
|
|
326
|
+
'gen_ai.usage.input_tokens': res.usage.input_tokens,
|
|
327
|
+
'gen_ai.usage.output_tokens': res.usage.output_tokens,
|
|
328
|
+
'gen_ai.response.finish_reasons': [res.stop_reason],
|
|
329
|
+
});
|
|
330
|
+
span.addEvent('gen_ai.assistant.message', { 'gen_ai.message.content': redact(res.content) });
|
|
331
|
+
span.end();
|
|
332
|
+
return res;
|
|
333
|
+
});
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### MCP (Model Context Protocol) spans
|
|
337
|
+
|
|
338
|
+
For agentic flows that call MCP servers:
|
|
339
|
+
- `gen_ai.system`: `"mcp"`
|
|
340
|
+
- `gen_ai.tool.name`: tool invoked
|
|
341
|
+
- Span kind: `CLIENT` (your agent → MCP server)
|
|
342
|
+
|
|
343
|
+
### Cost & token metrics
|
|
344
|
+
|
|
345
|
+
```ts
|
|
346
|
+
const meter = metrics.getMeter('llm');
|
|
347
|
+
const tokensUsed = meter.createCounter('gen_ai.client.token.usage', { unit: '{token}' });
|
|
348
|
+
|
|
349
|
+
tokensUsed.add(res.usage.input_tokens, { 'gen_ai.system': 'anthropic', 'gen_ai.token.type': 'input', 'gen_ai.request.model': 'claude-opus-4-5' });
|
|
350
|
+
tokensUsed.add(res.usage.output_tokens, { 'gen_ai.system': 'anthropic', 'gen_ai.token.type': 'output', 'gen_ai.request.model': 'claude-opus-4-5' });
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
**PII rules still apply** — redact user content in events the same way you redact request bodies in HTTP logs.
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
288
357
|
## 7. Health Checks
|
|
289
358
|
|
|
290
359
|
Two endpoints, distinct semantics:
|
|
@@ -331,6 +400,7 @@ Different retention (often longer), different access (security team), different
|
|
|
331
400
|
- [ ] Sentry initialized with `beforeSend` scrubber
|
|
332
401
|
- [ ] Tracing initialized at process start (before app code)
|
|
333
402
|
- [ ] `/healthz` + `/readyz` endpoints exist
|
|
403
|
+
- [ ] LLM calls emit `gen_ai.*` attributes + token-usage metrics; user content redacted
|
|
334
404
|
|
|
335
405
|
## FORBIDDEN
|
|
336
406
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: openapi-design
|
|
3
|
-
version:
|
|
4
|
-
description: OpenAPI 3.1 spec design — resource naming, status codes, problem+json (RFC 9457) errors, pagination, versioning, security schemes, idempotency, deprecation. Stack-agnostic. Invoke when designing or documenting any HTTP/REST API that publishes an OpenAPI spec, regardless of framework (Fastify, Hono, FastAPI, Laravel, Express, etc.).
|
|
3
|
+
version: 2.0.0
|
|
4
|
+
description: "OpenAPI 3.1 / 3.2 spec design — resource naming, status codes, problem+json (RFC 9457) errors, pagination, versioning, security schemes, idempotency, deprecation. Covers OpenAPI 3.2 (Sept 2025) deltas: native Server-Sent Events (SSE), JSON Lines streaming, hierarchical tags (parent/kind), QUERY HTTP method, additionalOperations, querystring location, $self document URI, OAuth2 device flow. Fully backward-compatible with 3.1 (no breaking changes). Stack-agnostic. Invoke when designing or documenting any HTTP/REST API that publishes an OpenAPI spec, regardless of framework (Fastify, Hono, FastAPI, Laravel, Express, etc.)."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
# OpenAPI Design — Universal API Contract
|
|
7
|
+
# OpenAPI Design — Universal API Contract (3.1 + 3.2)
|
|
8
8
|
|
|
9
9
|
**Invoke before adding a new endpoint, before publishing a public API, and during code review of any route handler.**
|
|
10
10
|
|
|
@@ -12,6 +12,27 @@ description: OpenAPI 3.1 spec design — resource naming, status codes, problem+
|
|
|
12
12
|
|
|
13
13
|
This skill covers **what the spec should look like**. For **how to generate it** in your stack, see the per-framework skill (`fastify-api`, `hono-api`, `fastapi-patterns`, `laravel-patterns`, etc.).
|
|
14
14
|
|
|
15
|
+
## Version policy (2026)
|
|
16
|
+
|
|
17
|
+
- **OpenAPI 3.2** (Sept 2025): adopt for new specs — fully backward-compatible with 3.1, just bump the `openapi` field. **Zero breaking changes.**
|
|
18
|
+
- **OpenAPI 3.1** (Feb 2021): full JSON Schema 2020-12 — still fine for stable APIs.
|
|
19
|
+
- **OpenAPI 3.0**: legacy. Migrate when convenient.
|
|
20
|
+
|
|
21
|
+
### What 3.2 adds (cheat sheet)
|
|
22
|
+
|
|
23
|
+
| Feature | Use when |
|
|
24
|
+
|---|---|
|
|
25
|
+
| **Server-Sent Events (SSE)** as a first-class media type | Long-running progress, AI streaming responses, live dashboards |
|
|
26
|
+
| **JSON Lines** (`application/jsonl`) | Streaming row-oriented data (logs, exports) |
|
|
27
|
+
| **Multipart streaming** | Upload progress with metadata |
|
|
28
|
+
| **Hierarchical tags** (`parent`, `kind`, `summary`) | Replaces vendor-extensions `x-tagGroups`, `x-displayName`. Native nav for large APIs. |
|
|
29
|
+
| **`QUERY` HTTP method** | Safe & idempotent reads with a request body (search APIs that exceed URL limits) |
|
|
30
|
+
| **`additionalOperations`** field | Document custom HTTP verbs without spec extensions |
|
|
31
|
+
| **`querystring` parameter location** | Describe an entire query string as a single typed parameter |
|
|
32
|
+
| **`$self`** document URI | Solves multi-document `$ref` resolution |
|
|
33
|
+
| **OAuth2 Device Authorization flow** | CLI / IoT auth |
|
|
34
|
+
| **Discriminator `defaultMapping`** | Cleaner polymorphic schemas |
|
|
35
|
+
|
|
15
36
|
---
|
|
16
37
|
|
|
17
38
|
## 1. Resource Naming
|
|
@@ -52,6 +73,93 @@ The action sub-resource pattern (`/orders/{id}/cancel`) is the escape hatch when
|
|
|
52
73
|
|
|
53
74
|
---
|
|
54
75
|
|
|
76
|
+
## 2.5. Streaming Responses *(OpenAPI 3.2)*
|
|
77
|
+
|
|
78
|
+
Stop documenting streams as `200 application/json` with hand-rolled extensions. 3.2 lets you describe SSE, JSON Lines, and multipart streams natively.
|
|
79
|
+
|
|
80
|
+
### Server-Sent Events (SSE)
|
|
81
|
+
|
|
82
|
+
```yaml
|
|
83
|
+
paths:
|
|
84
|
+
/chat/{id}/stream:
|
|
85
|
+
get:
|
|
86
|
+
summary: Stream assistant tokens as they generate
|
|
87
|
+
responses:
|
|
88
|
+
'200':
|
|
89
|
+
description: SSE stream
|
|
90
|
+
content:
|
|
91
|
+
text/event-stream:
|
|
92
|
+
schema:
|
|
93
|
+
type: object
|
|
94
|
+
properties:
|
|
95
|
+
event: { type: string, enum: [token, tool_call, done] }
|
|
96
|
+
data: { type: object }
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### JSON Lines (newline-delimited JSON)
|
|
100
|
+
|
|
101
|
+
```yaml
|
|
102
|
+
paths:
|
|
103
|
+
/exports/{id}:
|
|
104
|
+
get:
|
|
105
|
+
responses:
|
|
106
|
+
'200':
|
|
107
|
+
description: One JSON object per line
|
|
108
|
+
content:
|
|
109
|
+
application/jsonl:
|
|
110
|
+
schema:
|
|
111
|
+
$ref: '#/components/schemas/Order'
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Use SSE for **client subscribes to live updates** (chat tokens, progress); JSON Lines for **bulk row export** (logs, analytics dumps). Document the terminator (`event: done` for SSE; EOF for JSON Lines) so codegen clients know when to stop reading.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## 2.6. Hierarchical Tags *(OpenAPI 3.2)*
|
|
119
|
+
|
|
120
|
+
Replaces the legacy `x-tagGroups` / `x-displayName` extensions. Native nested navigation in tools that support 3.2.
|
|
121
|
+
|
|
122
|
+
```yaml
|
|
123
|
+
tags:
|
|
124
|
+
- name: billing
|
|
125
|
+
summary: Billing
|
|
126
|
+
kind: nav # 'nav' = navigation group only, no operations directly under it
|
|
127
|
+
- name: invoices
|
|
128
|
+
parent: billing
|
|
129
|
+
summary: Invoices
|
|
130
|
+
- name: payments
|
|
131
|
+
parent: billing
|
|
132
|
+
summary: Payments
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
For tools still on 3.1, the same effect is achieved with `x-tagGroups` / `x-displayName`. Specs published as 3.2 should drop the extensions.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## 2.7. The `QUERY` HTTP method *(OpenAPI 3.2)*
|
|
140
|
+
|
|
141
|
+
For **safe & idempotent** reads whose parameters exceed URL length limits (complex search, large filter sets). Bodies on `GET` are technically legal but proxies eat them; `QUERY` is the standardized escape hatch.
|
|
142
|
+
|
|
143
|
+
```yaml
|
|
144
|
+
paths:
|
|
145
|
+
/search:
|
|
146
|
+
additionalOperations:
|
|
147
|
+
QUERY:
|
|
148
|
+
summary: Complex search
|
|
149
|
+
requestBody:
|
|
150
|
+
required: true
|
|
151
|
+
content:
|
|
152
|
+
application/json:
|
|
153
|
+
schema:
|
|
154
|
+
$ref: '#/components/schemas/SearchRequest'
|
|
155
|
+
responses:
|
|
156
|
+
'200': { $ref: '#/components/responses/SearchResults' }
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
`QUERY` is **safe** (no side effects) and **idempotent** (same body → same response) — clients and caches treat it like `GET`.
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
55
163
|
## 3. Error Envelope — `application/problem+json` (RFC 9457)
|
|
56
164
|
|
|
57
165
|
One shape for every error response in the entire API. Codegen clients can match a single discriminator.
|
|
@@ -1,23 +1,85 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: playwright-automation
|
|
3
|
-
version:
|
|
3
|
+
version: 2.0.0
|
|
4
|
+
description: "Playwright 1.60+ E2E and browser-automation patterns for 2026. Page Object Model, fixtures (auth + DB cleanup), multi-viewport (375 / 768 / 1280), trace viewer, UI Mode v2 (search + system theme), HAR recording (1.60), screencast API (1.59), Playwright Agents (planner/generator/healer, 1.56+), sharding for CI, accessibility snapshots. Invoke when writing E2E tests or browser automation."
|
|
4
5
|
---
|
|
5
6
|
|
|
6
|
-
# Playwright Automation — E2E Testing
|
|
7
|
+
# Playwright Automation — E2E Testing (1.60+)
|
|
7
8
|
|
|
8
9
|
**ALWAYS invoke when writing E2E tests or browser automation.**
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
> Pin Playwright in `package.json` and pin the matching browser binary version. Mismatch between client and browser is the #1 source of "flaky in CI" reports.
|
|
12
|
+
|
|
13
|
+
## Version policy (2026)
|
|
14
|
+
|
|
15
|
+
- **1.60** (current): HAR recording first-class API (`tracing.startHar()` / `stopHar()`), `locator.drop()`, `test.abort()`, aria-snapshot bounding-box.
|
|
16
|
+
- **1.59**: `page.screencast` (video with action annotations + real-time frames), `browser.bind()`.
|
|
17
|
+
- **1.58**: UI Mode + Trace Viewer search (`Cmd/Ctrl+F`), `system` theme, JSON auto-format, **CLI+SKILLs token-efficient mode** (relevant for AI-assisted authoring).
|
|
18
|
+
- **1.57**: aria snapshot improvements.
|
|
19
|
+
- **1.56**: **Playwright Agents** (planner / generator / healer) — AI-assisted test creation and self-healing locators.
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
// package.json
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"@playwright/test": "1.60.0"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"test:e2e": "playwright test",
|
|
28
|
+
"test:e2e:ui": "playwright test --ui",
|
|
29
|
+
"test:e2e:debug": "PWDEBUG=1 playwright test",
|
|
30
|
+
"test:e2e:trace": "playwright show-trace test-results/trace.zip"
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Folder structure
|
|
11
35
|
|
|
12
36
|
```
|
|
13
37
|
tests/e2e/
|
|
14
|
-
├── fixtures/
|
|
15
|
-
├── pages/
|
|
16
|
-
├── flows/
|
|
17
|
-
├── api/
|
|
38
|
+
├── fixtures/ # Auth, DB cleanup, custom fixtures
|
|
39
|
+
├── pages/ # Page Object Model
|
|
40
|
+
├── flows/ # User journey tests
|
|
41
|
+
├── api/ # API-only tests (request fixture)
|
|
42
|
+
├── visual/ # Screenshot / aria-snapshot regressions
|
|
18
43
|
└── playwright.config.ts
|
|
19
44
|
```
|
|
20
45
|
|
|
46
|
+
## `playwright.config.ts` — production-ready baseline
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import { defineConfig, devices } from '@playwright/test';
|
|
50
|
+
|
|
51
|
+
export default defineConfig({
|
|
52
|
+
testDir: './tests/e2e',
|
|
53
|
+
fullyParallel: true,
|
|
54
|
+
forbidOnly: !!process.env['CI'],
|
|
55
|
+
retries: process.env['CI'] ? 2 : 0,
|
|
56
|
+
workers: process.env['CI'] ? 4 : undefined,
|
|
57
|
+
reporter: [
|
|
58
|
+
['html', { open: 'never' }],
|
|
59
|
+
['junit', { outputFile: 'test-results/junit.xml' }],
|
|
60
|
+
...(process.env['CI'] ? [['github']] : []),
|
|
61
|
+
],
|
|
62
|
+
use: {
|
|
63
|
+
baseURL: process.env['E2E_BASE_URL'] ?? 'http://localhost:3000',
|
|
64
|
+
trace: 'on-first-retry', // saves cost; debug locally with --trace=on
|
|
65
|
+
screenshot: 'only-on-failure',
|
|
66
|
+
video: 'retain-on-failure',
|
|
67
|
+
actionTimeout: 10_000,
|
|
68
|
+
navigationTimeout: 30_000,
|
|
69
|
+
},
|
|
70
|
+
projects: [
|
|
71
|
+
{ name: 'chromium-desktop', use: { ...devices['Desktop Chrome'], viewport: { width: 1280, height: 800 } } },
|
|
72
|
+
{ name: 'webkit-tablet', use: { ...devices['iPad (gen 7)'] } },
|
|
73
|
+
{ name: 'mobile-chrome', use: { ...devices['Pixel 7'] } },
|
|
74
|
+
],
|
|
75
|
+
webServer: process.env['CI'] ? undefined : {
|
|
76
|
+
command: 'npm run dev',
|
|
77
|
+
url: 'http://localhost:3000',
|
|
78
|
+
reuseExistingServer: true,
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
21
83
|
## Page Object Model
|
|
22
84
|
|
|
23
85
|
```typescript
|
|
@@ -42,10 +104,15 @@ export abstract class BasePage {
|
|
|
42
104
|
}
|
|
43
105
|
```
|
|
44
106
|
|
|
45
|
-
##
|
|
107
|
+
## Fixtures — cleanup is mandatory
|
|
46
108
|
|
|
47
|
-
```
|
|
48
|
-
|
|
109
|
+
```ts
|
|
110
|
+
// tests/e2e/fixtures/auth.ts
|
|
111
|
+
import { test as base } from '@playwright/test';
|
|
112
|
+
|
|
113
|
+
type TestUser = { name: string; email: string; password: string };
|
|
114
|
+
|
|
115
|
+
function generateTestUser(): TestUser {
|
|
49
116
|
const ts = Date.now();
|
|
50
117
|
const rand = Math.random().toString(36).substring(7);
|
|
51
118
|
return {
|
|
@@ -54,9 +121,42 @@ export function generateTestUser(): TestUser {
|
|
|
54
121
|
password: 'TestPassword123!',
|
|
55
122
|
};
|
|
56
123
|
}
|
|
124
|
+
|
|
125
|
+
export const test = base.extend<{ user: TestUser }>({
|
|
126
|
+
user: async ({ request }, use) => {
|
|
127
|
+
const user = generateTestUser();
|
|
128
|
+
await request.post('/api/test/users', { data: user });
|
|
129
|
+
await use(user);
|
|
130
|
+
await request.delete(`/api/test/users/${encodeURIComponent(user.email)}`); // cleanup
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
The cleanup runs **even if the test fails** — fixtures unwind with try/finally semantics. Track every created entity in a fixture; never trust a test to clean up after itself.
|
|
136
|
+
|
|
137
|
+
## Selector priority (use locators, not CSS)
|
|
138
|
+
|
|
139
|
+
1. `getByRole('button', { name: 'Save' })` — accessibility-aware
|
|
140
|
+
2. `getByLabel('Email')` — form fields
|
|
141
|
+
3. `getByPlaceholder('Search...')` — when label is implied
|
|
142
|
+
4. `getByText('Welcome back')` — visible text content
|
|
143
|
+
5. `getByTestId('submit-button')` — escape hatch when above don't fit
|
|
144
|
+
|
|
145
|
+
CSS / XPath only as last resort. Prefer roles — they double as accessibility checks.
|
|
146
|
+
|
|
147
|
+
```html
|
|
148
|
+
<!-- Required data-testid on app-rendered elements -->
|
|
149
|
+
<input data-testid="email-input" />
|
|
150
|
+
<input data-testid="password-input" />
|
|
151
|
+
<button data-testid="submit-button" />
|
|
152
|
+
<div data-testid="error-message" />
|
|
153
|
+
<div data-testid="success-message" />
|
|
154
|
+
<div data-testid="loading-spinner" />
|
|
155
|
+
<nav data-testid="sidebar" />
|
|
156
|
+
<button data-testid="hamburger-menu" />
|
|
57
157
|
```
|
|
58
158
|
|
|
59
|
-
## Multi-
|
|
159
|
+
## Multi-viewport testing (375 / 768 / 1280)
|
|
60
160
|
|
|
61
161
|
```typescript
|
|
62
162
|
const viewports = [
|
|
@@ -66,30 +166,76 @@ const viewports = [
|
|
|
66
166
|
] as const;
|
|
67
167
|
|
|
68
168
|
for (const viewport of viewports) {
|
|
69
|
-
test.describe(`Responsive
|
|
169
|
+
test.describe(`Responsive — ${viewport.name}`, () => {
|
|
70
170
|
test.use({ viewport: { width: viewport.width, height: viewport.height } });
|
|
71
171
|
test('navigation adapts', async ({ page }) => { /* ... */ });
|
|
72
172
|
});
|
|
73
173
|
}
|
|
74
174
|
```
|
|
75
175
|
|
|
76
|
-
##
|
|
176
|
+
## Aria snapshots — accessibility regression *(1.50+, improved in 1.60)*
|
|
77
177
|
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
<div data-testid="loading-spinner" />
|
|
85
|
-
<nav data-testid="sidebar" />
|
|
86
|
-
<button data-testid="hamburger-menu" />
|
|
178
|
+
```ts
|
|
179
|
+
// First run records snapshot:
|
|
180
|
+
await expect(page.locator('main')).toMatchAriaSnapshot();
|
|
181
|
+
|
|
182
|
+
// Subsequent runs assert structure stays the same — catches accessibility regressions
|
|
183
|
+
// Update with: npx playwright test --update-snapshots
|
|
87
184
|
```
|
|
88
185
|
|
|
186
|
+
## HAR recording *(1.60)*
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
// Record outbound network for replay or debugging
|
|
190
|
+
await context.tracing.startHar({ path: 'test.har' });
|
|
191
|
+
await page.goto('/checkout');
|
|
192
|
+
await context.tracing.stopHar();
|
|
193
|
+
|
|
194
|
+
// Replay against a recorded HAR (offline / deterministic tests):
|
|
195
|
+
await context.routeFromHAR('checkout.har', { update: false });
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Trace Viewer + UI Mode
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
npx playwright test --trace on # full trace
|
|
202
|
+
npx playwright show-trace test-results/.../trace.zip
|
|
203
|
+
npx playwright test --ui # UI Mode v2 — Cmd/Ctrl+F search, system theme, single-worker
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Sharding for CI
|
|
207
|
+
|
|
208
|
+
```yaml
|
|
209
|
+
# Split a slow E2E suite across N runners
|
|
210
|
+
strategy:
|
|
211
|
+
matrix:
|
|
212
|
+
shardIndex: [1, 2, 3, 4]
|
|
213
|
+
shardTotal: [4]
|
|
214
|
+
steps:
|
|
215
|
+
- run: npx playwright test --shard=${{ matrix.shardIndex }}/${{ matrix.shardTotal }}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Then merge HTML reports with `npx playwright merge-reports`.
|
|
219
|
+
|
|
220
|
+
## Playwright Agents *(1.56+, AI-assisted)*
|
|
221
|
+
|
|
222
|
+
The `planner`, `generator`, and `healer` agents support AI-driven test creation and self-healing locators. Useful when bootstrapping coverage on a legacy app, but **always commit human-reviewed tests** — never let an agent rewrite locked tests in CI without approval.
|
|
223
|
+
|
|
89
224
|
## FORBIDDEN
|
|
90
225
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
226
|
+
| Pattern | Why |
|
|
227
|
+
|---|---|
|
|
228
|
+
| Hardcoded test data (`test@test.com`) | Breaks parallel runs, leaves orphan rows |
|
|
229
|
+
| `.skip()` or `.only()` in committed code | Silently disables coverage |
|
|
230
|
+
| No fixture cleanup | Test DB fills with garbage |
|
|
231
|
+
| Mocked auth | Integration value lost; the real flow breaks unnoticed |
|
|
232
|
+
| Single viewport | Mobile breaks shipped to prod |
|
|
233
|
+
| `page.waitForTimeout(N)` | Flaky; use `waitFor`, `toBeVisible`, etc. |
|
|
234
|
+
| CSS selectors when a role / label exists | Loses accessibility coverage |
|
|
235
|
+
| `--no-deps` in CI install | Browser binaries silently outdated |
|
|
236
|
+
|
|
237
|
+
## See Also
|
|
238
|
+
|
|
239
|
+
- `test-coverage` — what to test, fixtures rules
|
|
240
|
+
- `accessibility-wcag22` — pair with `getByRole` + aria snapshots
|
|
241
|
+
- `ci-pipelines` — Playwright on CI (cache `~/.cache/ms-playwright`)
|
|
@@ -1,16 +1,97 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: postgres-patterns
|
|
3
|
-
version:
|
|
4
|
-
description: PostgreSQL design, security, and tuning at the SQL/operations layer — role separation, Row-Level Security (RLS), connection security, JSONB, partitioning, indexing strategy (BTREE/GIN/BRIN/partial/expression), EXPLAIN ANALYZE, isolation levels, locking, advisory locks, connection pool sizing. Stack-agnostic. Invoke when designing Postgres schemas, hardening database access, or debugging Postgres query performance. Complements per-stack ORM skills (Drizzle, SQLAlchemy, Eloquent, Mongoose) and the database-migrations skill.
|
|
3
|
+
version: 2.0.0
|
|
4
|
+
description: "PostgreSQL design, security, and tuning at the SQL/operations layer — role separation, Row-Level Security (RLS), connection security, JSONB, partitioning, indexing strategy (BTREE/GIN/BRIN/skip-scan/partial/expression), EXPLAIN ANALYZE, isolation levels, locking, advisory locks, connection pool sizing, plus PG 18 (Sept 2025) features: uuidv7(), virtual generated columns (now default), temporal constraints, asynchronous I/O, OAuth 2.0 auth. Stack-agnostic. Invoke when designing Postgres schemas, hardening database access, or debugging Postgres query performance. Complements per-stack ORM skills (Drizzle, SQLAlchemy, Eloquent, Mongoose) and the database-migrations skill."
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
# PostgreSQL — Design, Security, and Tuning
|
|
7
|
+
# PostgreSQL — Design, Security, and Tuning (PG 17 / 18-aware)
|
|
8
8
|
|
|
9
9
|
**Invoke when designing a Postgres schema, granting database access, picking an index, debugging a slow query, or hardening prod connection settings.**
|
|
10
10
|
|
|
11
11
|
> Postgres is the same on every stack. The application code differs. This skill covers the database side — what to ask of it, how to lock it down, and how to read its performance signals. For schema **deployment** (concurrent indexes, lock timeouts, backfills, parallel change), see `database-migrations`.
|
|
12
12
|
|
|
13
|
-
This skill assumes Postgres ≥
|
|
13
|
+
This skill assumes Postgres ≥ 16. **PG 18 (released Sept 25, 2025) is the recommended target for new projects** — see §0 for headline features. PG 17 (Sept 2024) is the safe LTS-style choice for production today; PG 14/15 are still supported but missing features called out below.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## 0. PG 18 — What changed (Sept 25, 2025)
|
|
18
|
+
|
|
19
|
+
The most consequential release for application code in years. Adopt for greenfield; plan migration for prod.
|
|
20
|
+
|
|
21
|
+
| Feature | Why it matters |
|
|
22
|
+
|---|---|
|
|
23
|
+
| **`uuidv7()`** built-in function | Time-ordered UUIDs → drastically better B-tree locality than `gen_random_uuid()` (uuidv4). Use as PK default. |
|
|
24
|
+
| **Virtual generated columns** (now the **default** for `GENERATED ... AS (...)` without `STORED`) | Computed at read time, no write amplification. PG 17 only had `STORED`. |
|
|
25
|
+
| **Temporal constraints** on PK / UNIQUE / FK (`PERIOD`) | Native bitemporal modelling — no more manual `tstzrange` + EXCLUDE workarounds for "valid history" tables. |
|
|
26
|
+
| **Asynchronous I/O (AIO) subsystem** | Up to **3×** sequential-scan / bitmap-heap / vacuum perf. Tunable via `io_method` GUC. |
|
|
27
|
+
| **Skip-scan on multicolumn B-tree indexes** | Index `(a, b)` is now usable to filter by `b` alone (with `a`-cardinality penalty). Reduces "wrong-prefix" index proliferation. |
|
|
28
|
+
| **OAuth 2.0 authentication** | Native SSO at the wire protocol — no PAM/LDAP gymnastics. |
|
|
29
|
+
| **`pg_upgrade` keeps optimizer stats** | Post-upgrade slow-down on first day eliminated. |
|
|
30
|
+
| **OLD / NEW in `RETURNING`** | `UPDATE ... RETURNING OLD.balance, NEW.balance` — audit trails without triggers. |
|
|
31
|
+
|
|
32
|
+
### `uuidv7()` as PK default *(PG 18 idiom)*
|
|
33
|
+
|
|
34
|
+
```sql
|
|
35
|
+
-- PG 18+
|
|
36
|
+
CREATE TABLE orders (
|
|
37
|
+
id uuid PRIMARY KEY DEFAULT uuidv7(),
|
|
38
|
+
...
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
-- PG ≤ 17 — emulate via extension
|
|
42
|
+
CREATE EXTENSION IF NOT EXISTS pg_uuidv7; -- third-party
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
UUIDv7 layout: 48-bit unix-ms timestamp + random tail. Inserts hit the *right* edge of the B-tree → no random-IO write amplification (the well-known v4 problem).
|
|
46
|
+
|
|
47
|
+
### Virtual generated columns
|
|
48
|
+
|
|
49
|
+
```sql
|
|
50
|
+
-- PG 18 — default is VIRTUAL (computed at read)
|
|
51
|
+
CREATE TABLE invoices (
|
|
52
|
+
amount_cents int NOT NULL,
|
|
53
|
+
tax_cents int GENERATED ALWAYS AS (amount_cents * 0.1) NOT STORED, -- still works; explicit NOT STORED
|
|
54
|
+
total_cents int GENERATED ALWAYS AS (amount_cents + (amount_cents * 0.1)) -- DEFAULT virtual in PG 18
|
|
55
|
+
);
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Use VIRTUAL for cheap derivations; STORED when the value is read >> written or indexed.
|
|
59
|
+
|
|
60
|
+
### Temporal constraints
|
|
61
|
+
|
|
62
|
+
```sql
|
|
63
|
+
-- PG 18 — native bitemporal PK
|
|
64
|
+
CREATE TABLE employee_salary (
|
|
65
|
+
employee_id int NOT NULL,
|
|
66
|
+
salary numeric NOT NULL,
|
|
67
|
+
valid tstzrange NOT NULL,
|
|
68
|
+
PRIMARY KEY (employee_id, valid WITHOUT OVERLAPS)
|
|
69
|
+
);
|
|
70
|
+
-- INSERTs that overlap on `valid` for the same employee are rejected — no EXCLUDE-with-gist hack.
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Tuning AIO
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
# postgresql.conf — PG 18
|
|
77
|
+
io_method = 'io_uring' # Linux ≥ 5.x recommended; 'worker' fallback on others
|
|
78
|
+
io_workers = 3 # tune to NVMe queue depth
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## PG 17 — Still excellent (Sept 2024)
|
|
84
|
+
|
|
85
|
+
If you're not yet on 18, prioritize these PG 17 features:
|
|
86
|
+
|
|
87
|
+
- **VACUUM** memory cut up to 20× → much shorter maintenance windows.
|
|
88
|
+
- **WAL throughput** up to 2× under heavy write concurrency.
|
|
89
|
+
- **`pg_createsubscriber`** — promote a physical standby to a logical subscriber in minutes (cuts initial-sync time).
|
|
90
|
+
- **`JSON_TABLE()`** — SQL-standard way to flatten JSONB into rows; avoid hand-written `jsonb_to_recordset` plumbing.
|
|
91
|
+
- **`COPY ... ON_ERROR ignore`** — bulk loads tolerate single bad rows without aborting.
|
|
92
|
+
- **BRIN parallel build** — finally usable for big append-only tables.
|
|
93
|
+
|
|
94
|
+
---
|
|
14
95
|
|
|
15
96
|
---
|
|
16
97
|
|