@coralai/sps-cli 0.42.0 → 0.44.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/README.md +59 -4
- package/dist/commands/consoleCommand.d.ts +2 -0
- package/dist/commands/consoleCommand.d.ts.map +1 -0
- package/dist/commands/consoleCommand.js +129 -0
- package/dist/commands/consoleCommand.js.map +1 -0
- package/dist/commands/projectInit.d.ts.map +1 -1
- package/dist/commands/projectInit.js +40 -53
- package/dist/commands/projectInit.js.map +1 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +14 -2
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/skillCommand.d.ts +2 -0
- package/dist/commands/skillCommand.d.ts.map +1 -0
- package/dist/commands/skillCommand.js +235 -0
- package/dist/commands/skillCommand.js.map +1 -0
- package/dist/console-assets/assets/index-Bhd2f9AP.js +125 -0
- package/dist/console-assets/assets/index-bsAN2a12.css +1 -0
- package/dist/console-assets/index.html +16 -0
- package/dist/console-server/index.d.ts +29 -0
- package/dist/console-server/index.d.ts.map +1 -0
- package/dist/console-server/index.js +145 -0
- package/dist/console-server/index.js.map +1 -0
- package/dist/console-server/lib/lockFile.d.ts +17 -0
- package/dist/console-server/lib/lockFile.d.ts.map +1 -0
- package/dist/console-server/lib/lockFile.js +61 -0
- package/dist/console-server/lib/lockFile.js.map +1 -0
- package/dist/console-server/lib/portPicker.d.ts +3 -0
- package/dist/console-server/lib/portPicker.d.ts.map +1 -0
- package/dist/console-server/lib/portPicker.js +25 -0
- package/dist/console-server/lib/portPicker.js.map +1 -0
- package/dist/console-server/routes/projects.d.ts +11 -0
- package/dist/console-server/routes/projects.d.ts.map +1 -0
- package/dist/console-server/routes/projects.js +149 -0
- package/dist/console-server/routes/projects.js.map +1 -0
- package/dist/console-server/routes/system.d.ts +7 -0
- package/dist/console-server/routes/system.d.ts.map +1 -0
- package/dist/console-server/routes/system.js +19 -0
- package/dist/console-server/routes/system.js.map +1 -0
- package/dist/console-server/sse/eventBus.d.ts +25 -0
- package/dist/console-server/sse/eventBus.d.ts.map +1 -0
- package/dist/console-server/sse/eventBus.js +32 -0
- package/dist/console-server/sse/eventBus.js.map +1 -0
- package/dist/console-server/watchers/cardWatcher.d.ts +9 -0
- package/dist/console-server/watchers/cardWatcher.d.ts.map +1 -0
- package/dist/console-server/watchers/cardWatcher.js +42 -0
- package/dist/console-server/watchers/cardWatcher.js.map +1 -0
- package/dist/core/skillStore.d.ts +46 -0
- package/dist/core/skillStore.d.ts.map +1 -0
- package/dist/core/skillStore.js +210 -0
- package/dist/core/skillStore.js.map +1 -0
- package/dist/core/skillStore.test.d.ts +2 -0
- package/dist/core/skillStore.test.d.ts.map +1 -0
- package/dist/core/skillStore.test.js +203 -0
- package/dist/core/skillStore.test.js.map +1 -0
- package/dist/main.js +27 -17
- package/dist/main.js.map +1 -1
- package/package.json +8 -2
- package/skills/architecture-decision-records/SKILL.md +207 -0
- package/skills/backend/SKILL.md +62 -0
- package/skills/backend/references/api-design.md +168 -0
- package/skills/backend/references/caching.md +181 -0
- package/skills/backend/references/data-access.md +173 -0
- package/skills/backend/references/layering.md +181 -0
- package/skills/backend/references/observability.md +190 -0
- package/skills/backend/references/resilience.md +201 -0
- package/skills/backend/references/security.md +186 -0
- package/skills/backend-architect/SKILL.md +119 -0
- package/skills/code-reviewer/SKILL.md +143 -0
- package/skills/coding-standards/SKILL.md +60 -0
- package/skills/coding-standards/references/clean-code.md +258 -0
- package/skills/coding-standards/references/code-review.md +192 -0
- package/skills/coding-standards/references/commits-and-prs.md +226 -0
- package/skills/coding-standards/references/error-strategy.md +193 -0
- package/skills/coding-standards/references/naming.md +185 -0
- package/skills/coding-standards/references/tdd.md +171 -0
- package/skills/database/SKILL.md +53 -0
- package/skills/database/references/indexing.md +190 -0
- package/skills/database/references/migrations.md +199 -0
- package/skills/database/references/nosql.md +185 -0
- package/skills/database/references/queries.md +295 -0
- package/skills/database/references/scaling.md +203 -0
- package/skills/database/references/schema.md +191 -0
- package/skills/database-optimizer/SKILL.md +168 -0
- package/skills/debugging-workflow/SKILL.md +244 -0
- package/skills/devops/SKILL.md +55 -0
- package/skills/devops/references/ci-cd.md +204 -0
- package/skills/devops/references/containers.md +272 -0
- package/skills/devops/references/deploy.md +201 -0
- package/skills/devops/references/iac.md +252 -0
- package/skills/devops/references/observability.md +228 -0
- package/skills/devops/references/secrets.md +178 -0
- package/skills/devops-automator/SKILL.md +164 -0
- package/skills/frontend/SKILL.md +52 -0
- package/skills/frontend/references/accessibility.md +222 -0
- package/skills/frontend/references/components.md +206 -0
- package/skills/frontend/references/performance.md +219 -0
- package/skills/frontend/references/routing.md +209 -0
- package/skills/frontend/references/state.md +190 -0
- package/skills/frontend/references/testing.md +216 -0
- package/skills/frontend-developer/SKILL.md +115 -0
- package/skills/git-workflow/SKILL.md +355 -0
- package/skills/golang/SKILL.md +49 -0
- package/skills/golang/references/concurrency.md +284 -0
- package/skills/golang/references/errors.md +241 -0
- package/skills/golang/references/idioms.md +285 -0
- package/skills/golang/references/testing.md +238 -0
- package/skills/java/SKILL.md +50 -0
- package/skills/java/references/concurrency.md +194 -0
- package/skills/java/references/idioms.md +283 -0
- package/skills/java/references/testing.md +228 -0
- package/skills/kotlin/SKILL.md +47 -0
- package/skills/kotlin/references/coroutines.md +240 -0
- package/skills/kotlin/references/idioms.md +268 -0
- package/skills/kotlin/references/testing.md +219 -0
- package/skills/mobile/SKILL.md +50 -0
- package/skills/mobile/references/architecture.md +204 -0
- package/skills/mobile/references/navigation.md +158 -0
- package/skills/mobile/references/performance.md +152 -0
- package/skills/mobile/references/platform.md +166 -0
- package/skills/mobile/references/state-and-data.md +174 -0
- package/skills/python/SKILL.md +51 -0
- package/skills/python/THIRD_PARTY.md +14 -0
- package/skills/python/references/async.md +218 -0
- package/skills/python/references/error-handling.md +254 -0
- package/skills/python/references/idioms.md +279 -0
- package/skills/python/references/packaging.md +233 -0
- package/skills/python/references/testing.md +269 -0
- package/skills/python/references/typing.md +292 -0
- package/skills/qa-tester/SKILL.md +186 -0
- package/skills/rust/SKILL.md +50 -0
- package/skills/rust/references/async.md +224 -0
- package/skills/rust/references/errors.md +240 -0
- package/skills/rust/references/ownership.md +263 -0
- package/skills/rust/references/testing.md +274 -0
- package/skills/rust/references/traits.md +250 -0
- package/skills/security-engineer/SKILL.md +157 -0
- package/skills/swift/SKILL.md +48 -0
- package/skills/swift/references/concurrency.md +280 -0
- package/skills/swift/references/idioms.md +334 -0
- package/skills/swift/references/testing.md +229 -0
- package/skills/typescript/SKILL.md +51 -0
- package/skills/typescript/references/async.md +241 -0
- package/skills/typescript/references/errors.md +208 -0
- package/skills/typescript/references/idioms.md +246 -0
- package/skills/typescript/references/testing.md +225 -0
- package/skills/typescript/references/tooling.md +208 -0
- package/skills/typescript/references/types.md +259 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# Frontend Testing
|
|
2
|
+
|
|
3
|
+
Component tests, E2E (Playwright), visual regression. For TDD, see `coding-standards/references/tdd.md`.
|
|
4
|
+
|
|
5
|
+
## The pyramid, frontend version
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
▲
|
|
9
|
+
│ Visual / E2E (5%) ← real browser, real flows
|
|
10
|
+
│ Integration (20%) ← component + key deps; real rendering
|
|
11
|
+
│ Unit (75%) ← functions, hooks, reducers; no DOM
|
|
12
|
+
▼
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Invert this and tests become slow, flaky, and your fast feedback loop dies.
|
|
16
|
+
|
|
17
|
+
## Unit tests — pure logic
|
|
18
|
+
|
|
19
|
+
For reducers, selectors, utility functions, validation. Same rules as any language.
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
describe('priceWithTax', () => {
|
|
23
|
+
it('adds tax by rate', () => {
|
|
24
|
+
expect(priceWithTax(100, 0.2)).toBe(120);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Component tests — Testing Library philosophy
|
|
30
|
+
|
|
31
|
+
Test the component through the accessibility tree, not through implementation internals.
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
import { render, screen } from '@testing-library/react';
|
|
35
|
+
import { userEvent } from '@testing-library/user-event';
|
|
36
|
+
|
|
37
|
+
test('submit is disabled until email is valid', async () => {
|
|
38
|
+
render(<SignupForm />);
|
|
39
|
+
const email = screen.getByLabelText(/email/i);
|
|
40
|
+
const submit = screen.getByRole('button', { name: /sign up/i });
|
|
41
|
+
|
|
42
|
+
expect(submit).toBeDisabled();
|
|
43
|
+
await userEvent.type(email, 'a@x.com');
|
|
44
|
+
expect(submit).toBeEnabled();
|
|
45
|
+
});
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Key conventions:
|
|
49
|
+
- **Query by role** (`getByRole('button', { name: 'Save' })`) — that's what a screen reader sees.
|
|
50
|
+
- **Query by label / text** for inputs and visible content.
|
|
51
|
+
- **Never by className / test-id unless nothing else works.**
|
|
52
|
+
- **Never assert on implementation details** (internal state, prop changes, class names).
|
|
53
|
+
|
|
54
|
+
## Mock at the network boundary
|
|
55
|
+
|
|
56
|
+
Don't mock internal hooks. Mock the fetch / WS. Tests stay honest.
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
import { setupServer } from 'msw/node';
|
|
60
|
+
import { http, HttpResponse } from 'msw';
|
|
61
|
+
|
|
62
|
+
const server = setupServer(
|
|
63
|
+
http.get('/api/users/:id', ({ params }) =>
|
|
64
|
+
HttpResponse.json({ id: params.id, email: 'a@x.com' })
|
|
65
|
+
),
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
beforeAll(() => server.listen());
|
|
69
|
+
afterEach(() => server.resetHandlers());
|
|
70
|
+
afterAll(() => server.close());
|
|
71
|
+
|
|
72
|
+
test('loads and renders user', async () => {
|
|
73
|
+
render(<UserCard id="u1" />);
|
|
74
|
+
expect(await screen.findByText('a@x.com')).toBeInTheDocument();
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
MSW (Mock Service Worker) works for unit/integration tests and in the browser. Same mocks everywhere.
|
|
79
|
+
|
|
80
|
+
## Async: `findBy` over `waitFor` + `getBy`
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
# ✅ waits implicitly; throws if not found in time
|
|
84
|
+
expect(await screen.findByText('Welcome')).toBeInTheDocument();
|
|
85
|
+
|
|
86
|
+
# ❌ verbose
|
|
87
|
+
await waitFor(() => expect(screen.getByText('Welcome')).toBeInTheDocument());
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Don't sleep in tests
|
|
91
|
+
|
|
92
|
+
If `setTimeout(..., 500)` is the only way to make it pass, fake the timer:
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
vi.useFakeTimers();
|
|
96
|
+
// interact
|
|
97
|
+
vi.advanceTimersByTime(500);
|
|
98
|
+
// assert
|
|
99
|
+
vi.useRealTimers();
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Real sleeps make tests slow and flaky.
|
|
103
|
+
|
|
104
|
+
## Hook / composable tests
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
import { renderHook, act } from '@testing-library/react';
|
|
108
|
+
|
|
109
|
+
test('useCounter increments', () => {
|
|
110
|
+
const { result } = renderHook(() => useCounter());
|
|
111
|
+
act(() => result.current.increment());
|
|
112
|
+
expect(result.current.count).toBe(1);
|
|
113
|
+
});
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Vue's equivalent uses `mount` with a minimal wrapper.
|
|
117
|
+
|
|
118
|
+
## Store / reducer tests
|
|
119
|
+
|
|
120
|
+
Pure functions — test without a DOM.
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
test('cartReducer adds item', () => {
|
|
124
|
+
const state = cartReducer(initial, { type: 'add', item });
|
|
125
|
+
expect(state.items).toHaveLength(1);
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
If you need the store under Redux-Toolkit or similar, create a test-only store instance. Don't export globals shared across tests.
|
|
130
|
+
|
|
131
|
+
## E2E — Playwright (or Cypress)
|
|
132
|
+
|
|
133
|
+
Playwright has clear advantages:
|
|
134
|
+
- First-class TypeScript, no CLI glue.
|
|
135
|
+
- Multi-browser (Chromium, Firefox, WebKit).
|
|
136
|
+
- Parallel by default.
|
|
137
|
+
- Network interception built in.
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
import { test, expect } from '@playwright/test';
|
|
141
|
+
|
|
142
|
+
test('signup flow', async ({ page }) => {
|
|
143
|
+
await page.goto('/signup');
|
|
144
|
+
await page.getByLabel('Email').fill('a@x.com');
|
|
145
|
+
await page.getByLabel('Password').fill('correct horse battery staple');
|
|
146
|
+
await page.getByRole('button', { name: 'Create account' }).click();
|
|
147
|
+
await expect(page.getByRole('heading', { name: 'Welcome' })).toBeVisible();
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Rules:
|
|
152
|
+
- **Write by role / label**, not CSS selectors.
|
|
153
|
+
- **Wait for state, not for time.** Playwright's `expect().toBeVisible()` auto-retries.
|
|
154
|
+
- **Own your data.** Seed the DB (API or fixture) before each test; tests shouldn't depend on state from other tests.
|
|
155
|
+
- **Keep E2E to happy-path + critical flows.** Unit / integration cover the combinatorics.
|
|
156
|
+
|
|
157
|
+
## Visual regression
|
|
158
|
+
|
|
159
|
+
Tools: Playwright built-in screenshot diff, Chromatic (Storybook), Percy, Applitools.
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
await expect(page).toHaveScreenshot('home.png');
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Traps:
|
|
166
|
+
- Flaky anti-alias / font rendering across OSes → run on Linux in CI only, accept minor diff tolerance.
|
|
167
|
+
- Time / random IDs in the screenshot → stub or mask.
|
|
168
|
+
- Snapshot explosion (one per page × browser × theme) → be selective.
|
|
169
|
+
|
|
170
|
+
Great for design systems and key pages. Less valuable for the long tail.
|
|
171
|
+
|
|
172
|
+
## Storybook
|
|
173
|
+
|
|
174
|
+
Isolate components. One story per variant.
|
|
175
|
+
|
|
176
|
+
```
|
|
177
|
+
export default { title: 'Button' };
|
|
178
|
+
|
|
179
|
+
export const Primary = () => <Button variant="primary">Save</Button>;
|
|
180
|
+
export const Disabled = () => <Button disabled>Save</Button>;
|
|
181
|
+
export const Long = () => <Button>Save my very long message</Button>;
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
- Run component tests against stories (`play` function).
|
|
185
|
+
- Run a11y checks against stories (`@storybook/addon-a11y`).
|
|
186
|
+
- Use as visual regression fixture.
|
|
187
|
+
|
|
188
|
+
Kills the "how did the component look in that one state again?" question.
|
|
189
|
+
|
|
190
|
+
## CI configuration
|
|
191
|
+
|
|
192
|
+
Typical split:
|
|
193
|
+
|
|
194
|
+
| Stage | Runs | On |
|
|
195
|
+
|---|---|---|
|
|
196
|
+
| Unit | Every commit | All PRs, fast fail |
|
|
197
|
+
| Integration / component | Every commit | All PRs |
|
|
198
|
+
| E2E happy path | Every commit | PRs, on staging |
|
|
199
|
+
| Visual regression | Daily + on design-touching PRs | Scheduled |
|
|
200
|
+
| Full E2E matrix | On main / pre-release | Nightly |
|
|
201
|
+
|
|
202
|
+
Run the cheap ones on every commit; save the expensive ones for what they uniquely catch.
|
|
203
|
+
|
|
204
|
+
## Anti-patterns
|
|
205
|
+
|
|
206
|
+
| Anti-pattern | Fix |
|
|
207
|
+
|---|---|
|
|
208
|
+
| Testing internal state / prop of a component | Test behaviour the user experiences |
|
|
209
|
+
| Snapshot tests for entire pages | Brittle; use for specific outputs |
|
|
210
|
+
| `data-testid` everywhere | Rely on roles / labels first |
|
|
211
|
+
| Mocking React / Vue internals | You're testing the framework, not your code |
|
|
212
|
+
| Shared global state between E2E tests | Seed per-test; reset between |
|
|
213
|
+
| 50 E2E tests, 5 unit tests | Flip the pyramid |
|
|
214
|
+
| Waiting for `await page.waitForTimeout(1000)` | Wait for a condition / state |
|
|
215
|
+
| Testing against prod or shared staging | Test against ephemeral / dedicated env |
|
|
216
|
+
| CSS-class-based assertions (`toHaveClass('active')`) | Assert on observable effect (visible text, role, aria) |
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: frontend-developer
|
|
3
|
+
description: Persona skill — think like a frontend developer. Users, latency, accessibility, state clarity. Overlay on top of `frontend` + language skills. For patterns, load `frontend`.
|
|
4
|
+
origin: agency-agents-fork + original (https://github.com/msitarzewski/agency-agents, MIT)
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Frontend Developer
|
|
8
|
+
|
|
9
|
+
Think like a frontend developer. This is a **mindset overlay** — load `frontend` for patterns.
|
|
10
|
+
|
|
11
|
+
## When to load
|
|
12
|
+
|
|
13
|
+
- Building / reviewing UI
|
|
14
|
+
- Making a call on what state shape to use
|
|
15
|
+
- Reviewing a design for technical feasibility
|
|
16
|
+
- Debating component API, routing, or data fetching
|
|
17
|
+
- Triaging a frontend perf / a11y issue
|
|
18
|
+
|
|
19
|
+
## The posture
|
|
20
|
+
|
|
21
|
+
1. **Users don't see your code.** They see latency, layout shifts, broken keyboards. Measure what they experience.
|
|
22
|
+
2. **State is the hard part.** Components are easy. Server state ≠ client state ≠ URL state — don't mix them.
|
|
23
|
+
3. **Native first, custom second.** `<button>` beats `<div role="button">`. Always.
|
|
24
|
+
4. **Every feature has a loading state AND an error state.** Design all three up front, not "happy path, will add later".
|
|
25
|
+
5. **Accessibility is correctness.** Keyboard, contrast, screen reader. Not a final pass.
|
|
26
|
+
6. **Perf is a feature, not an optimization pass.** Budgets in CI, not hope.
|
|
27
|
+
7. **The browser is hostile.** Old versions, slow networks, third-party scripts, ad blockers. Degrade gracefully.
|
|
28
|
+
|
|
29
|
+
## The questions you always ask
|
|
30
|
+
|
|
31
|
+
- **What does this look like at 300 ms latency?** Many interactions.
|
|
32
|
+
- **What if the fetch fails halfway?** Partial render, retry, error message.
|
|
33
|
+
- **What if the user presses Tab?** Does focus go where a keyboard user expects?
|
|
34
|
+
- **What state is this, exactly?** Local, shared, server, URL, derived?
|
|
35
|
+
- **Is this URL bookmarkable?** Would I land on the same view if I pasted it?
|
|
36
|
+
- **Which images / fonts / scripts block first paint?** Measure LCP.
|
|
37
|
+
- **Does this work with JavaScript disabled / broken?** (For critical content paths.)
|
|
38
|
+
- **Is this accessible with a screen reader?** Actually tested, not just "has alt attributes".
|
|
39
|
+
- **Who re-renders when this state changes?** Keep the blast radius small.
|
|
40
|
+
- **What does this cost in bundle bytes?** Every library has a weight.
|
|
41
|
+
|
|
42
|
+
## The checklist
|
|
43
|
+
|
|
44
|
+
### UX shape
|
|
45
|
+
- [ ] Loading state designed
|
|
46
|
+
- [ ] Error state designed with actionable message
|
|
47
|
+
- [ ] Empty state designed
|
|
48
|
+
- [ ] Disabled states clear (not just grayed out — provide the reason)
|
|
49
|
+
- [ ] Optimistic updates for user-initiated writes
|
|
50
|
+
|
|
51
|
+
### Accessibility
|
|
52
|
+
- [ ] Semantic HTML throughout
|
|
53
|
+
- [ ] Keyboard-navigable
|
|
54
|
+
- [ ] Visible focus styles (`:focus-visible`)
|
|
55
|
+
- [ ] Screen-reader tested for critical flows
|
|
56
|
+
- [ ] Contrast ≥ WCAG AA (4.5:1 text / 3:1 UI)
|
|
57
|
+
- [ ] Respects `prefers-reduced-motion`
|
|
58
|
+
|
|
59
|
+
### State
|
|
60
|
+
- [ ] Server data via a cache lib (TanStack Query / SWR / Apollo / equivalent)
|
|
61
|
+
- [ ] No duplicate sources of truth
|
|
62
|
+
- [ ] URL holds filters / selected tabs / deep state
|
|
63
|
+
- [ ] Forms validate client-side + server-side
|
|
64
|
+
- [ ] Drafts persisted where the user expects (offline / reload)
|
|
65
|
+
|
|
66
|
+
### Performance
|
|
67
|
+
- [ ] LCP < 2.5 s, INP < 200 ms, CLS < 0.1 (at 75th percentile)
|
|
68
|
+
- [ ] Images sized to display; lazy below the fold
|
|
69
|
+
- [ ] Bundle analysis done; no 5 MB surprises
|
|
70
|
+
- [ ] Route-level code splitting
|
|
71
|
+
- [ ] Virtualization for long lists
|
|
72
|
+
- [ ] Web fonts preloaded with `font-display: swap`
|
|
73
|
+
|
|
74
|
+
### Code
|
|
75
|
+
- [ ] Components do one thing
|
|
76
|
+
- [ ] Props typed
|
|
77
|
+
- [ ] Side effects in hooks / stores, not in render
|
|
78
|
+
- [ ] No `any` / untyped data
|
|
79
|
+
- [ ] Dead code / commented-out code removed
|
|
80
|
+
|
|
81
|
+
## Tradeoffs you name
|
|
82
|
+
|
|
83
|
+
- **CSR vs. SSR vs. SSG** — per-page, based on content and SEO needs.
|
|
84
|
+
- **Controlled vs. uncontrolled** — controlled for complex forms, uncontrolled for simple.
|
|
85
|
+
- **Optimistic vs. pessimistic** — optimistic for routine writes, pessimistic for dangerous ones (payments, delete).
|
|
86
|
+
- **Animation vs. function** — animation is a detail; don't block interactions.
|
|
87
|
+
- **Client-side vs. server-side validation** — both, not one.
|
|
88
|
+
|
|
89
|
+
## What you push back on
|
|
90
|
+
|
|
91
|
+
- **Designs that ignore loading / error / empty states.** Those ARE the user experience.
|
|
92
|
+
- **Accessibility-as-final-sprint.** Costs more to retrofit.
|
|
93
|
+
- **"Just wrap it in a div with onClick".** No.
|
|
94
|
+
- **Reactive libraries for static data.** Over-engineering.
|
|
95
|
+
- **Prop drilling 6 levels deep.** Context / composition.
|
|
96
|
+
- **Toasts / dialogs for critical errors.** Modals that block progress, then. Not a toast that vanishes.
|
|
97
|
+
|
|
98
|
+
## Forbidden patterns
|
|
99
|
+
|
|
100
|
+
- `<div onClick>` where `<button>` / `<a>` fits
|
|
101
|
+
- Placeholder used as label
|
|
102
|
+
- Color as the only error signal
|
|
103
|
+
- Business logic in JSX
|
|
104
|
+
- `useEffect` to derive state (use computed values / `useMemo`)
|
|
105
|
+
- Untyped data at boundaries
|
|
106
|
+
- Images without `width`/`height` (layout shift)
|
|
107
|
+
- Toasts swallowing irrecoverable errors
|
|
108
|
+
- "Submit" enabled during in-flight submit (double-submit bug)
|
|
109
|
+
- Hand-rolled date pickers / selects that ignore keyboard / screen reader
|
|
110
|
+
|
|
111
|
+
## Pair with
|
|
112
|
+
|
|
113
|
+
- [`frontend`](../frontend/SKILL.md) — the patterns.
|
|
114
|
+
- A language skill (`typescript`) — syntax and types.
|
|
115
|
+
- [`coding-standards`](../coding-standards/SKILL.md) — general principles.
|
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: git-workflow
|
|
3
|
+
description: Workflow skill — git hygiene beyond commit-message style. Branching, rebasing, conflict resolution, recovering from mistakes. For commit-message format and PR sizing, see `coding-standards/references/commits-and-prs.md`.
|
|
4
|
+
origin: original
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Git Workflow
|
|
8
|
+
|
|
9
|
+
Day-to-day git habits for keeping history useful and recovery cheap. For commit message style and PR sizing, load [`coding-standards/references/commits-and-prs.md`](../coding-standards/references/commits-and-prs.md).
|
|
10
|
+
|
|
11
|
+
## When to load
|
|
12
|
+
|
|
13
|
+
- Starting a new feature branch
|
|
14
|
+
- Reviewing how to structure a series of commits
|
|
15
|
+
- Resolving a merge / rebase conflict
|
|
16
|
+
- Recovering from a "bad git operation"
|
|
17
|
+
- Onboarding to a repo with a specific workflow (trunk-based, GitFlow, etc.)
|
|
18
|
+
|
|
19
|
+
## The posture
|
|
20
|
+
|
|
21
|
+
1. **Commits are communication.** The history is read by your future self and your teammates. Make it readable.
|
|
22
|
+
2. **Small, logical, atomic commits.** Each commit reverts cleanly. Each commit tells one story.
|
|
23
|
+
3. **Rebase your own branch; never shared branches.** History rewriting on public branches breaks everyone.
|
|
24
|
+
4. **Never `--force` without `--force-with-lease`.** The safer form checks no one else has pushed.
|
|
25
|
+
5. **Lost work is usually recoverable.** `git reflog` remembers.
|
|
26
|
+
6. **When in doubt, stash or branch before experimenting.** Cheap insurance.
|
|
27
|
+
|
|
28
|
+
## Workflow patterns
|
|
29
|
+
|
|
30
|
+
Pick one per project and stick to it.
|
|
31
|
+
|
|
32
|
+
### Trunk-based
|
|
33
|
+
|
|
34
|
+
Short-lived branches, merge to `main` at least daily. Best for CI-strong teams.
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
main ─────────────●──────●──────●──────●──────
|
|
38
|
+
↑ ↑ ↑ ↑
|
|
39
|
+
feature branches, 1–3 days each
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Pros: minimal merge pain, fast feedback, clear integration point.
|
|
43
|
+
Cons: needs strong CI and feature flags to ship incomplete work safely.
|
|
44
|
+
|
|
45
|
+
### Feature-branch + PR (pragmatic default)
|
|
46
|
+
|
|
47
|
+
Named branches per feature, merged via PR to `main`. Branches live days to a week.
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
main ─────●────────●────────●─────
|
|
51
|
+
\ / \ /
|
|
52
|
+
feature1 feature2
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Most teams. Rebase on `main` before merging to keep history linear.
|
|
56
|
+
|
|
57
|
+
### GitFlow
|
|
58
|
+
|
|
59
|
+
`develop`, `release/*`, `hotfix/*` alongside `main`. Heavier; fits products with long support branches.
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
main (release tags)
|
|
63
|
+
│
|
|
64
|
+
├─ release/* (stabilization)
|
|
65
|
+
│
|
|
66
|
+
└─ develop
|
|
67
|
+
├─ feature/*
|
|
68
|
+
└─ hotfix/* → main + develop
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Use when you ship monthly / quarterly and have parallel release support. Overkill for most web apps.
|
|
72
|
+
|
|
73
|
+
## Branch naming
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
<type>/<short-slug>
|
|
77
|
+
|
|
78
|
+
feat/partial-shipments
|
|
79
|
+
fix/auth-alg-none
|
|
80
|
+
chore/bump-node-20
|
|
81
|
+
refactor/extract-validator
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Include a ticket id if your team tracks: `fix/SEC-412-alg-none`.
|
|
85
|
+
|
|
86
|
+
Rules:
|
|
87
|
+
- Lowercase + hyphens.
|
|
88
|
+
- No personal names (`alice/wip`); one-off personal branches die without review.
|
|
89
|
+
- No `feature/` prefix — type is the prefix, and `feature/` duplicates.
|
|
90
|
+
|
|
91
|
+
## The commit loop
|
|
92
|
+
|
|
93
|
+
Good session flow:
|
|
94
|
+
|
|
95
|
+
1. `git status` before starting — clean? any leftover changes?
|
|
96
|
+
2. Make a change; run tests.
|
|
97
|
+
3. `git diff` — review before staging.
|
|
98
|
+
4. `git add -p` — stage in logical chunks, not everything blindly.
|
|
99
|
+
5. `git commit` — one logical change, good message.
|
|
100
|
+
6. Repeat.
|
|
101
|
+
|
|
102
|
+
`-p` (patch) mode is underrated. It lets you split "one big edit" into several commits.
|
|
103
|
+
|
|
104
|
+
## Rebase your own branch on `main`
|
|
105
|
+
|
|
106
|
+
Keeps a clean linear history and avoids dirty "Merge main into feature" commits.
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
git fetch origin
|
|
110
|
+
git rebase origin/main
|
|
111
|
+
# resolve conflicts if any
|
|
112
|
+
git push --force-with-lease
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Never rebase a branch others are working on — they'll have conflicts and lose commits.
|
|
116
|
+
|
|
117
|
+
## Interactive rebase — clean up your history
|
|
118
|
+
|
|
119
|
+
Before opening a PR:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
git rebase -i origin/main
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Opens an editor listing your commits. Actions:
|
|
126
|
+
|
|
127
|
+
| Action | Effect |
|
|
128
|
+
|---|---|
|
|
129
|
+
| `pick` | Keep as-is |
|
|
130
|
+
| `reword` | Change the commit message |
|
|
131
|
+
| `edit` | Stop to amend the commit |
|
|
132
|
+
| `squash` | Combine with the previous commit; edit message |
|
|
133
|
+
| `fixup` | Combine with previous; keep previous message |
|
|
134
|
+
| `drop` | Delete |
|
|
135
|
+
|
|
136
|
+
Use to squash WIP commits, fix typos in messages, reorder logically. Don't use on already-pushed-to-shared branches.
|
|
137
|
+
|
|
138
|
+
## Handling conflicts
|
|
139
|
+
|
|
140
|
+
When rebase or merge hits a conflict:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
# git marks conflict files; you edit and resolve
|
|
144
|
+
<<<<<<< HEAD
|
|
145
|
+
your code
|
|
146
|
+
=======
|
|
147
|
+
their code
|
|
148
|
+
>>>>>>> branch
|
|
149
|
+
|
|
150
|
+
# After editing:
|
|
151
|
+
git add <resolved-files>
|
|
152
|
+
git rebase --continue # or git merge --continue
|
|
153
|
+
|
|
154
|
+
# To back out:
|
|
155
|
+
git rebase --abort
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Rules:
|
|
159
|
+
- **Read both sides.** Don't just pick one wholesale.
|
|
160
|
+
- **Ensure tests pass after the resolution.** A bad merge compiles but breaks behaviour.
|
|
161
|
+
- **Commit the resolution as is.** Don't mix in other changes.
|
|
162
|
+
|
|
163
|
+
### `rerere` — reuse recorded resolutions
|
|
164
|
+
|
|
165
|
+
Turn on once, saves pain on repeated rebases:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
git config --global rerere.enabled true
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Git remembers how you resolved a given conflict; applies it automatically next time the same conflict appears.
|
|
172
|
+
|
|
173
|
+
## Force-push safely
|
|
174
|
+
|
|
175
|
+
Rebasing changes commit SHAs, requiring a force-push. Use `--force-with-lease`:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
git push --force-with-lease
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
`--force` overwrites the remote without checking. If someone else pushed to your branch, you silently overwrite their work. `--force-with-lease` fails safely in that case.
|
|
182
|
+
|
|
183
|
+
Better alias:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
git config --global alias.pf 'push --force-with-lease'
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Recovering lost work
|
|
190
|
+
|
|
191
|
+
Almost all "lost" git operations are recoverable via `reflog`.
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
git reflog # list of everything HEAD has pointed to
|
|
195
|
+
# HEAD@{0}: rebase: ...
|
|
196
|
+
# HEAD@{1}: rebase: ...
|
|
197
|
+
# HEAD@{2}: commit: "WIP"
|
|
198
|
+
git checkout HEAD@{2} # go back to the state before you broke it
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
If your last commit was broken and you ran `git reset --hard`:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
git reflog
|
|
205
|
+
git reset --hard HEAD@{1} # restore
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Works for ~90 days by default. The golden rule: **commit often. Committed work is almost never truly lost.**
|
|
209
|
+
|
|
210
|
+
## `stash` — quick save
|
|
211
|
+
|
|
212
|
+
Interrupting to switch branches?
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
git stash push -m "WIP: auth refactor"
|
|
216
|
+
git switch other-branch
|
|
217
|
+
|
|
218
|
+
# later
|
|
219
|
+
git switch original-branch
|
|
220
|
+
git stash pop # reapply and remove from stash
|
|
221
|
+
# or:
|
|
222
|
+
git stash apply stash@{0} # reapply but keep in stash
|
|
223
|
+
git stash list
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Stash names help when you accumulate several. Don't let stashes pile up — they're not backup.
|
|
227
|
+
|
|
228
|
+
## Cherry-pick
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
git cherry-pick <sha>
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Copy a commit from another branch. Handy for:
|
|
235
|
+
- Backporting a fix to a release branch.
|
|
236
|
+
- Salvaging one commit from an abandoned branch.
|
|
237
|
+
|
|
238
|
+
Avoid using cherry-pick as a regular integration strategy; it creates duplicate work in the history.
|
|
239
|
+
|
|
240
|
+
## Worktrees — parallel branches
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
git worktree add ../myrepo-release release/v2
|
|
244
|
+
# work in both directories simultaneously
|
|
245
|
+
git worktree remove ../myrepo-release
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
One clone, multiple branches checked out in separate directories. Useful for long-running release branches, bisect, comparison.
|
|
249
|
+
|
|
250
|
+
## `bisect` — find the commit that broke things
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
git bisect start
|
|
254
|
+
git bisect bad # current is broken
|
|
255
|
+
git bisect good v1.2.0 # this tag was fine
|
|
256
|
+
|
|
257
|
+
# git checks out a commit in the middle; test it
|
|
258
|
+
git bisect good # or bisect bad
|
|
259
|
+
# repeat until git identifies the culprit
|
|
260
|
+
git bisect reset
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
`git bisect run ./test.sh` automates it — git runs your script at each step.
|
|
264
|
+
|
|
265
|
+
## `.gitignore` discipline
|
|
266
|
+
|
|
267
|
+
Keep it current. Default entries:
|
|
268
|
+
|
|
269
|
+
```
|
|
270
|
+
# dependencies
|
|
271
|
+
node_modules/
|
|
272
|
+
.venv/
|
|
273
|
+
target/
|
|
274
|
+
|
|
275
|
+
# env / secrets
|
|
276
|
+
.env
|
|
277
|
+
.env.local
|
|
278
|
+
*.pem
|
|
279
|
+
*.key
|
|
280
|
+
|
|
281
|
+
# IDE / OS
|
|
282
|
+
.idea/
|
|
283
|
+
.vscode/
|
|
284
|
+
*.swp
|
|
285
|
+
.DS_Store
|
|
286
|
+
|
|
287
|
+
# build
|
|
288
|
+
dist/
|
|
289
|
+
build/
|
|
290
|
+
*.pyc
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
If a file was committed by accident, ignoring it doesn't remove it — use `git rm --cached <file>` + commit.
|
|
294
|
+
|
|
295
|
+
## `.gitattributes` — normalize line endings
|
|
296
|
+
|
|
297
|
+
Avoid CRLF / LF chaos across Windows + Mac + Linux:
|
|
298
|
+
|
|
299
|
+
```
|
|
300
|
+
* text=auto eol=lf
|
|
301
|
+
*.sh text eol=lf
|
|
302
|
+
*.bat text eol=crlf
|
|
303
|
+
*.png binary
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
Commit it; once everyone has it, cross-OS diffs go quiet.
|
|
307
|
+
|
|
308
|
+
## Git hooks — local enforcement
|
|
309
|
+
|
|
310
|
+
- **pre-commit** — run linter / formatter / tests before a commit is allowed.
|
|
311
|
+
- **commit-msg** — enforce commit message format.
|
|
312
|
+
- **pre-push** — run tests before push.
|
|
313
|
+
|
|
314
|
+
Use a hook manager (`lefthook`, `husky`, `pre-commit`) so hooks are in the repo, shared with the team.
|
|
315
|
+
|
|
316
|
+
```yaml
|
|
317
|
+
# lefthook.yml
|
|
318
|
+
pre-commit:
|
|
319
|
+
parallel: true
|
|
320
|
+
commands:
|
|
321
|
+
lint: { run: pnpm lint }
|
|
322
|
+
format-check: { run: pnpm format:check }
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
Don't `--no-verify` your hooks. If a hook fails, fix the underlying issue.
|
|
326
|
+
|
|
327
|
+
## Recovery cheat-sheet
|
|
328
|
+
|
|
329
|
+
| "I accidentally..." | Fix |
|
|
330
|
+
|---|---|
|
|
331
|
+
| Committed to wrong branch | `git reset --soft HEAD~1`, switch branches, recommit |
|
|
332
|
+
| Deleted a branch | `git reflog` → find SHA → `git branch <name> <sha>` |
|
|
333
|
+
| Force-pushed over someone | Coordinate; `git reflog` on their machine can recover |
|
|
334
|
+
| Committed a secret | Rotate the secret immediately; history rewrite is secondary |
|
|
335
|
+
| Lost uncommitted changes | `git reflog` only helps if committed; stashes via `git stash list` |
|
|
336
|
+
| Merged wrong branch | `git reset --hard HEAD~1` (before push), or `git revert` (after push) |
|
|
337
|
+
| Detached HEAD | `git branch <newname>` before switching away |
|
|
338
|
+
|
|
339
|
+
## Forbidden patterns
|
|
340
|
+
|
|
341
|
+
- `git push --force` (without `--force-with-lease`)
|
|
342
|
+
- `--no-verify` to skip hooks because "they're annoying"
|
|
343
|
+
- 20+ `WIP` / `fix` commits pushed to main (squash them)
|
|
344
|
+
- Long-lived feature branches (weeks+) without merging / rebasing
|
|
345
|
+
- Editing `.gitignore` without committing the removals that `.gitignore` was meant to catch
|
|
346
|
+
- Rebasing a shared branch
|
|
347
|
+
- Committing secrets (even if planning to remove next commit)
|
|
348
|
+
- `git add -A` without review
|
|
349
|
+
- Keeping personal branches around in the remote for months
|
|
350
|
+
- Mixing rename + content change in one commit (hides the rename)
|
|
351
|
+
|
|
352
|
+
## Pair with
|
|
353
|
+
|
|
354
|
+
- [`coding-standards/references/commits-and-prs.md`](../coding-standards/references/commits-and-prs.md) — commit messages + PR sizing.
|
|
355
|
+
- [`devops/references/ci-cd.md`](../devops/references/ci-cd.md) — branch protection + CI triggers.
|