start-vibing-stacks 2.6.0 → 2.7.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.
Files changed (79) hide show
  1. package/dist/index.js +16 -2
  2. package/dist/migrate.d.ts +27 -0
  3. package/dist/migrate.js +217 -0
  4. package/dist/setup.js +10 -0
  5. package/package.json +1 -1
  6. package/stacks/_shared/agents/claude-md-compactor.md +1 -0
  7. package/stacks/_shared/agents/commit-manager.md +1 -0
  8. package/stacks/_shared/agents/documenter.md +1 -0
  9. package/stacks/_shared/agents/domain-updater.md +1 -0
  10. package/stacks/_shared/agents/research-web.md +1 -0
  11. package/stacks/_shared/agents/security-auditor.md +168 -0
  12. package/stacks/_shared/agents/tester.md +1 -0
  13. package/stacks/_shared/hooks/final-check.ts +205 -0
  14. package/stacks/_shared/hooks/stop-validator.ts +77 -1
  15. package/stacks/_shared/skills/accessibility-wcag22/SKILL.md +284 -0
  16. package/stacks/_shared/skills/ci-pipelines/SKILL.md +166 -0
  17. package/stacks/_shared/skills/codebase-knowledge/SKILL.md +5 -0
  18. package/stacks/_shared/skills/database-migrations/SKILL.md +256 -0
  19. package/stacks/_shared/skills/debugging-patterns/SKILL.md +5 -0
  20. package/stacks/_shared/skills/docker-patterns/SKILL.md +5 -0
  21. package/stacks/_shared/skills/docs-tracker/SKILL.md +5 -0
  22. package/stacks/_shared/skills/error-handling/SKILL.md +335 -0
  23. package/stacks/_shared/skills/final-check/SKILL.md +74 -37
  24. package/stacks/_shared/skills/git-workflow/SKILL.md +5 -0
  25. package/stacks/_shared/skills/hook-development/SKILL.md +5 -0
  26. package/stacks/_shared/skills/observability/SKILL.md +351 -0
  27. package/stacks/_shared/skills/performance-patterns/SKILL.md +5 -0
  28. package/stacks/_shared/skills/playwright-automation/SKILL.md +5 -0
  29. package/stacks/_shared/skills/quality-gate/SKILL.md +5 -0
  30. package/stacks/_shared/skills/research-cache/SKILL.md +5 -0
  31. package/stacks/_shared/skills/secrets-management/SKILL.md +245 -0
  32. package/stacks/_shared/skills/security-baseline/SKILL.md +202 -0
  33. package/stacks/_shared/skills/test-coverage/SKILL.md +5 -0
  34. package/stacks/_shared/skills/ui-ux-audit/SKILL.md +5 -0
  35. package/stacks/frontend/react/skills/preline-ui/SKILL.md +5 -0
  36. package/stacks/frontend/react/skills/react-patterns/SKILL.md +5 -0
  37. package/stacks/frontend/react/skills/react-standards/SKILL.md +5 -0
  38. package/stacks/frontend/react/skills/react-ui-patterns/SKILL.md +5 -0
  39. package/stacks/frontend/react/skills/shadcn-ui/SKILL.md +5 -0
  40. package/stacks/frontend/react/skills/tailwind-patterns/SKILL.md +5 -0
  41. package/stacks/frontend/react/skills/zod-validation/SKILL.md +5 -0
  42. package/stacks/frontend/react-inertia/skills/inertia-react/SKILL.md +5 -0
  43. package/stacks/frontend/react-inertia/skills/react-standards/SKILL.md +5 -0
  44. package/stacks/nodejs/skills/api-security-node/SKILL.md +275 -0
  45. package/stacks/nodejs/skills/bun-runtime/SKILL.md +5 -0
  46. package/stacks/nodejs/skills/mongoose-patterns/SKILL.md +5 -0
  47. package/stacks/nodejs/skills/nextjs-app-router/SKILL.md +5 -0
  48. package/stacks/nodejs/skills/trpc-api/SKILL.md +5 -0
  49. package/stacks/nodejs/skills/typescript-strict/SKILL.md +5 -0
  50. package/stacks/nodejs/stack.json +2 -1
  51. package/stacks/nodejs/workflows/ci.yml +90 -0
  52. package/stacks/nodejs/workflows/security.yml +45 -0
  53. package/stacks/php/skills/api-design/SKILL.md +5 -0
  54. package/stacks/php/skills/api-security/SKILL.md +5 -0
  55. package/stacks/php/skills/composer-workflow/SKILL.md +5 -0
  56. package/stacks/php/skills/external-api-patterns/SKILL.md +5 -0
  57. package/stacks/php/skills/inertia-react/SKILL.md +5 -0
  58. package/stacks/php/skills/laravel-inertia-i18n/SKILL.md +5 -0
  59. package/stacks/php/skills/laravel-octane/SKILL.md +5 -0
  60. package/stacks/php/skills/laravel-patterns/SKILL.md +5 -0
  61. package/stacks/php/skills/mariadb-octane/SKILL.md +5 -0
  62. package/stacks/php/skills/php-patterns/SKILL.md +5 -0
  63. package/stacks/php/skills/phpstan-analysis/SKILL.md +5 -0
  64. package/stacks/php/skills/phpunit-testing/SKILL.md +5 -0
  65. package/stacks/php/skills/security-scan-php/SKILL.md +5 -0
  66. package/stacks/php/workflows/ci.yml +106 -0
  67. package/stacks/php/workflows/security.yml +36 -0
  68. package/stacks/python/skills/api-security-python/SKILL.md +312 -0
  69. package/stacks/python/skills/async-patterns/SKILL.md +5 -0
  70. package/stacks/python/skills/django-patterns/SKILL.md +5 -0
  71. package/stacks/python/skills/fastapi-patterns/SKILL.md +5 -0
  72. package/stacks/python/skills/pydantic-validation/SKILL.md +5 -0
  73. package/stacks/python/skills/pytest-testing/SKILL.md +5 -0
  74. package/stacks/python/skills/python-patterns/SKILL.md +5 -0
  75. package/stacks/python/skills/python-performance/SKILL.md +5 -0
  76. package/stacks/python/skills/scripting-automation/SKILL.md +5 -0
  77. package/stacks/python/stack.json +2 -1
  78. package/stacks/python/workflows/ci.yml +76 -0
  79. package/stacks/python/workflows/security.yml +56 -0
