swe-workflow-skills 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +144 -0
- package/VERSION +1 -0
- package/bin/cli.mjs +44 -0
- package/catalog.json +225 -0
- package/commands/role.md +34 -0
- package/hooks/session-start.mjs +118 -0
- package/install.mjs +318 -0
- package/package.json +47 -0
- package/roles.json +194 -0
- package/scripts/resolve.mjs +296 -0
- package/skills/accessibility-design/SKILL.md +128 -0
- package/skills/accessibility-design/evals/evals.json +51 -0
- package/skills/accessibility-design/references/accessibility-patterns.md +321 -0
- package/skills/api-design/SKILL.md +144 -0
- package/skills/api-design/evals/evals.json +48 -0
- package/skills/api-design/references/rest-conventions.md +147 -0
- package/skills/api-design/templates/api-spec.md +156 -0
- package/skills/architecture-design/SKILL.md +86 -0
- package/skills/architecture-design/evals/evals.json +44 -0
- package/skills/architecture-design/references/clean-architecture.md +147 -0
- package/skills/architecture-design/references/component-principles.md +129 -0
- package/skills/architecture-design/references/principles.md +81 -0
- package/skills/architecture-design/references/solid-principles.md +106 -0
- package/skills/architecture-design/templates/adr.md +63 -0
- package/skills/architecture-documentation/SKILL.md +126 -0
- package/skills/architecture-documentation/evals/evals.json +44 -0
- package/skills/architecture-documentation/references/abstraction-levels.md +91 -0
- package/skills/architecture-documentation/references/diagram-tooling.md +141 -0
- package/skills/architecture-documentation/templates/architecture-doc.md +171 -0
- package/skills/bug-investigating/SKILL.md +133 -0
- package/skills/bug-investigating/evals/evals.json +56 -0
- package/skills/bug-investigating/references/common-bugs.md +62 -0
- package/skills/bug-investigating/references/debugging-patterns.md +94 -0
- package/skills/cicd-pipeline/SKILL.md +87 -0
- package/skills/cicd-pipeline/evals/evals.json +34 -0
- package/skills/cicd-pipeline/references/pipeline-patterns.md +206 -0
- package/skills/cicd-pipeline/templates/github-actions.md +206 -0
- package/skills/code-reviewing/SKILL.md +92 -0
- package/skills/code-reviewing/evals/evals.json +62 -0
- package/skills/code-reviewing/references/error-handling.md +108 -0
- package/skills/code-reviewing/references/review-checklist.md +144 -0
- package/skills/configuration-strategy/SKILL.md +109 -0
- package/skills/configuration-strategy/evals/evals.json +41 -0
- package/skills/configuration-strategy/references/config-patterns.md +161 -0
- package/skills/containerization/SKILL.md +90 -0
- package/skills/containerization/evals/evals.json +36 -0
- package/skills/containerization/references/dockerfile-patterns.md +168 -0
- package/skills/containerization/templates/dockerfile.md +154 -0
- package/skills/data-modeling/SKILL.md +83 -0
- package/skills/data-modeling/evals/evals.json +43 -0
- package/skills/data-modeling/references/conventions.md +57 -0
- package/skills/data-modeling/templates/schema.md +120 -0
- package/skills/dependency-impact-analysis/SKILL.md +113 -0
- package/skills/dependency-impact-analysis/evals/evals.json +41 -0
- package/skills/dependency-impact-analysis/references/impact-patterns.md +150 -0
- package/skills/dependency-management/SKILL.md +161 -0
- package/skills/dependency-management/evals/evals.json +48 -0
- package/skills/dependency-management/references/evaluation-checklist.md +127 -0
- package/skills/deployment-checklist/SKILL.md +132 -0
- package/skills/deployment-checklist/evals/evals.json +58 -0
- package/skills/deployment-checklist/references/pre-deploy-gates.md +91 -0
- package/skills/deployment-repo/SKILL.md +190 -0
- package/skills/deployment-repo/evals/evals.json +44 -0
- package/skills/deployment-repo/references/deployment-repo-patterns.md +258 -0
- package/skills/deployment-repo/references/version-compatibility.md +227 -0
- package/skills/deployment-repo/templates/deployment-repo-structure.md +226 -0
- package/skills/effort-estimation/SKILL.md +95 -0
- package/skills/effort-estimation/evals/evals.json +32 -0
- package/skills/effort-estimation/references/estimation-methods.md +154 -0
- package/skills/feature-planning/SKILL.md +84 -0
- package/skills/feature-planning/evals/evals.json +44 -0
- package/skills/feature-planning/templates/plan.md +55 -0
- package/skills/feature-planning/templates/task.md +26 -0
- package/skills/frontend-architecture/SKILL.md +154 -0
- package/skills/frontend-architecture/evals/evals.json +34 -0
- package/skills/frontend-architecture/references/component-patterns.md +264 -0
- package/skills/frontend-architecture/templates/folder-structure.md +203 -0
- package/skills/git-workflow/SKILL.md +127 -0
- package/skills/git-workflow/evals/evals.json +44 -0
- package/skills/git-workflow/references/conventions.md +142 -0
- package/skills/git-workflow/templates/pull-request.md +57 -0
- package/skills/gitops-delivery/SKILL.md +319 -0
- package/skills/gitops-delivery/evals/evals.json +44 -0
- package/skills/gitops-delivery/references/gitops-patterns.md +478 -0
- package/skills/gitops-delivery/templates/gitops-config.md +196 -0
- package/skills/incident-response/SKILL.md +130 -0
- package/skills/incident-response/evals/evals.json +55 -0
- package/skills/incident-response/references/communication-templates.md +134 -0
- package/skills/incident-response/references/severity-levels.md +77 -0
- package/skills/incident-response/templates/incident-timeline.md +57 -0
- package/skills/infrastructure-as-code/SKILL.md +86 -0
- package/skills/infrastructure-as-code/evals/evals.json +33 -0
- package/skills/infrastructure-as-code/references/iac-patterns.md +179 -0
- package/skills/infrastructure-as-code/templates/terraform-module.md +158 -0
- package/skills/metrics-and-okrs/SKILL.md +113 -0
- package/skills/metrics-and-okrs/evals/evals.json +33 -0
- package/skills/metrics-and-okrs/references/metric-catalog.md +90 -0
- package/skills/metrics-and-okrs/templates/okr-document.md +61 -0
- package/skills/ml-experiment-tracking/SKILL.md +96 -0
- package/skills/ml-experiment-tracking/evals/evals.json +32 -0
- package/skills/ml-experiment-tracking/references/tracking-tools.md +127 -0
- package/skills/ml-model-deployment/SKILL.md +106 -0
- package/skills/ml-model-deployment/evals/evals.json +34 -0
- package/skills/ml-model-deployment/references/serving-patterns.md +162 -0
- package/skills/ml-pipeline-design/SKILL.md +162 -0
- package/skills/ml-pipeline-design/evals/evals.json +34 -0
- package/skills/ml-pipeline-design/references/pipeline-components.md +174 -0
- package/skills/observability-design/SKILL.md +162 -0
- package/skills/observability-design/evals/evals.json +52 -0
- package/skills/observability-design/references/logging-patterns.md +229 -0
- package/skills/observability-design/references/slo-framework.md +151 -0
- package/skills/observability-design/templates/slo-document.md +80 -0
- package/skills/performance-optimization/SKILL.md +83 -0
- package/skills/performance-optimization/evals/evals.json +47 -0
- package/skills/performance-optimization/references/bottleneck-patterns.md +256 -0
- package/skills/performance-optimization/references/concurrency.md +101 -0
- package/skills/prd-writing/SKILL.md +109 -0
- package/skills/prd-writing/evals/evals.json +33 -0
- package/skills/prd-writing/references/prd-examples.md +132 -0
- package/skills/prd-writing/templates/prd.md +71 -0
- package/skills/prd-writing/templates/rfc.md +79 -0
- package/skills/project-documentation/SKILL.md +104 -0
- package/skills/project-documentation/evals/evals.json +48 -0
- package/skills/project-documentation/references/contributing-guide.md +100 -0
- package/skills/project-documentation/templates/changelog.md +59 -0
- package/skills/project-documentation/templates/readme.md +121 -0
- package/skills/project-proposal/SKILL.md +90 -0
- package/skills/project-proposal/evals/evals.json +31 -0
- package/skills/project-proposal/references/proposal-examples.md +140 -0
- package/skills/project-proposal/templates/proposal.md +76 -0
- package/skills/project-review/SKILL.md +111 -0
- package/skills/project-review/evals/evals.json +42 -0
- package/skills/refactoring/SKILL.md +90 -0
- package/skills/refactoring/evals/evals.json +44 -0
- package/skills/refactoring/references/transformations.md +132 -0
- package/skills/retrospective/SKILL.md +157 -0
- package/skills/retrospective/evals/evals.json +34 -0
- package/skills/retrospective/references/facilitation-guide.md +130 -0
- package/skills/retrospective/templates/post-mortem.md +121 -0
- package/skills/rollback-strategy/SKILL.md +116 -0
- package/skills/rollback-strategy/evals/evals.json +55 -0
- package/skills/rollback-strategy/references/rollback-patterns.md +125 -0
- package/skills/rollback-strategy/templates/rollback-plan.md +69 -0
- package/skills/security-audit/SKILL.md +146 -0
- package/skills/security-audit/evals/evals.json +66 -0
- package/skills/security-audit/references/owasp-top-10.md +167 -0
- package/skills/security-audit/templates/security-report.md +79 -0
- package/skills/skill-router/SKILL.md +171 -0
- package/skills/skill-router/evals/evals.json +39 -0
- package/skills/strategic-review/SKILL.md +106 -0
- package/skills/strategic-review/evals/evals.json +42 -0
- package/skills/strategic-review/templates/full-review-prompt.md +84 -0
- package/skills/tdd-workflow/SKILL.md +147 -0
- package/skills/tdd-workflow/evals/evals.json +58 -0
- package/skills/tdd-workflow/references/test-quality.md +71 -0
- package/skills/technical-debt-review/SKILL.md +113 -0
- package/skills/technical-debt-review/evals/evals.json +41 -0
- package/skills/technical-debt-review/references/debt-taxonomy.md +124 -0
- package/skills/technical-debt-review/templates/debt-audit.md +85 -0
- package/skills/test-data-strategy/SKILL.md +129 -0
- package/skills/test-data-strategy/evals/evals.json +49 -0
- package/skills/test-data-strategy/references/data-generation-patterns.md +341 -0
- package/skills/test-suite-design/SKILL.md +137 -0
- package/skills/test-suite-design/evals/evals.json +69 -0
- package/skills/test-suite-design/references/test-infrastructure.md +175 -0
- package/skills/test-suite-design/references/testing-pyramid.md +140 -0
- package/skills/ui-ux-design/SKILL.md +117 -0
- package/skills/ui-ux-design/evals/evals.json +35 -0
- package/skills/ui-ux-design/references/interaction-patterns.md +145 -0
- package/skills/ui-ux-design/templates/screen-spec.md +97 -0
- package/skills/verification-before-completion/SKILL.md +85 -0
- package/skills/verification-before-completion/evals/evals.json +53 -0
- package/skills/writing-skills/SKILL.md +87 -0
- package/skills/writing-skills/evals/evals.json +41 -0
- package/skills/writing-skills/references/pressure-testing.md +69 -0
- package/uninstall.mjs +182 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# Pipeline Patterns
|
|
2
|
+
|
|
3
|
+
## Contents
|
|
4
|
+
- GitHub Actions patterns
|
|
5
|
+
- GitLab CI patterns
|
|
6
|
+
- General anti-patterns
|
|
7
|
+
|
|
8
|
+
## GitHub Actions: Full PR + Deploy Pipeline
|
|
9
|
+
|
|
10
|
+
```yaml
|
|
11
|
+
name: CI/CD
|
|
12
|
+
|
|
13
|
+
on:
|
|
14
|
+
push:
|
|
15
|
+
branches: [main]
|
|
16
|
+
pull_request:
|
|
17
|
+
branches: [main]
|
|
18
|
+
|
|
19
|
+
concurrency:
|
|
20
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
21
|
+
cancel-in-progress: true # Cancel outdated runs on same branch
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
lint:
|
|
25
|
+
runs-on: ubuntu-latest
|
|
26
|
+
timeout-minutes: 5
|
|
27
|
+
steps:
|
|
28
|
+
- uses: actions/checkout@v4
|
|
29
|
+
- uses: actions/setup-node@v4
|
|
30
|
+
with:
|
|
31
|
+
node-version: '20'
|
|
32
|
+
cache: 'npm'
|
|
33
|
+
- run: npm ci
|
|
34
|
+
- run: npm run lint
|
|
35
|
+
|
|
36
|
+
test:
|
|
37
|
+
runs-on: ubuntu-latest
|
|
38
|
+
timeout-minutes: 10
|
|
39
|
+
services:
|
|
40
|
+
postgres:
|
|
41
|
+
image: postgres:16-alpine
|
|
42
|
+
env:
|
|
43
|
+
POSTGRES_USER: test
|
|
44
|
+
POSTGRES_PASSWORD: test
|
|
45
|
+
POSTGRES_DB: testdb
|
|
46
|
+
ports: ['5432:5432']
|
|
47
|
+
options: >-
|
|
48
|
+
--health-cmd="pg_isready -U test"
|
|
49
|
+
--health-interval=5s
|
|
50
|
+
--health-timeout=3s
|
|
51
|
+
--health-retries=5
|
|
52
|
+
steps:
|
|
53
|
+
- uses: actions/checkout@v4
|
|
54
|
+
- uses: actions/setup-node@v4
|
|
55
|
+
with:
|
|
56
|
+
node-version: '20'
|
|
57
|
+
cache: 'npm'
|
|
58
|
+
- run: npm ci
|
|
59
|
+
- run: npm test
|
|
60
|
+
env:
|
|
61
|
+
DATABASE_URL: postgresql://test:test@localhost:5432/testdb
|
|
62
|
+
- uses: actions/upload-artifact@v4
|
|
63
|
+
if: always()
|
|
64
|
+
with:
|
|
65
|
+
name: coverage-report
|
|
66
|
+
path: coverage/
|
|
67
|
+
|
|
68
|
+
build:
|
|
69
|
+
needs: [lint, test]
|
|
70
|
+
runs-on: ubuntu-latest
|
|
71
|
+
timeout-minutes: 10
|
|
72
|
+
steps:
|
|
73
|
+
- uses: actions/checkout@v4
|
|
74
|
+
- uses: docker/setup-buildx-action@v3
|
|
75
|
+
- uses: docker/login-action@v3
|
|
76
|
+
with:
|
|
77
|
+
registry: ghcr.io
|
|
78
|
+
username: ${{ github.actor }}
|
|
79
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
80
|
+
- uses: docker/build-push-action@v5
|
|
81
|
+
with:
|
|
82
|
+
push: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
|
|
83
|
+
tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
|
|
84
|
+
cache-from: type=gha
|
|
85
|
+
cache-to: type=gha,mode=max
|
|
86
|
+
|
|
87
|
+
deploy-staging:
|
|
88
|
+
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
|
89
|
+
needs: [build]
|
|
90
|
+
runs-on: ubuntu-latest
|
|
91
|
+
environment: staging
|
|
92
|
+
timeout-minutes: 10
|
|
93
|
+
steps:
|
|
94
|
+
- name: Deploy to staging
|
|
95
|
+
run: echo "Deploy image ghcr.io/${{ github.repository }}:${{ github.sha }}"
|
|
96
|
+
- name: Smoke test
|
|
97
|
+
run: |
|
|
98
|
+
sleep 10
|
|
99
|
+
curl --fail https://staging.example.com/health
|
|
100
|
+
|
|
101
|
+
deploy-production:
|
|
102
|
+
needs: [deploy-staging]
|
|
103
|
+
runs-on: ubuntu-latest
|
|
104
|
+
environment:
|
|
105
|
+
name: production
|
|
106
|
+
url: https://example.com
|
|
107
|
+
timeout-minutes: 10
|
|
108
|
+
steps:
|
|
109
|
+
- name: Deploy to production
|
|
110
|
+
run: echo "Deploy image ghcr.io/${{ github.repository }}:${{ github.sha }}"
|
|
111
|
+
- name: Smoke test
|
|
112
|
+
run: curl --fail https://example.com/health
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## GitLab CI: Full Pipeline
|
|
116
|
+
|
|
117
|
+
```yaml
|
|
118
|
+
stages:
|
|
119
|
+
- validate
|
|
120
|
+
- test
|
|
121
|
+
- build
|
|
122
|
+
- deploy
|
|
123
|
+
|
|
124
|
+
variables:
|
|
125
|
+
NODE_VERSION: "20"
|
|
126
|
+
|
|
127
|
+
default:
|
|
128
|
+
cache:
|
|
129
|
+
key: ${CI_COMMIT_REF_SLUG}
|
|
130
|
+
paths:
|
|
131
|
+
- node_modules/
|
|
132
|
+
|
|
133
|
+
lint:
|
|
134
|
+
stage: validate
|
|
135
|
+
image: node:${NODE_VERSION}-alpine
|
|
136
|
+
script:
|
|
137
|
+
- npm ci --cache .npm
|
|
138
|
+
- npm run lint
|
|
139
|
+
timeout: 5 minutes
|
|
140
|
+
|
|
141
|
+
test:
|
|
142
|
+
stage: test
|
|
143
|
+
image: node:${NODE_VERSION}-alpine
|
|
144
|
+
services:
|
|
145
|
+
- postgres:16-alpine
|
|
146
|
+
variables:
|
|
147
|
+
POSTGRES_USER: test
|
|
148
|
+
POSTGRES_PASSWORD: test
|
|
149
|
+
POSTGRES_DB: testdb
|
|
150
|
+
DATABASE_URL: postgresql://test:test@postgres:5432/testdb
|
|
151
|
+
script:
|
|
152
|
+
- npm ci --cache .npm
|
|
153
|
+
- npm test
|
|
154
|
+
coverage: '/Statements\s*:\s*(\d+\.?\d*)%/'
|
|
155
|
+
artifacts:
|
|
156
|
+
reports:
|
|
157
|
+
coverage_report:
|
|
158
|
+
coverage_format: cobertura
|
|
159
|
+
path: coverage/cobertura-coverage.xml
|
|
160
|
+
|
|
161
|
+
build:
|
|
162
|
+
stage: build
|
|
163
|
+
image: docker:24
|
|
164
|
+
services:
|
|
165
|
+
- docker:24-dind
|
|
166
|
+
script:
|
|
167
|
+
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
|
|
168
|
+
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
|
|
169
|
+
only:
|
|
170
|
+
- main
|
|
171
|
+
|
|
172
|
+
deploy-staging:
|
|
173
|
+
stage: deploy
|
|
174
|
+
environment:
|
|
175
|
+
name: staging
|
|
176
|
+
url: https://staging.example.com
|
|
177
|
+
script:
|
|
178
|
+
- echo "Deploy $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA to staging"
|
|
179
|
+
only:
|
|
180
|
+
- main
|
|
181
|
+
|
|
182
|
+
deploy-production:
|
|
183
|
+
stage: deploy
|
|
184
|
+
environment:
|
|
185
|
+
name: production
|
|
186
|
+
url: https://example.com
|
|
187
|
+
script:
|
|
188
|
+
- echo "Deploy $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA to production"
|
|
189
|
+
when: manual # Require manual approval
|
|
190
|
+
only:
|
|
191
|
+
- main
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## General Anti-patterns
|
|
195
|
+
|
|
196
|
+
**No caching**: Installing dependencies from scratch on every run wastes 2-5 minutes. Always cache package manager artifacts.
|
|
197
|
+
|
|
198
|
+
**Testing in production only**: If the pipeline only deploys, bugs reach users. Every pipeline needs at minimum: lint + test + build before deploy.
|
|
199
|
+
|
|
200
|
+
**Long-running monolith pipeline**: A 45-minute pipeline that runs everything sequentially kills developer productivity. Parallelize independent jobs. Target: green in under 10 minutes for PR checks.
|
|
201
|
+
|
|
202
|
+
**Secrets in pipeline logs**: Use masking. Never `echo $SECRET` or pass secrets as command-line arguments (visible in process lists). Use environment variables and ensure the CI platform masks them.
|
|
203
|
+
|
|
204
|
+
**No concurrency control**: Without concurrency groups, pushing twice quickly queues two full pipeline runs. Cancel outdated runs on the same branch.
|
|
205
|
+
|
|
206
|
+
**Deploy without smoke test**: A successful deploy step doesn't mean the app works. Always follow deploys with an automated health check.
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# CI/CD Pipeline Templates
|
|
2
|
+
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
## GitHub Actions — PR Validation
|
|
6
|
+
|
|
7
|
+
`.github/workflows/ci.yml`
|
|
8
|
+
|
|
9
|
+
```yaml
|
|
10
|
+
name: CI
|
|
11
|
+
|
|
12
|
+
on:
|
|
13
|
+
pull_request:
|
|
14
|
+
branches: [main]
|
|
15
|
+
push:
|
|
16
|
+
branches: [main]
|
|
17
|
+
|
|
18
|
+
# Cancel in-progress runs on new push to the same PR/branch
|
|
19
|
+
concurrency:
|
|
20
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
21
|
+
cancel-in-progress: true
|
|
22
|
+
|
|
23
|
+
jobs:
|
|
24
|
+
validate:
|
|
25
|
+
name: Lint, Test, Build
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
timeout-minutes: 15
|
|
28
|
+
|
|
29
|
+
steps:
|
|
30
|
+
- uses: actions/checkout@v4
|
|
31
|
+
|
|
32
|
+
- name: Set up Node.js
|
|
33
|
+
uses: actions/setup-node@v4
|
|
34
|
+
with:
|
|
35
|
+
node-version: "20"
|
|
36
|
+
cache: "npm" # Caches node_modules between runs
|
|
37
|
+
|
|
38
|
+
- name: Install dependencies
|
|
39
|
+
run: npm ci
|
|
40
|
+
|
|
41
|
+
- name: Lint
|
|
42
|
+
run: npm run lint
|
|
43
|
+
|
|
44
|
+
- name: Type check
|
|
45
|
+
run: npm run typecheck
|
|
46
|
+
|
|
47
|
+
- name: Unit tests
|
|
48
|
+
run: npm test -- --coverage
|
|
49
|
+
|
|
50
|
+
- name: Build
|
|
51
|
+
run: npm run build
|
|
52
|
+
|
|
53
|
+
# Upload coverage to your coverage service (Codecov, Coveralls, etc.)
|
|
54
|
+
# - uses: codecov/codecov-action@v4
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## GitHub Actions — Production Deploy
|
|
60
|
+
|
|
61
|
+
`.github/workflows/deploy.yml`
|
|
62
|
+
|
|
63
|
+
```yaml
|
|
64
|
+
name: Deploy to Production
|
|
65
|
+
|
|
66
|
+
on:
|
|
67
|
+
push:
|
|
68
|
+
branches: [main]
|
|
69
|
+
|
|
70
|
+
concurrency:
|
|
71
|
+
group: deploy-production
|
|
72
|
+
cancel-in-progress: false # Never cancel a deploy in progress
|
|
73
|
+
|
|
74
|
+
jobs:
|
|
75
|
+
test:
|
|
76
|
+
name: Tests
|
|
77
|
+
runs-on: ubuntu-latest
|
|
78
|
+
timeout-minutes: 15
|
|
79
|
+
steps:
|
|
80
|
+
- uses: actions/checkout@v4
|
|
81
|
+
- uses: actions/setup-node@v4
|
|
82
|
+
with:
|
|
83
|
+
node-version: "20"
|
|
84
|
+
cache: "npm"
|
|
85
|
+
- run: npm ci
|
|
86
|
+
- run: npm test
|
|
87
|
+
|
|
88
|
+
build:
|
|
89
|
+
name: Build & Push Image
|
|
90
|
+
needs: test
|
|
91
|
+
runs-on: ubuntu-latest
|
|
92
|
+
timeout-minutes: 20
|
|
93
|
+
outputs:
|
|
94
|
+
image-tag: ${{ steps.meta.outputs.tags }}
|
|
95
|
+
steps:
|
|
96
|
+
- uses: actions/checkout@v4
|
|
97
|
+
|
|
98
|
+
- name: Docker metadata
|
|
99
|
+
id: meta
|
|
100
|
+
uses: docker/metadata-action@v5
|
|
101
|
+
with:
|
|
102
|
+
images: ghcr.io/${{ github.repository }}
|
|
103
|
+
tags: |
|
|
104
|
+
type=sha,prefix=sha-
|
|
105
|
+
|
|
106
|
+
- name: Log in to registry
|
|
107
|
+
uses: docker/login-action@v3
|
|
108
|
+
with:
|
|
109
|
+
registry: ghcr.io
|
|
110
|
+
username: ${{ github.actor }}
|
|
111
|
+
password: ${{ secrets.GITHUB_TOKEN }}
|
|
112
|
+
|
|
113
|
+
- name: Build and push
|
|
114
|
+
uses: docker/build-push-action@v5
|
|
115
|
+
with:
|
|
116
|
+
push: true
|
|
117
|
+
tags: ${{ steps.meta.outputs.tags }}
|
|
118
|
+
cache-from: type=gha
|
|
119
|
+
cache-to: type=gha,mode=max
|
|
120
|
+
|
|
121
|
+
deploy:
|
|
122
|
+
name: Deploy
|
|
123
|
+
needs: build
|
|
124
|
+
runs-on: ubuntu-latest
|
|
125
|
+
timeout-minutes: 20
|
|
126
|
+
environment: production # Requires manual approval gate in GitHub environment settings
|
|
127
|
+
steps:
|
|
128
|
+
- name: Deploy
|
|
129
|
+
env:
|
|
130
|
+
IMAGE_TAG: ${{ needs.build.outputs.image-tag }}
|
|
131
|
+
DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
|
|
132
|
+
run: |
|
|
133
|
+
# Replace with your deploy command: kubectl, fly deploy, aws ecs update-service, etc.
|
|
134
|
+
echo "Deploying $IMAGE_TAG"
|
|
135
|
+
|
|
136
|
+
- name: Smoke test
|
|
137
|
+
run: |
|
|
138
|
+
# Verify the deployment is healthy before declaring success
|
|
139
|
+
curl --fail --retry 5 --retry-delay 5 https://your-app.example.com/health
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## GitLab CI Equivalent
|
|
145
|
+
|
|
146
|
+
`.gitlab-ci.yml`
|
|
147
|
+
|
|
148
|
+
```yaml
|
|
149
|
+
stages:
|
|
150
|
+
- test
|
|
151
|
+
- build
|
|
152
|
+
- deploy
|
|
153
|
+
|
|
154
|
+
variables:
|
|
155
|
+
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
|
|
156
|
+
|
|
157
|
+
# Cache dependencies across jobs in the same pipeline
|
|
158
|
+
cache:
|
|
159
|
+
key:
|
|
160
|
+
files:
|
|
161
|
+
- package-lock.json
|
|
162
|
+
paths:
|
|
163
|
+
- node_modules/
|
|
164
|
+
|
|
165
|
+
test:
|
|
166
|
+
stage: test
|
|
167
|
+
image: node:20-alpine
|
|
168
|
+
script:
|
|
169
|
+
- npm ci
|
|
170
|
+
- npm run lint
|
|
171
|
+
- npm test
|
|
172
|
+
coverage: '/Lines\s*:\s*(\d+\.?\d*)%/' # Regex to parse coverage from test output
|
|
173
|
+
|
|
174
|
+
build:
|
|
175
|
+
stage: build
|
|
176
|
+
image: docker:24
|
|
177
|
+
services:
|
|
178
|
+
- docker:24-dind
|
|
179
|
+
script:
|
|
180
|
+
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
|
181
|
+
- docker build -t $IMAGE_TAG .
|
|
182
|
+
- docker push $IMAGE_TAG
|
|
183
|
+
only:
|
|
184
|
+
- main
|
|
185
|
+
|
|
186
|
+
deploy_production:
|
|
187
|
+
stage: deploy
|
|
188
|
+
environment:
|
|
189
|
+
name: production
|
|
190
|
+
url: https://your-app.example.com
|
|
191
|
+
script:
|
|
192
|
+
- echo "Deploy $IMAGE_TAG to production"
|
|
193
|
+
# kubectl set image deployment/app app=$IMAGE_TAG
|
|
194
|
+
when: manual # Require manual trigger for production
|
|
195
|
+
only:
|
|
196
|
+
- main
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
> **Key practices baked into these templates:**
|
|
202
|
+
> - `concurrency` cancels stale CI runs but never cancels in-progress deploys
|
|
203
|
+
> - Secrets are scoped to jobs that need them via GitHub environments
|
|
204
|
+
> - Actions pinned to `@v4` tags (replace with SHA pins for stronger supply-chain security)
|
|
205
|
+
> - `cache: "npm"` / GitLab `cache` reduces install time by 60–80% after first run
|
|
206
|
+
> - Smoke test after deploy catches broken deployments before users do
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: code-reviewing
|
|
3
|
+
description: "Structured code reviews enforcing DRY, KISS, YAGNI, SRP, best practices, and project conventions. Triggers: review this code, code review, check my code, what do you think of this implementation, review this PR, is this code good, feedback on my code, review staged changes before commit."
|
|
4
|
+
model: sonnet
|
|
5
|
+
allowed-tools: Read, Grep, Glob, Write, Edit
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Code Reviewing
|
|
9
|
+
|
|
10
|
+
Perform thorough, constructive code reviews that catch bugs, enforce principles, and improve code quality. Good reviews are specific, actionable, and educational — they explain *why*, not just *what*.
|
|
11
|
+
|
|
12
|
+
## Review Process
|
|
13
|
+
|
|
14
|
+
### Step 1: Understand the Context
|
|
15
|
+
|
|
16
|
+
Before reviewing line-by-line, understand the big picture:
|
|
17
|
+
|
|
18
|
+
- **What is this code supposed to do?** Read the PR description, linked issue, or ask the user.
|
|
19
|
+
- **What changed?** If reviewing a diff, understand the scope of changes.
|
|
20
|
+
- **What's the surrounding code like?** Read adjacent files for conventions and patterns.
|
|
21
|
+
|
|
22
|
+
### Step 2: First Pass — Structural Review
|
|
23
|
+
|
|
24
|
+
Look at the forest before the trees. Check:
|
|
25
|
+
|
|
26
|
+
- **Does the change belong here?** Is it in the right module/file?
|
|
27
|
+
- **Is the scope right?** Does it do one thing or is it mixing concerns?
|
|
28
|
+
- **Is the approach sound?** Before nitpicking syntax, is the overall strategy correct?
|
|
29
|
+
- **Are there missing pieces?** Tests? Documentation? Error handling? Migration?
|
|
30
|
+
|
|
31
|
+
### Step 3: Detailed Review
|
|
32
|
+
|
|
33
|
+
Review the code against these categories, in order of importance. See [references/review-checklist.md](references/review-checklist.md) for the detailed checklist.
|
|
34
|
+
|
|
35
|
+
**Severity levels for findings:**
|
|
36
|
+
|
|
37
|
+
- 🔴 **Blocker**: Must fix before merge (bugs, security issues, data loss risks)
|
|
38
|
+
- 🟡 **Suggestion**: Should fix, significantly improves quality (principle violations, missing tests)
|
|
39
|
+
- 🔵 **Nit**: Optional improvement (naming, style, minor simplification)
|
|
40
|
+
|
|
41
|
+
### Step 4: Present Findings
|
|
42
|
+
|
|
43
|
+
Structure your review as:
|
|
44
|
+
|
|
45
|
+
1. **Summary** (1-2 sentences: overall impression and most important finding)
|
|
46
|
+
2. **Blockers** (if any — these must be addressed)
|
|
47
|
+
3. **Suggestions** (improvements that meaningfully raise quality)
|
|
48
|
+
4. **Nits** (optional, non-blocking)
|
|
49
|
+
5. **Positive notes** (what's done well — this matters for morale and learning)
|
|
50
|
+
|
|
51
|
+
For each finding, provide:
|
|
52
|
+
- The specific location (file and line/function)
|
|
53
|
+
- What the issue is
|
|
54
|
+
- Why it matters (link to principle)
|
|
55
|
+
- A concrete suggestion for fixing it (show code when helpful)
|
|
56
|
+
|
|
57
|
+
### Step 5: Offer to Help Fix
|
|
58
|
+
|
|
59
|
+
After presenting findings, offer to help implement the suggested changes. Don't just criticize — help improve.
|
|
60
|
+
|
|
61
|
+
## Review Dimensions
|
|
62
|
+
|
|
63
|
+
These are the lenses through which code is examined:
|
|
64
|
+
|
|
65
|
+
**Correctness**: Does it work? Does it handle edge cases? Can it fail silently?
|
|
66
|
+
|
|
67
|
+
**Design Principles**: DRY, KISS, YAGNI, SRP, functional independence. See [references/review-checklist.md](references/review-checklist.md).
|
|
68
|
+
|
|
69
|
+
**Security**: Input validation, auth checks, SQL injection, XSS, secrets in code.
|
|
70
|
+
|
|
71
|
+
**Performance**: Obvious N+1 queries, unnecessary allocations, missing indexes. Don't optimize prematurely, but flag clearly wasteful patterns.
|
|
72
|
+
|
|
73
|
+
**Testability**: Is the code testable? Are there tests? Do tests test behavior or implementation?
|
|
74
|
+
|
|
75
|
+
**Readability**: Can someone unfamiliar with this code understand it in one reading? Good naming, appropriate comments (why, not what), manageable function length.
|
|
76
|
+
|
|
77
|
+
**Error Handling**: Are errors caught, logged, and handled appropriately? Are error messages helpful? See [references/error-handling.md](references/error-handling.md) for detailed patterns (null safety, exception context, caller-oriented exceptions).
|
|
78
|
+
|
|
79
|
+
## Tone Guidelines
|
|
80
|
+
|
|
81
|
+
- Be specific: "This function does three things" beats "this could be cleaner"
|
|
82
|
+
- Be constructive: "Consider extracting X into its own function because..." beats "this is messy"
|
|
83
|
+
- Ask questions when uncertain: "Is this intentionally returning null here?" invites discussion
|
|
84
|
+
- Acknowledge good work: If something is well-written, say so
|
|
85
|
+
- Propose, don't command: "What do you think about..." respects the author's judgment
|
|
86
|
+
- Apply the Boy Scout Rule: note small cleanup opportunities near the changed code — a renamed variable, a dead import removed. These compound over time.
|
|
87
|
+
|
|
88
|
+
## Cross-Skill References
|
|
89
|
+
|
|
90
|
+
- `refactoring` — when the review surfaces code smells worth a structured cleanup
|
|
91
|
+
- `security-audit` — for a dedicated, deep security pass beyond the review checklist
|
|
92
|
+
- `verification-before-completion` — verify the change actually runs before approving, not from the diff alone
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_name": "code-reviewing",
|
|
3
|
+
"evals": [
|
|
4
|
+
{
|
|
5
|
+
"id": 1,
|
|
6
|
+
"prompt": "Review this Python function:\n\ndef process_order(order_data, db, email_service, logger):\n # Validate\n if not order_data.get('items'):\n return {'error': 'No items'}\n if not order_data.get('user_id'):\n return {'error': 'No user'}\n \n # Calculate total\n total = 0\n for item in order_data['items']:\n price = db.query(f\"SELECT price FROM products WHERE id = {item['product_id']}\")\n total += price * item['quantity']\n if item['quantity'] > 10:\n total -= price * item['quantity'] * 0.1\n \n # Save order\n order = db.query(f\"INSERT INTO orders (user_id, total) VALUES ({order_data['user_id']}, {total}) RETURNING id\")\n \n # Send email\n try:\n email_service.send(order_data['user_id'], f'Order {order.id} confirmed', f'Total: ${total}')\n except:\n logger.error('Email failed')\n \n return {'order_id': order.id, 'total': total}",
|
|
7
|
+
"expected_output": "Should identify SQL injection (blocker), SRP violation, bare except, missing transaction, N+1 query potential, and magic number 0.1. Should use severity levels and provide concrete fix suggestions.",
|
|
8
|
+
"assertions": [
|
|
9
|
+
"Identifies SQL injection as a blocker (string interpolation in SQL)",
|
|
10
|
+
"Flags SRP violation (validate + calculate + save + email in one function)",
|
|
11
|
+
"Catches the bare except clause",
|
|
12
|
+
"Notes the magic number 0.1 (discount percentage)",
|
|
13
|
+
"Suggests parameterized queries",
|
|
14
|
+
"Uses severity levels (blocker/suggestion/nit)",
|
|
15
|
+
"Provides concrete code suggestions for fixes",
|
|
16
|
+
"Notes something positive (if applicable)"
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"id": 2,
|
|
21
|
+
"prompt": "Can you review the changes in my last git commit?",
|
|
22
|
+
"expected_output": "Should read the git diff, understand context from surrounding files, and perform a structured review.",
|
|
23
|
+
"assertions": [
|
|
24
|
+
"Runs git diff or git show to read the changes",
|
|
25
|
+
"Reads surrounding code for context",
|
|
26
|
+
"Follows the structured review format (summary, blockers, suggestions, nits)",
|
|
27
|
+
"Provides specific file:line references for findings"
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"id": 3,
|
|
32
|
+
"prompt": "Review this TypeScript code:\n\nexport const UserService = {\n async getUser(id: string): Promise<any> {\n const user = await db.users.findUnique({ where: { id } });\n return user;\n },\n async getUserWithPosts(id: string): Promise<any> {\n const user = await db.users.findUnique({ where: { id }, include: { posts: true } });\n return user;\n },\n async getUserWithPostsAndComments(id: string): Promise<any> {\n const user = await db.users.findUnique({ where: { id }, include: { posts: { include: { comments: true } } } });\n return user;\n }\n};",
|
|
33
|
+
"expected_output": "Should flag DRY violation (three nearly identical functions), Promise<any> types, and suggest a more flexible approach (e.g., include options parameter). Should be constructive and suggest a concrete refactoring.",
|
|
34
|
+
"assertions": [
|
|
35
|
+
"Flags DRY violation across the three functions",
|
|
36
|
+
"Flags the use of any return type",
|
|
37
|
+
"Suggests consolidation with an options/includes parameter",
|
|
38
|
+
"Does not just say 'this is bad' — provides a refactored version",
|
|
39
|
+
"Notes that this might be a case for a single flexible query function",
|
|
40
|
+
"Applies KISS — suggested solution is not over-engineered"
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
],
|
|
44
|
+
"pressure_tests": [
|
|
45
|
+
{
|
|
46
|
+
"id": 1,
|
|
47
|
+
"prompt": "Review this new test file for our storage codec:\n\nimport { packRaw, unpackRaw } from '../rawjson.js';\ndescribe('raw_json codec', () => {\n it('round-trips a value', () => { expect(unpackRaw(packRaw('hi'))).toBe('hi'); });\n it('writes the gzip magic bytes', () => { expect(packRaw('hi')[0]).toBe(0x1f); expect(packRaw('hi')[1]).toBe(0x8b); });\n it('shrinks a big payload', () => { const big = 'x'.repeat(5000); expect(packRaw(big).length).toBeLessThan(big.length); });\n});\n\nLooks like solid coverage to me — anything to flag?",
|
|
48
|
+
"pressure": [
|
|
49
|
+
"looks_good",
|
|
50
|
+
"more_tests_is_better"
|
|
51
|
+
],
|
|
52
|
+
"expected_behavior": "Flags that two of the three tests re-verify zlib's own behavior rather than the project's logic: asserting the gzip magic bytes and that compression shrinks the payload are zlib's contract, covered by zlib's own tests, and add maintenance cost without testing our code. Recommends keeping the round-trip and adding tests for OUR logic (the legacy-fallback branches), not the library's.",
|
|
53
|
+
"assertions": [
|
|
54
|
+
"Flags the 'gzip magic bytes' test as re-verifying third-party (zlib) behavior",
|
|
55
|
+
"Flags the 'shrinks a big payload' test as testing zlib's compression, not our code",
|
|
56
|
+
"Does NOT praise the file as good coverage just because it has more tests",
|
|
57
|
+
"Keeps/endorses the round-trip test as legitimate own-logic coverage",
|
|
58
|
+
"Suggests testing our actual logic instead (e.g. the legacy fallback branches)"
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Error Handling Reference
|
|
2
|
+
|
|
3
|
+
## Contents
|
|
4
|
+
- Exceptions over error codes
|
|
5
|
+
- Don't return null
|
|
6
|
+
- Don't pass null
|
|
7
|
+
- Provide context in exceptions
|
|
8
|
+
- Define exceptions by caller's needs
|
|
9
|
+
- Never swallow exceptions
|
|
10
|
+
|
|
11
|
+
## Exceptions Over Error Codes
|
|
12
|
+
|
|
13
|
+
Prefer exceptions (or Result/Either types in functional languages) over returning error codes. Error codes force the caller to check the result immediately, cluttering the happy path with conditional logic.
|
|
14
|
+
|
|
15
|
+
**With error codes** (hard to read, easy to forget):
|
|
16
|
+
```
|
|
17
|
+
result = saveOrder(order)
|
|
18
|
+
if result == ERROR_VALIDATION:
|
|
19
|
+
handleValidation()
|
|
20
|
+
elif result == ERROR_DB:
|
|
21
|
+
handleDbError()
|
|
22
|
+
elif result == SUCCESS:
|
|
23
|
+
continue()
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**With exceptions** (clean happy path):
|
|
27
|
+
```
|
|
28
|
+
try:
|
|
29
|
+
saveOrder(order)
|
|
30
|
+
sendConfirmation(order)
|
|
31
|
+
except ValidationError as e:
|
|
32
|
+
handleValidation(e)
|
|
33
|
+
except DatabaseError as e:
|
|
34
|
+
handleDbError(e)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The exception approach separates the happy path from error handling, making both easier to read and maintain. The caller can also choose to let exceptions propagate rather than handling every error locally.
|
|
38
|
+
|
|
39
|
+
In languages with Result types (Rust, Kotlin, modern TypeScript), prefer those — they provide the same separation while making error handling explicit in the type system.
|
|
40
|
+
|
|
41
|
+
## Don't Return Null
|
|
42
|
+
|
|
43
|
+
Returning null forces every caller to add a null check. One forgotten check means a null pointer exception at runtime, often far from the source.
|
|
44
|
+
|
|
45
|
+
Instead of returning null:
|
|
46
|
+
- **Empty collection**: Return `[]` instead of null for "no results" — callers can iterate safely
|
|
47
|
+
- **Optional/Maybe type**: Use `Optional<User>` instead of `User?` when absence is a valid business case — makes the possibility explicit in the type
|
|
48
|
+
- **Null Object pattern**: Return a no-op implementation instead of null (e.g., `NullLogger` that discards messages)
|
|
49
|
+
- **Throw an exception**: If the absence indicates an error condition, throw rather than returning null
|
|
50
|
+
|
|
51
|
+
The goal is to make the absence of a value explicit and safe to handle, rather than a trap waiting to crash.
|
|
52
|
+
|
|
53
|
+
## Don't Pass Null
|
|
54
|
+
|
|
55
|
+
Passing null as a function argument is worse than returning it — it pushes the defensive checking responsibility into the callee, and every function in the chain must guard against it.
|
|
56
|
+
|
|
57
|
+
When you're tempted to pass null:
|
|
58
|
+
- Use an overloaded method or default parameter instead
|
|
59
|
+
- Introduce a Null Object or "empty" value
|
|
60
|
+
- Redesign the API so the parameter isn't needed in that case
|
|
61
|
+
|
|
62
|
+
In languages without null safety (Java, JavaScript), establish a convention: public API methods never accept null. This eliminates an entire class of bugs.
|
|
63
|
+
|
|
64
|
+
## Provide Context in Exceptions
|
|
65
|
+
|
|
66
|
+
An exception should tell you everything needed to understand the failure without reading the source code:
|
|
67
|
+
- **What operation failed**: "Failed to save order"
|
|
68
|
+
- **What input caused the failure**: "Order ID: 12345, User: jane@example.com"
|
|
69
|
+
- **Why it failed**: "Database connection timeout after 30s"
|
|
70
|
+
|
|
71
|
+
Bad: `throw new RuntimeException("Error")`
|
|
72
|
+
Good: `throw new OrderPersistenceException("Failed to save order 12345: connection timeout after 30s", cause)`
|
|
73
|
+
|
|
74
|
+
Include the original exception as the cause — stack traces that start at the catch site instead of the error site are useless for debugging.
|
|
75
|
+
|
|
76
|
+
## Define Exceptions by Caller's Needs
|
|
77
|
+
|
|
78
|
+
Don't create exception hierarchies based on the source of the error. Create them based on what the caller needs to do about it.
|
|
79
|
+
|
|
80
|
+
The caller typically has 3-4 responses to an error:
|
|
81
|
+
1. **Retry** (transient failure) — network timeout, lock contention
|
|
82
|
+
2. **Report to user** (validation failure) — bad input, missing required fields
|
|
83
|
+
3. **Abort operation** (unrecoverable) — corrupt data, missing configuration
|
|
84
|
+
4. **Ignore/degrade** (optional failure) — analytics failed, cache miss
|
|
85
|
+
|
|
86
|
+
Design your exception types around these responses. A single `AppException` with a severity/category field is often better than dozens of specific exception classes that all get handled the same way.
|
|
87
|
+
|
|
88
|
+
When wrapping third-party exceptions, translate them into your domain's exception types. Callers shouldn't need to know whether the failure came from PostgreSQL, MongoDB, or a REST API — they need to know whether to retry, report, or abort.
|
|
89
|
+
|
|
90
|
+
## Never Swallow Exceptions
|
|
91
|
+
|
|
92
|
+
An empty catch block is a bug waiting to happen. The exception represents a condition the system couldn't handle, and silently ignoring it means the system is now in an unknown state.
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
// Never do this
|
|
96
|
+
try {
|
|
97
|
+
processPayment(order)
|
|
98
|
+
} catch (Exception e) {
|
|
99
|
+
// TODO: handle later
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
If you genuinely need to catch and continue:
|
|
104
|
+
- **Log the exception** with enough context to diagnose later
|
|
105
|
+
- **Add a comment explaining why** ignoring this specific error is safe
|
|
106
|
+
- **Be specific** about which exception you're catching — never catch the base Exception/Error type to ignore it
|
|
107
|
+
|
|
108
|
+
The only acceptable empty catch is `InterruptedException` restoration in Java threading, and even that should restore the interrupt flag.
|