@@ -0,0 +1,335 @@
1
+ ---
2
+ name: error-handling
3
+ version: 1.0.0
4
+ description: Universal error-handling patterns. Error taxonomy, Result types, error boundaries, structured exceptions, never-swallow rules. Invoke when designing service layers, API responses, or any try/catch.
5
+ ---
6
+
7
+ # Error Handling
8
+
9
+ **ALWAYS invoke when adding try/catch, designing service layers, API error responses, or handling external API failures.**
10
+
11
+ > The default state of software is broken. Errors are not exceptional — handling them is the job.
12
+
13
+ ## Core Principles
14
+
15
+ 1. **Errors are values.** Plan for them in the type signature, not as a side channel.
16
+ 2. **Throw at boundaries, return at edges.** Internal modules return Results; HTTP layer translates to status codes; user sees a generic message.
17
+ 3. **Never swallow.** A `catch` with no log, no rethrow, and no recovery is a bug.
18
+ 4. **Fail closed.** On unexpected error, deny — don't continue with degraded state.
19
+ 5. **Distinguish operational from programmer errors.** Operational = expected (network blip, validation). Programmer = bug. They get different treatment.
20
+
21
+ ---
22
+
23
+ ## Error Taxonomy (mandatory)
24
+
25
+ Every error you create classifies into one of these:
26
+
27
+ | Class | Examples | Strategy |
28
+ |---|---|---|
29
+ | **Validation** | Bad input, schema mismatch | Return 4xx with field details; do not log as error |
30
+ | **Authentication** | Missing/expired/invalid token | 401, log at info |
31
+ | **Authorization** | Authenticated but not allowed | 403, log at warn (could be probing) |
32
+ | **NotFound** | Resource missing | 404, log at info |
33
+ | **Conflict** | Optimistic lock, duplicate key | 409, log at warn |
34
+ | **RateLimit** | Quota exceeded | 429 + `Retry-After`, log at info |
35
+ | **External** | Upstream API failed | Retry+backoff if idempotent; 502/503; log at error |
36
+ | **Programmer** | Null deref, type mismatch, switch missed | 500, log at error, alert, fix |
37
+
38
+ If the error doesn't fit, you have a new class — add it deliberately.
39
+
40
+ ---
41
+
42
+ ## Pattern 1 — Custom Error Classes (TS / Python / PHP)
43
+
44
+ ### TypeScript
45
+ ```ts
46
+ // lib/errors.ts
47
+ export class AppError extends Error {
48
+ constructor(
49
+ public readonly code: string,
50
+ public readonly httpStatus: number,
51
+ message: string,
52
+ public readonly cause?: unknown,
53
+ public readonly meta?: Record<string, unknown>,
54
+ ) {
55
+ super(message);
56
+ this.name = this.constructor.name;
57
+ }
58
+ }
59
+
60
+ export class ValidationError extends AppError {
61
+ constructor(message: string, fields?: Record<string, string[]>) {
62
+ super('validation_error', 422, message, undefined, { fields });
63
+ }
64
+ }
65
+ export class NotFoundError extends AppError {
66
+ constructor(resource: string) { super('not_found', 404, `${resource} not found`); }
67
+ }
68
+ export class UnauthorizedError extends AppError {
69
+ constructor(message = 'Unauthorized') { super('unauthorized', 401, message); }
70
+ }
71
+ export class ForbiddenError extends AppError {
72
+ constructor(message = 'Forbidden') { super('forbidden', 403, message); }
73
+ }
74
+ export class ConflictError extends AppError {
75
+ constructor(message: string) { super('conflict', 409, message); }
76
+ }
77
+ export class ExternalError extends AppError {
78
+ constructor(service: string, cause: unknown) {
79
+ super('external_error', 502, `Upstream ${service} failed`, cause);
80
+ }
81
+ }
82
+ ```
83
+
84
+ ### Python
85
+ ```python
86
+ class AppError(Exception):
87
+ code: str = "error"
88
+ http_status: int = 500
89
+ def __init__(self, message: str, *, cause: Exception | None = None, meta: dict | None = None):
90
+ super().__init__(message)
91
+ self.cause, self.meta = cause, meta or {}
92
+
93
+ class ValidationError(AppError): code, http_status = "validation_error", 422
94
+ class NotFoundError(AppError): code, http_status = "not_found", 404
95
+ class UnauthorizedError(AppError):code, http_status = "unauthorized", 401
96
+ class ForbiddenError(AppError): code, http_status = "forbidden", 403
97
+ class ConflictError(AppError): code, http_status = "conflict", 409
98
+ class ExternalError(AppError): code, http_status = "external_error", 502
99
+ ```
100
+
101
+ ### PHP
102
+ Use Laravel's `HttpException` family + custom domain exceptions. Override `Handler::register` to map domain exceptions to HTTP responses.
103
+
104
+ ---
105
+
106
+ ## Pattern 2 — Result Types (for internal layers)
107
+
108
+ Throwing across many layers is expensive (TypeScript) and opaque (it's not in the signature). For internal service code, use a Result.
109
+
110
+ ### TypeScript
111
+ ```ts
112
+ type Result<T, E = AppError> =
113
+ | { ok: true; data: T }
114
+ | { ok: false; error: E };
115
+
116
+ const ok = <T>(data: T): Result<T> => ({ ok: true, data });
117
+ const err = <E extends AppError>(error: E): Result<never, E> => ({ ok: false, error });
118
+
119
+ // Service returns Result; caller handles both branches
120
+ async function findUser(id: string): Promise<Result<User>> {
121
+ const user = await db.user.findById(id);
122
+ if (!user) return err(new NotFoundError('user'));
123
+ return ok(user);
124
+ }
125
+
126
+ // Caller (route handler) — throws to be caught by error middleware
127
+ const result = await findUser(id);
128
+ if (!result.ok) throw result.error;
129
+ return result.data;
130
+ ```
131
+
132
+ ### Python
133
+ ```python
134
+ from dataclasses import dataclass
135
+ from typing import Generic, TypeVar
136
+ T = TypeVar("T"); E = TypeVar("E")
137
+
138
+ @dataclass(frozen=True)
139
+ class Ok(Generic[T]): data: T
140
+ @dataclass(frozen=True)
141
+ class Err(Generic[E]): error: E
142
+
143
+ Result = Ok[T] | Err[E]
144
+ ```
145
+
146
+ Or use `returns` library: `from returns.result import Result, Success, Failure`.
147
+
148
+ ---
149
+
150
+ ## Pattern 3 — Centralized HTTP Error Handler
151
+
152
+ ### Express
153
+ ```ts
154
+ // errors of type AppError → mapped; everything else → 500
155
+ app.use((err: unknown, req: Request, res: Response, _next: NextFunction) => {
156
+ const requestId = res.getHeader('x-request-id');
157
+ if (err instanceof AppError) {
158
+ req.log.warn({ err, code: err.code, status: err.httpStatus }, err.message);
159
+ return res.status(err.httpStatus).json({
160
+ error: { code: err.code, message: err.message, requestId, ...err.meta },
161
+ });
162
+ }
163
+ // Unknown — programmer error, never leak details
164
+ req.log.error({ err }, 'Unhandled exception');
165
+ res.status(500).json({ error: { code: 'internal_error', message: 'Internal Server Error', requestId } });
166
+ });
167
+ ```
168
+
169
+ ### Next.js — App Router
170
+ Use `error.tsx` + `global-error.tsx` for client-rendered errors, and a wrapper for Route Handlers:
171
+
172
+ ```ts
173
+ // app/api/_handler.ts
174
+ export function safeHandler<T extends Request>(fn: (req: T) => Promise<Response>) {
175
+ return async (req: T): Promise<Response> => {
176
+ try { return await fn(req); }
177
+ catch (e) {
178
+ if (e instanceof AppError) {
179
+ return Response.json({ error: { code: e.code, message: e.message, ...e.meta } }, { status: e.httpStatus });
180
+ }
181
+ logger.error({ err: e }, 'Unhandled');
182
+ return Response.json({ error: { code: 'internal_error', message: 'Internal Server Error' } }, { status: 500 });
183
+ }
184
+ };
185
+ }
186
+ ```
187
+
188
+ ### FastAPI
189
+ ```python
190
+ from fastapi import FastAPI, Request
191
+ from fastapi.responses import JSONResponse
192
+
193
+ @app.exception_handler(AppError)
194
+ async def app_error_handler(req: Request, exc: AppError):
195
+ return JSONResponse(
196
+ {"error": {"code": exc.code, "message": str(exc), **exc.meta}},
197
+ status_code=exc.http_status,
198
+ )
199
+
200
+ @app.exception_handler(Exception)
201
+ async def unhandled_handler(req: Request, exc: Exception):
202
+ log.exception("unhandled", request_id=req.headers.get("x-request-id"))
203
+ return JSONResponse(
204
+ {"error": {"code": "internal_error", "message": "Internal Server Error"}},
205
+ status_code=500,
206
+ )
207
+ ```
208
+
209
+ ---
210
+
211
+ ## Pattern 4 — Retry with Backoff (External Calls)
212
+
213
+ ```ts
214
+ // Idempotent operations only. Never retry payments without idempotency keys.
215
+ async function withRetry<T>(
216
+ fn: () => Promise<T>,
217
+ { tries = 3, baseMs = 200, factor = 2, jitter = 0.3 } = {},
218
+ ): Promise<T> {
219
+ let lastErr: unknown;
220
+ for (let i = 0; i < tries; i++) {
221
+ try { return await fn(); }
222
+ catch (e) {
223
+ lastErr = e;
224
+ if (i === tries - 1) break;
225
+ const delay = baseMs * Math.pow(factor, i) * (1 + (Math.random() - 0.5) * 2 * jitter);
226
+ await new Promise(r => setTimeout(r, delay));
227
+ }
228
+ }
229
+ throw new ExternalError('upstream', lastErr);
230
+ }
231
+ ```
232
+
233
+ For HTTP retries, use `axios-retry`, `got` (built-in), or `tenacity` (Python). Don't roll your own unless you must.
234
+
235
+ **Idempotency keys** for non-idempotent operations (POST that creates / charges):
236
+ ```ts
237
+ await stripe.charges.create({ amount, customer }, { idempotencyKey: orderId });
238
+ ```
239
+
240
+ ---
241
+
242
+ ## Pattern 5 — Circuit Breaker (External Outages)
243
+
244
+ When upstream is down, fail fast instead of piling up requests:
245
+
246
+ ```ts
247
+ import CircuitBreaker from 'opossum';
248
+ const breaker = new CircuitBreaker(callUpstream, {
249
+ timeout: 3000,
250
+ errorThresholdPercentage: 50,
251
+ resetTimeout: 30000,
252
+ });
253
+ breaker.fallback(() => ({ data: cached, stale: true }));
254
+ ```
255
+
256
+ Python: `pybreaker`. PHP: `ackintosh/ganesha`.
257
+
258
+ ---
259
+
260
+ ## Pattern 6 — Async / Promise Hygiene
261
+
262
+ ```ts
263
+ // WRONG — unhandled rejection crashes the process (Node 15+)
264
+ somePromise();
265
+
266
+ // CORRECT — always handle, even if just to log
267
+ somePromise().catch(err => logger.error({ err }, 'background task failed'));
268
+
269
+ // Promise.all rejects on first failure — for partial success use allSettled
270
+ const results = await Promise.allSettled([fetchA(), fetchB(), fetchC()]);
271
+ const failures = results.filter(r => r.status === 'rejected');
272
+ if (failures.length > 0) logger.warn({ failures }, 'partial failure');
273
+ ```
274
+
275
+ Catch handler at process level (defense in depth):
276
+ ```ts
277
+ process.on('unhandledRejection', (reason) => {
278
+ logger.fatal({ reason }, 'Unhandled rejection');
279
+ process.exit(1);
280
+ });
281
+ process.on('uncaughtException', (err) => {
282
+ logger.fatal({ err }, 'Uncaught exception');
283
+ process.exit(1);
284
+ });
285
+ ```
286
+
287
+ ---
288
+
289
+ ## Anti-Patterns
290
+
291
+ | Anti-pattern | Why it's bad | Fix |
292
+ |---|---|---|
293
+ | `try { ... } catch {}` | Silent failure | Log + rethrow or recover deliberately |
294
+ | `catch (e) { console.log(e) }` | No structured context | Use logger; include `request_id`, `user_id` |
295
+ | `catch (e) { throw new Error(e.message) }` | Loses stack trace | `throw new AppError(..., e)` (preserve cause) |
296
+ | `if (err) return null` | Caller can't distinguish "no result" from "failed" | Result type |
297
+ | Returning `{ error: 'something' }` from a function | Convention drift, untyped | Result type or throw |
298
+ | Catching `Error` to coerce | Hides bugs | Catch specific classes |
299
+ | Generic 500 response with raw stack | Info disclosure | Log full, return generic message + requestId |
300
+ | Retrying non-idempotent calls | Double-charge / double-create | Idempotency keys |
301
+ | Retrying without backoff | DoS your own upstream | Exponential + jitter |
302
+
303
+ ## API Error Response Shape (Standardize)
304
+
305
+ ```json
306
+ {
307
+ "error": {
308
+ "code": "validation_error",
309
+ "message": "Invalid input",
310
+ "fields": {
311
+ "email": ["must be a valid email"],
312
+ "age": ["must be at least 13"]
313
+ },
314
+ "requestId": "req_abc123"
315
+ }
316
+ }
317
+ ```
318
+
319
+ Stable contract: client code can switch on `code`. Always include `requestId` so support can correlate with logs.
320
+
321
+ ## Pre-Commit Checklist
322
+
323
+ - [ ] No empty `catch` blocks
324
+ - [ ] Every `catch` either logs+rethrows, recovers deliberately, or transforms to `AppError`
325
+ - [ ] Service-layer functions that can fail return `Result` or throw `AppError`
326
+ - [ ] HTTP layer has a single error mapper; routes do not stringify exceptions
327
+ - [ ] External calls have retry + backoff (if idempotent) or idempotency keys
328
+ - [ ] No raw stack traces in API responses
329
+ - [ ] Process-level `unhandledRejection` / `uncaughtException` handlers exist (Node)
330
+
331
+ ## See Also
332
+
333
+ - `observability` — what to log on errors
334
+ - `security-baseline` — A09 logging, A05 config (don't leak stack traces)
335
+ - Stack `api-security-*` — error mapping in framework handlers
@@ -1,56 +1,93 @@
1
- # Final Check — Task Completion Validator
1
+ ---
2
+ name: final-check
3
+ version: 2.0.0
4
+ description: Final validator with VETO power. Now executable — runs `npx tsx .claude/hooks/final-check.ts` to scan for debug statements, secrets, .skip, any, RCE risks, SQL concat. Blocks completion on CRITICAL/HIGH findings.
5
+ ---
2
6
 
3
- **ALWAYS invoke BEFORE completing ANY task. HAS VETO POWER.**
7
+ # Final Check Executable Validator
4
8
 
5
- > ⚠️ **VETO POWER:** If any rule is violated STOP, list violations, require fix, re-validate.
9
+ **ALWAYS run BEFORE completing ANY task. HAS VETO POWER.**
6
10
 
7
- ## Purpose
11
+ > v2.0+ promotes this from a manual checklist to an executable script. The script never trusts memory.
8
12
 
9
- Last check before task completion. Ensures nothing was forgotten.
13
+ ## How to Run
10
14
 
11
- ## Checklist
15
+ ```bash
16
+ npx tsx .claude/hooks/final-check.ts
17
+ ```
12
18
 
13
- ### 1. Code Quality
14
- - [ ] Quality gates passed (PHPStan/TypeCheck, Tests, Lint)
15
- - [ ] No `console.log` / `var_dump` left in code
16
- - [ ] No TODO/FIXME left unresolved
17
- - [ ] No hardcoded secrets or credentials
19
+ Exit codes:
20
+ - `0` clean (or only LOW/MEDIUM warnings)
21
+ - `1` at least one CRITICAL or HIGH finding → **task blocked**
18
22
 
19
- ### 2. Documentation
20
- - [ ] CLAUDE.md "Last Change" updated
21
- - [ ] Affected domain files updated
22
- - [ ] New patterns documented
23
+ ## What It Scans
23
24
 
24
- ### 3. Testing
25
- - [ ] New code has tests
26
- - [ ] Existing tests still pass
27
- - [ ] Edge cases covered
25
+ For every file in the diff (staged + unstaged + untracked) matching the active stack's source extensions, every line is checked against:
28
26
 
29
- ### 4. Git
30
- - [ ] On correct branch
31
- - [ ] All files committed
32
- - [ ] Conventional commit message
33
- - [ ] Merged to main (if complete)
27
+ | Severity | Rule | Why blocking |
28
+ |---|---|---|
29
+ | CRITICAL | Hardcoded secret pattern (api key / token / password) | Leak risk |
30
+ | CRITICAL | Public env var with `*SECRET\|*TOKEN\|*PRIVATE\|*PASSWORD\|*CREDENTIAL` | Bundles into client |
31
+ | HIGH | Arbitrary code-execution function (`eval`, Python `exec`) | RCE |
32
+ | HIGH | `subprocess` with `shell=True` (Python) | Command injection |
33
+ | HIGH | SQL string concatenation / f-string SQL | SQL injection |
34
+ | HIGH | `it.only` / `test.only` / `describe.only` / `it.skip` in committed test | False green CI |
35
+ | MEDIUM | `console.log/debug/trace` outside tests | Debug leftover, log noise |
36
+ | MEDIUM | PHP `var_dump/dd/dump/print_r` | Debug leftover |
37
+ | MEDIUM | TypeScript `any` (without `/* ok */` escape) | Erodes strict mode |
38
+ | MEDIUM | `@ts-ignore` (use `@ts-expect-error` with comment) | Silently outdated |
39
+ | MEDIUM | `@pytest.mark.skip` | Hidden test gaps |
40
+ | LOW | `print()` outside tests (Python) | Use logger |
41
+ | LOW | TODO / FIXME / XXX / HACK | Track or remove |
34
42
 
35
- ### 5. Skills Consulted
36
- - [ ] Codebase knowledge checked before implementing
37
- - [ ] Research cache checked before web search
38
- - [ ] Security patterns applied
43
+ Lines containing recognised placeholders (`<your...>`, `YOUR_X`, `placeholder`, `example.com`, `sk_test_`) are skipped to avoid false positives in `.env.example` and docs.
39
44
 
40
- ## Violation Response
45
+ ## Workflow
41
46
 
42
47
  ```
43
- BLOCKED: [Violation description]
48
+ 1. Implementation done
49
+ 2. Run npx tsx .claude/hooks/final-check.ts
50
+ 3. If exit 1 → fix, re-run
51
+ 4. If exit 0 → proceed to quality-gate → commit
52
+ ```
53
+
54
+ ## Suppressing False Positives
44
55
 
45
- Required fixes:
46
- 1. [Fix needed]
47
- 2. [Fix needed]
56
+ When a finding is intentional (e.g. an `any` in well-justified glue code):
48
57
 
49
- Re-run final-check after fixing.
58
+ ```ts
59
+ const result = (json as any) /* ok: external lib without types */;
50
60
  ```
51
61
 
62
+ Only `/* ok */` immediately after `: any` is recognised. For other rules, refactor — there is no general-purpose suppression by design.
63
+
64
+ ## Hook Wiring (Optional)
65
+
66
+ To run automatically on Stop, add to `.claude/settings.json`:
67
+
68
+ ```json
69
+ {
70
+ "hooks": {
71
+ "Stop": [{
72
+ "matcher": "*",
73
+ "hooks": [{ "type": "command", "command": "npx tsx ${CLAUDE_PROJECT_DIR}/.claude/hooks/final-check.ts" }]
74
+ }]
75
+ }
76
+ }
77
+ ```
78
+
79
+ The default Stop is `stop-validator.ts` (git/branch/CLAUDE.md/secret scan). `final-check.ts` is complementary — runs before it as a code-quality gate.
80
+
52
81
  ## Rules
53
82
 
54
- 1. **VETO POWER** — can block task completion
55
- 2. **ALL CHECKS MUST PASS** — no partial approvals
56
- 3. **RE-VALIDATE AFTER FIXES** — don't trust "I fixed it"
83
+ 1. **VETO POWER** — exit 1 blocks task completion
84
+ 2. **NEVER skip** — `--no-verify` style bypasses are forbidden
85
+ 3. **RE-RUN after fixes** — don't trust memory
86
+ 4. **One finding at a time** — fix CRITICAL first, then HIGH
87
+ 5. **Refactor over suppress** — `/* ok */` is for extraordinary cases
88
+
89
+ ## See Also
90
+
91
+ - `quality-gate` — typecheck/lint/test/build (different scope)
92
+ - `security-baseline` — what the secret/RCE rules enforce
93
+ - `stop-validator.ts` — git/branch/docs gate (sibling, complementary)
@@ -1,3 +1,8 @@
1
+ ---
2
+ name: git-workflow
3
+ version: 1.0.0
4
+ ---
5
+
1
6
  # Git Workflow
2
7
 
3
8
  ## Branch Naming
@@ -1,3 +1,8 @@
1
+ ---
2
+ name: hook-development
3
+ version: 1.0.0
4
+ ---
5
+
1
6
  # Hook Development — Claude Code Hooks
2
7
 
3
8
  **ALWAYS invoke when creating or modifying Claude Code hooks.**