@softspark/ai-toolkit 4.32.2 → 4.32.4

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/CHANGELOG.md CHANGED
@@ -7,6 +7,62 @@ Versioning follows [Semantic Versioning](https://semver.org/).
7
7
 
8
8
  ---
9
9
 
10
+ ## v4.32.4 - Validation parity and API error contracts (2026-09-06)
11
+
12
+ ### Changed
13
+
14
+ - **Validation contracts:** Add practical guidance to `/api-patterns`,
15
+ `/security-patterns`, `/review` and `/testing-patterns` for a shared backend
16
+ and frontend rule source, operation-specific DTO groups/defaults, finite
17
+ collection bounds, Unicode units, explicit server-only checks and parity
18
+ fixtures. Local validation failures remain distinct from HTTP responses.
19
+ Cross-skill references resolve through the installed catalog so namespaced
20
+ adapters, including GitHub Copilot, can find the same guidance.
21
+ - **API error contracts:** Preserve the host's error representation and
22
+ distinguish domain refusals from technical failures. Cover actionable
23
+ messages, JSON types, background-job failures, unknown outcomes and safe
24
+ retries. Common security/testing rules retain shared-database test isolation.
25
+
26
+ ### Fixed
27
+
28
+ - **Schema guidance:** Correct JSON Schema conditional requirements and Ajv
29
+ strict-mode semantics, retain domain checks alongside boundary validation,
30
+ and import `Field` in the Pydantic example. The FastAPI example catches a
31
+ specific request refusal while preserving its exception cause.
32
+ - **Release verification:** Compare inventories with the current artifact,
33
+ run installation smoke in a disposable environment, and fail when an
34
+ expected generated JSON file is missing. Preserve the operator's install
35
+ and authentication state during candidate and published-package checks.
36
+ - **Package metadata:** Refresh skill and hook inventory descriptions.
37
+
38
+ ### Release checks
39
+
40
+ - **Ecosystem:** Refresh documentation hashes and local CLI version probes;
41
+ the review found no heading or capability-marker changes and changes no
42
+ generators, runtime integration paths or permission declarations.
43
+ - **Skill body budget:** Keep the existing 18,000-byte warning threshold;
44
+ the largest body remains 17,197 bytes, so the ratchet cannot tighten yet.
45
+
46
+ ## v4.32.3 — Search-first stops firing on things nobody asked (2026-09-04)
47
+
48
+ ### Fixed
49
+
50
+ - **A background task finishing no longer demands a KB search.**
51
+ `user-prompt-submit.sh` set the search-required flag on any stdin longer
52
+ than 30 characters. The harness delivers background-task notifications, CI
53
+ events and replayed slash-command output on the same channel as a prompt, so
54
+ a task completing blocked `Stop` and asked for a search over text nobody
55
+ wrote. Harness event envelopes no longer set the flag.
56
+ - **A pasted credential no longer becomes a search query.** A 43-character API
57
+ key on its own line cleared the length gate, and the block message names the
58
+ prompt as the thing to search for — so the enforcement path asked for a live
59
+ secret to be sent to a retrieval service. Single-token prompts no longer set
60
+ the flag; a genuine one-word prompt is under the length gate anyway, so
61
+ nothing that was enforced before stops being enforced. Prose that merely
62
+ mentions a task notification still sets it, and that case is now a test.
63
+
64
+ ---
65
+
10
66
  ## v4.32.2 - Plugin exports land where you run the command (2026-09-04)
11
67
 
12
68
  ### Fixed
package/README.md CHANGED
@@ -6,25 +6,24 @@
6
6
  [![License: Apache 2.0](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](LICENSE)
7
7
  [![Skills](https://img.shields.io/badge/skills-114-brightgreen)](app/skills/)
8
8
  [![Agents](https://img.shields.io/badge/agents-44-blue)](app/agents/)
9
- [![Tests](https://img.shields.io/badge/tests-1974%20passing-success)](tests/)
10
-
11
- ## What's New in v4.32.2
12
-
13
- **v4.32.2** makes plugin exports land where you run the command:
14
-
15
- - `ai-toolkit claude-app export`, `codex-plugin export`, and
16
- `antigravity-plugin export` write their ZIP (and the sibling
17
- `*-global-instructions.md`) to the directory you typed the command in. They
18
- used to appear inside `node_modules/@softspark/ai-toolkit` because the CLI
19
- runs them with its own working directory; the wrapper now hands the shell's
20
- directory to the exporters. Absolute `--output` paths are unchanged.
21
- - Root-level `*.zip` and `*-global-instructions.md` are gitignored so an
22
- export run from the repository is never committed.
23
- - Still in this train: `plugin remove` leaves nothing behind, every skill
24
- script answers `--help` (v4.32.1); path-scoped common rules, project-scoped
25
- language skills, `doctor` context-budget and permission checks, one strict
26
- frontmatter parser, `git-team` for `--profile strict` (v4.32.0). Test
27
- count: 1972 -> 1974 bats + 348 pytest.
9
+ [![Tests](https://img.shields.io/badge/tests-1978%20passing-success)](tests/)
10
+
11
+ ## What's New in v4.32.4
12
+
13
+ **v4.32.4** documents validation parity and reliable API error handling:
14
+
15
+ - **One validation contract for frontend and backend.** Existing skills cover
16
+ DTO groups/defaults, nested collections, Unicode, explicit server-only checks
17
+ and shared regression fixtures, with backend enforcement kept authoritative.
18
+ - **Errors preserve the API contract.** Guidance separates local refusals,
19
+ domain conflicts and technical failures, retaining field paths, machine
20
+ codes and safe recovery when a write outcome is uncertain.
21
+ - **Schema examples match validator behavior.** Correct JSON Schema
22
+ conditional requirements and Ajv strict-mode guidance; keep state-dependent
23
+ domain checks alongside validation at the request boundary.
24
+ - **Release smoke tests verify the installed artifact.** Procedures use
25
+ disposable environments, current inventories and assertions for missing
26
+ generated files, without modifying the operator's live installation.
28
27
 
29
28
  See [CHANGELOG.md](CHANGELOG.md) for full history.
30
29
 
@@ -206,7 +205,7 @@ ai-toolkit/
206
205
  │ ├── agents/ # 44 agent definitions
207
206
  │ ├── skills/ # 114 skills (task / hybrid / knowledge)
208
207
  │ ├── rules/ # Source rules synced into Claude/editor rule files
209
- │ ├── hooks/ # Hook scripts (29 entries, 14 lifecycle events)
208
+ │ ├── hooks/ # Hook scripts (28 entries, 14 lifecycle events)
210
209
  │ ├── claude-app/ # Generated Chat/Cowork plugin rules, hooks, instructions
211
210
  │ ├── plugins/ # 2 experimental plugin packs (opt-in)
212
211
  │ ├── output-styles/ # System prompt output style overrides
@@ -3,7 +3,7 @@
3
3
  "name": "ai-toolkit",
4
4
  "displayName": "AI Toolkit",
5
5
  "description": "Professional-grade engineering skills, agents, rules, and lifecycle guardrails for Claude Code, Claude Chat, and Cowork.",
6
- "version": "4.32.2",
6
+ "version": "4.32.4",
7
7
  "author": {
8
8
  "name": "SoftSpark",
9
9
  "url": "https://github.com/softspark"
@@ -8,7 +8,7 @@ Universal multi-agent system for software development. Works across all reposito
8
8
  |-----------|-------|
9
9
  | Agents | See agents catalog |
10
10
  | Skills | See skills catalog |
11
- | Hooks | 14 events / 29 entries (SessionStart ×2, Notification ×1, PreToolUse ×5, UserPromptSubmit ×2, PostToolUse ×6, Stop ×4, TaskCompleted ×1, TeammateIdle ×1, SubagentStart ×1, SubagentStop ×1, PreCompact ×2, SessionEnd ×1, InstructionsLoaded ×1, ConfigChange ×1) plus statusLine |
11
+ | Hooks | 14 events / 28 entries (SessionStart ×2, Notification ×1, PreToolUse ×5, UserPromptSubmit ×2, PostToolUse ×5, Stop ×4, TaskCompleted ×1, TeammateIdle ×1, SubagentStart ×1, SubagentStop ×1, PreCompact ×2, SessionEnd ×1, InstructionsLoaded ×1, ConfigChange ×1) plus statusLine |
12
12
 
13
13
  ---
14
14
 
@@ -352,7 +352,8 @@ Solo-safe core: everything here holds whether one person or twenty merge into
352
352
  - Use HTTPS everywhere. No exceptions.
353
353
  - Implement rate limiting and request throttling.
354
354
  - Set CORS headers explicitly. Never use `*` in production.
355
- - Return generic error messages to clients. Log details server-side.
355
+ - Return safe, actionable messages for known failures; use a neutral fallback when the cause is unknown or disclosure would reveal protected information.
356
+ - Keep SQL, stack traces and provider internals out of ordinary client responses, including 4xx and background-job error fields. Preserve original causes in access-controlled, redacted diagnostics.
356
357
  - Use security headers: HSTS, X-Content-Type-Options, X-Frame-Options.
357
358
 
358
359
  ## Dependencies
@@ -403,6 +404,12 @@ Solo-safe core: everything here holds whether one person or twenty merge into
403
404
  - Tests must be fast: unit tests <100ms each, test suite <60s.
404
405
  - Avoid `sleep` in tests: use polling, events, or test clocks.
405
406
  - Do not test implementation details (private methods, internal state).
407
+ - Run integration suites serially when they share or reset a database. Parallel runners need isolated databases/stores; a filtered test must not invalidate an in-progress full suite.
408
+
409
+ ## API Error Paths
410
+ - For changed error handling, assert the actual status, public code/message, field paths, locale and recovery headers through the API boundary.
411
+ - Preserve JSON object/list types, including empty nested `{}` and `[]`, when testing response filters.
412
+ - Exercise real database constraints, idempotent replay and known versus uncertain write outcomes; schema-generated fixtures may omit migration-only indexes.
406
413
 
407
414
  ## Coverage
408
415
  - Measure coverage but do not chase 100%: focus on critical paths.
@@ -29,7 +29,28 @@ PROMPT_LEN=${#PROMPT_TEXT}
29
29
  STATE_DIR="$HOME/.softspark/ai-toolkit/state"
30
30
  FLAG="$STATE_DIR/search-required-$(hook_session_id).flag"
31
31
  mkdir -p "$STATE_DIR" 2>/dev/null
32
+
33
+ # The flag means "somebody asked a question the KB might answer". Length alone
34
+ # does not establish that. The harness delivers background-task notifications,
35
+ # CI events and replayed slash-command output on the same channel as a prompt,
36
+ # and a 43-character API key pasted on its own line clears the length gate too.
37
+ # Both were observed in one session: the first demanded a KB search for text
38
+ # nobody asked about, the second would have sent a live credential to a
39
+ # retrieval service as the query. Neither is a prompt.
40
+ PROMPT_HEAD="${PROMPT_TEXT#"${PROMPT_TEXT%%[![:space:]]*}"}"
41
+ PROMPT_IS_QUESTION=1
42
+ case "$PROMPT_HEAD" in
43
+ '<task-notification'*|'<ci-monitor-event'*|'<system-reminder'*|\
44
+ '<local-command-'*|'<command-name'*|'<command-message'*) PROMPT_IS_QUESTION=0 ;;
45
+ esac
46
+ # A single opaque token — API key, hash, URL, path — carries no question. A
47
+ # genuine one-word prompt is under the length gate anyway, so nothing is lost.
48
+ if [ "$(printf '%s' "$PROMPT_TEXT" | wc -w | tr -d ' ')" -le 1 ]; then
49
+ PROMPT_IS_QUESTION=0
50
+ fi
51
+
32
52
  if [ "$PROMPT_LEN" -gt 30 ] && \
53
+ [ "$PROMPT_IS_QUESTION" -eq 1 ] && \
33
54
  [ "${CLAUDE_SKIP_SEARCH_FIRST:-0}" != "1" ] && \
34
55
  ai_toolkit_has_search_provider; then
35
56
  { printf '%s\n%s\n' "$(date -u +%s)" "$PROMPT_TEXT" > "$FLAG"; } 2>/dev/null
@@ -45,7 +45,8 @@ version: "1.0.0"
45
45
  - Use HTTPS everywhere. No exceptions.
46
46
  - Implement rate limiting and request throttling.
47
47
  - Set CORS headers explicitly. Never use `*` in production.
48
- - Return generic error messages to clients. Log details server-side.
48
+ - Return safe, actionable messages for known failures; use a neutral fallback when the cause is unknown or disclosure would reveal protected information.
49
+ - Keep SQL, stack traces and provider internals out of ordinary client responses, including 4xx and background-job error fields. Preserve original causes in access-controlled, redacted diagnostics.
49
50
  - Use security headers: HSTS, X-Content-Type-Options, X-Frame-Options.
50
51
 
51
52
  ## Dependencies
@@ -46,6 +46,12 @@ paths:
46
46
  - Tests must be fast: unit tests <100ms each, test suite <60s.
47
47
  - Avoid `sleep` in tests: use polling, events, or test clocks.
48
48
  - Do not test implementation details (private methods, internal state).
49
+ - Run integration suites serially when they share or reset a database. Parallel runners need isolated databases/stores; a filtered test must not invalidate an in-progress full suite.
50
+
51
+ ## API Error Paths
52
+ - For changed error handling, assert the actual status, public code/message, field paths, locale and recovery headers through the API boundary.
53
+ - Preserve JSON object/list types, including empty nested `{}` and `[]`, when testing response filters.
54
+ - Exercise real database constraints, idempotent replay and known versus uncertain write outcomes; schema-generated fixtures may omit migration-only indexes.
49
55
 
50
56
  ## Coverage
51
57
  - Measure coverage but do not chase 100%: focus on critical paths.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: api-patterns
3
- description: "REST/GraphQL API design: naming, versioning, pagination, idempotency, OpenAPI. Triggers: API design, REST, GraphQL, OpenAPI, Swagger, idempotency, rate limit."
3
+ description: "API design: naming, versioning, pagination, idempotency, OpenAPI, error contracts and safe retries. Triggers: API design, REST, GraphQL, OpenAPI, Swagger, error response, HTTP status, rate limit."
4
4
  effort: medium
5
5
  user-invocable: false
6
6
  allowed-tools: Read
@@ -34,17 +34,24 @@ GET /api/v1/users/{id}/documents # User's documents
34
34
  | 200 | OK | Successful GET/PUT/PATCH |
35
35
  | 201 | Created | Successful POST |
36
36
  | 204 | No Content | Successful DELETE |
37
- | 400 | Bad Request | Invalid input |
37
+ | 400 | Bad Request | Malformed request or an established domain refusal |
38
38
  | 401 | Unauthorized | Missing/invalid auth |
39
39
  | 403 | Forbidden | No permission |
40
40
  | 404 | Not Found | Resource doesn't exist |
41
- | 409 | Conflict | Duplicate resource |
41
+ | 405 | Method Not Allowed | Unsupported method; preserve Allow |
42
+ | 409 | Conflict | Duplicate resource or current-state conflict |
43
+ | 412 | Precondition Failed | Supplied concurrency version is stale |
42
44
  | 422 | Unprocessable | Validation error |
45
+ | 428 | Precondition Required | Required concurrency precondition is missing |
43
46
  | 429 | Too Many Requests | Rate limited |
44
47
  | 500 | Internal Error | Server error |
48
+ | 503 | Service Unavailable | Dependency temporarily unavailable |
45
49
 
46
50
  ### Response Format
47
51
 
52
+ Follow the host's existing resource and collection contract. This envelope is
53
+ illustrative; do not impose it on a framework that already defines another shape.
54
+
48
55
  ```json
49
56
  {
50
57
  "data": {
@@ -65,6 +72,9 @@ GET /api/v1/users/{id}/documents # User's documents
65
72
 
66
73
  ### Error Response
67
74
 
75
+ Use the established error representation, which may be problem details,
76
+ framework validation errors, or a domain-specific envelope like this example:
77
+
68
78
  ```json
69
79
  {
70
80
  "error": {
@@ -78,6 +88,17 @@ GET /api/v1/users/{id}/documents # User's documents
78
88
  }
79
89
  ```
80
90
 
91
+ When implementing or reviewing failure paths, read
92
+ [Error contracts and safe retries](reference/error-contracts.md). Classify from
93
+ the original cause, preserve public codes and JSON types, and distinguish an
94
+ unknown operation outcome from a confirmed refusal. Do not turn arbitrary
95
+ server failures into invalid-input responses.
96
+
97
+ When adding client-side validation, read `reference/input-validation.md` from
98
+ the installed `security-patterns` skill. Resolve that skill through the current
99
+ client's catalog, since adapters may namespace skill directory names.
100
+ Use one authoritative rule source and prove parity at the request boundary.
101
+
81
102
  ---
82
103
 
83
104
  ## FastAPI Implementation
@@ -88,6 +109,9 @@ from pydantic import BaseModel, Field
88
109
 
89
110
  app = FastAPI(title="RAG-MCP API", version="1.0.0")
90
111
 
112
+ class InvalidSearchQuery(Exception):
113
+ """Known request-level refusal from the application-owned search adapter."""
114
+
91
115
  class SearchRequest(BaseModel):
92
116
  query: str = Field(..., min_length=1, description="Search query")
93
117
  limit: int = Field(10, ge=1, le=100, description="Max results")
@@ -115,8 +139,11 @@ async def search(request: SearchRequest):
115
139
  try:
116
140
  results = await perform_search(request.query, request.limit)
117
141
  return SearchResponse(results=results, total=len(results))
118
- except ValueError as e:
119
- raise HTTPException(status_code=400, detail=str(e))
142
+ except InvalidSearchQuery as exc:
143
+ raise HTTPException(
144
+ status_code=400,
145
+ detail="The search query is not supported. Check its syntax.",
146
+ ) from exc
120
147
  ```
121
148
 
122
149
  ---
@@ -146,7 +173,7 @@ In OpenAPI, pair `enum` with the value meanings in the description (or `x-enum-d
146
173
 
147
174
  ### Encode cross-field dependencies in the description
148
175
 
149
- If a field is only valid given another, say so where the dependent field is defined schemas cannot express "required when":
176
+ Document cross-field requirements where the dependent field is defined and encode them in the supported schema dialect. JSON Schema supports conditional requirements with `dependentRequired` or `if`/`then`; application state and opaque-token provenance still require prose and runtime checks. See [conditional schema validation](https://json-schema.org/understanding-json-schema/reference/conditionals).
150
177
 
151
178
  ```python
152
179
  cursor: str | None = Field(
@@ -372,10 +399,10 @@ Accept: application/vnd.myapi.v1+json
372
399
  ## Hard Rules
373
400
 
374
401
  - **MUST** version the API from day one (URL path or Accept header) — unversioned APIs break clients on every change
375
- - **MUST** validate every input at the API boundary, not inside business logic
402
+ - **MUST** validate input type, format and size at the API boundary; enforce state-dependent domain invariants in business logic as well
376
403
  - **MUST** use PUT for full replacement and PATCH for partial update — confusing the two causes silent data loss
377
404
  - **NEVER** return unbounded list responses — pagination (offset or cursor) is mandatory
378
- - **NEVER** expose stack traces or internal error details in 5xx responses clients get a `code`, `message`, and optional `details[]`
405
+ - **NEVER** expose private implementation details in ordinary API errors, including 4xx and background-job error fields; preserve the host's public error representation
379
406
  - **CRITICAL**: idempotency on POST/PUT/PATCH is non-negotiable when retries are possible — accept an `Idempotency-Key` header or design the endpoint to be naturally idempotent
380
407
  - **CRITICAL**: rate limits exist from the first deploy, not "later" — unprotected endpoints get abused within hours
381
408
 
@@ -395,10 +422,10 @@ Accept: application/vnd.myapi.v1+json
395
422
  ## Gotchas
396
423
 
397
424
  - CDN and load-balancer caches key on the full URL by default. If you version via both URL path and `Accept` header (e.g., `/api/v1/...` + `Accept: application/vnd.myapi.v2+json`), the edge returns the wrong payload for non-path versioning. Pick one versioning axis and stick to it.
398
- - OpenAPI `additionalProperties: false` is **not** enforced by most JSON Schema validators unless you explicitly enable strict mode (`ajv({strict: true})`, Pydantic `Config.extra = "forbid"`). An API marked "strict" in the spec silently accepts unknown fields.
425
+ - A published OpenAPI schema does not prove request enforcement. Ajv enforces `additionalProperties: false` when that schema is executed; its strict mode checks schema correctness and does not change validation results. Verify that the request boundary runs the intended schema and test unknown fields. See [Ajv strict mode](https://ajv.js.org/strict-mode.html) and [additionalProperties](https://ajv.js.org/json-schema.html#additionalproperties).
399
426
  - `Idempotency-Key` only works if the server persists the mapping from key to response — purely in-memory implementations forget it on restart. Back it with Redis or the primary DB.
400
427
  - HTTP methods are **case-sensitive** per RFC 7230 (all uppercase); some clients and proxies normalize, some don't. A `post` method reaches the server as-is through some edge proxies and hits a 405 instead of the POST route.
401
- - `429 Too Many Requests` without a `Retry-After` header leaves clients guessing most libraries back off exponentially from zero and hammer the server. Always include `Retry-After` on 429 and 503.
428
+ - Give rate-limited callers a meaningful `Retry-After`. For 503, include it when the server can provide a credible retry window; do not invent an outage duration. A retry header does not prove that repeating a write is safe.
402
429
 
403
430
  ## When NOT to Load
404
431
 
@@ -407,4 +434,3 @@ Accept: application/vnd.myapi.v1+json
407
434
  - For **language-specific idioms** (Fastify middleware chains, ASP.NET minimal APIs, etc.) — pair this skill with `/typescript-patterns`, `/csharp-patterns`, etc.
408
435
  - For **OpenAPI schema authoring** as the primary task — use `/docs` with OpenAPI output; this skill is design-only
409
436
  - For **authentication deep-dives** beyond the starter API-key and JWT snippets — use `/security-patterns`
410
-
@@ -0,0 +1,88 @@
1
+ # Error Contracts and Safe Retries
2
+
3
+ Use this reference for API failure paths. Apply the host's current contract and
4
+ keep unrelated endpoints, schemas and workflows outside the requested change.
5
+
6
+ ## Classify before wording
7
+
8
+ - Trace the original exception and persisted/provider state. A truncated message
9
+ or a generic unique-constraint error does not identify the affected entity.
10
+ - Prefer typed domain exceptions and structured database/provider codes. If a
11
+ constraint name is available only in text, read its diagnostic header rather
12
+ than matching a submitted value in the detail section.
13
+ - Distinguish malformed input, authorization, current-state conflict and a
14
+ dependency failure. Preserve deliberate domain-specific status conventions;
15
+ do not mechanically rewrite every existing 400.
16
+ - Catch known domain failures narrowly and preserve the exception cause. A
17
+ catch-all that copies arbitrary exception text into a 4xx response both leaks
18
+ internals and blames the caller for server failures.
19
+
20
+ ## Give a supported explanation and a safe action
21
+
22
+ A useful message identifies the affected operation/resource, the cause that is
23
+ actually known, and the next safe action. Use a neutral fallback when the cause
24
+ is unknown or disclosing it would reveal protected information.
25
+
26
+ | Observation | Response direction |
27
+ |---|---|
28
+ | A resource already has an open session | Explain the existing-session conflict |
29
+ | A provider request received no response | Explain the missing response, not a supposed provider rejection |
30
+ | The date parser rejected a timestamp | Identify the date/time problem, not an unrelated score or amount |
31
+ | An internal persistence invariant failed | Report a server failure, not a missing client field |
32
+ | A refund may have been accepted remotely | Require reconciliation before another refund |
33
+
34
+ Keep SQL, traces, internal paths and provider internals out of ordinary failure
35
+ text. Preserve original causes in access-controlled, redacted diagnostics.
36
+ Sanitized administrator connection tests and expected row-level validation may
37
+ legitimately provide more detail; do not erase useful authorized diagnostics.
38
+
39
+ ## Preserve the wire contract
40
+
41
+ - Keep existing envelopes, machine codes, field paths and supported media types.
42
+ A client-owned error code must not silently become localized server prose.
43
+ - Preserve JSON object/list types, including empty nested `{}` and `[]`.
44
+ Test the round trip when adding a response filter or normalizer.
45
+ - Keep recovery headers such as `Allow`, `Retry-After`, authentication challenges
46
+ and concurrency preconditions.
47
+ - Verify the host's actual locale selection and fallback. Do not invent a
48
+ profile/tenant/header precedence or assume every existing literal is translated.
49
+ - Cover router/firewall errors outside the main API framework and account for
50
+ cloned requests or error subrequests before relying on request attributes.
51
+ - Inspect error fields returned with HTTP 200 by background-job status APIs and
52
+ downloadable failure reports. Retain domain validation; sanitize unexpected
53
+ worker failures without changing retry/accounting behavior.
54
+
55
+ ## Do not infer rollback from the response
56
+
57
+ A transaction can commit before serialization or response delivery fails. A
58
+ remote service can accept an operation before its reply is lost. An HTTP error,
59
+ including a 5xx, therefore does not prove that nothing happened.
60
+
61
+ Use the operation's existing idempotency/reconciliation contract. Confirm state
62
+ before creating another write. Do not recommend blind replay of an uncertain
63
+ payment, refund or other external mutation, and do not remove a database
64
+ invariant merely to make its error disappear.
65
+
66
+ ## Verify behavior at the boundary
67
+
68
+ Choose tests for the paths actually changed:
69
+
70
+ - real constraints, including migration-only indexes omitted by test schemas;
71
+ - expected refusals versus unexpected runtime/provider failures;
72
+ - idempotent replay, conflicts and recovery after the conflict is resolved;
73
+ - HTTP status, public text/code, field paths, locale and recovery headers;
74
+ - supported response formats and empty object/list preservation;
75
+ - known versus uncertain write outcomes and background-job error fields;
76
+ - diagnostic retention without public technical details.
77
+
78
+ Test actual rate-limit exhaustion and recovery. A non-consuming peek may report
79
+ that the peek was accepted even when no request budget remains; the configured
80
+ limiter's behavior determines the guard.
81
+
82
+ Run tests serially when they share or reset a database. Parallel test runners
83
+ need isolated databases/stores; a filtered test must not reset the fixtures of
84
+ an in-progress full suite.
85
+
86
+ For additional project evidence, when RAG-MCP is available, retrieve
87
+ `shared/rag-mcp/best-practices/api-error-contracts.md`. The guidance above is
88
+ self-contained and does not require that integration.
@@ -125,7 +125,12 @@ After all reviewers complete:
125
125
  - [ ] Backward compatibility preserved (no silent breaking changes)
126
126
  - [ ] API versioning updated if contract changed
127
127
  - [ ] Schema validation on request/response
128
+ - [ ] Client validation uses the authoritative input contract, including operation groups/defaults, finite bounds, nested paths and documented Unicode units; read `reference/input-validation.md` from the `security-patterns` skill located through the current client's installed catalog
129
+ - [ ] Shared backend/client fixtures and generation drift checks cover changed rules; unsupported/server-only checks are explicit, and local refusals do not masquerade as HTTP responses
128
130
  - [ ] Error responses follow project convention
131
+ - [ ] Statuses and messages distinguish input/state refusals from infrastructure failures; original causes remain available in authorized diagnostics
132
+ - [ ] Error filtering preserves machine codes, field paths, JSON object/list types, locale and recovery headers; background-job error fields are covered too
133
+ - [ ] Retry advice reflects known persisted/provider state and does not invite blind replay of an uncertain mutation
129
134
  - [ ] Wire-level contracts checked, not just code signatures: HTTP routes, webhook payloads, event/queue schemas
130
135
 
131
136
  ### Concurrency / Async
@@ -129,13 +129,13 @@ For authentication patterns (JWT, passwords, token strategy), see [reference/aut
129
129
 
130
130
  For authorization patterns (RBAC, ABAC), see [reference/authorization.md](reference/authorization.md).
131
131
 
132
- For input validation patterns (SQL injection, XSS, Pydantic), see [reference/input-validation.md](reference/input-validation.md).
132
+ For input validation, client/backend contract parity, finite bounds and Unicode gotchas, see [reference/input-validation.md](reference/input-validation.md).
133
133
 
134
134
  For OAuth2 flows, CSRF protection, and audit logging, see [reference/oauth-csrf-audit.md](reference/oauth-csrf-audit.md).
135
135
 
136
136
  ## Rules
137
137
 
138
- - **MUST** validate all input at the trust boundary, not inside business logic deep validation allows bad data to spread before rejection
138
+ - **MUST** validate input type, format and size at the trust boundary; domain logic must also enforce state-dependent invariants before side effects
139
139
  - **MUST** use parameterized queries (prepared statements) for every SQL interaction — string concatenation is SQL injection
140
140
  - **NEVER** store secrets (API keys, tokens, passwords) in code, config files, or git history — use the platform's secret manager
141
141
  - **NEVER** log passwords, tokens, PII, or PHI — even at debug level. Logs reach aggregation systems, backups, and disk snapshots.
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Validation Rules
4
4
  ```python
5
- from pydantic import BaseModel, EmailStr, constr
5
+ from pydantic import BaseModel, EmailStr, Field, constr
6
6
 
7
7
  class UserInput(BaseModel):
8
8
  email: EmailStr
@@ -10,6 +10,74 @@ class UserInput(BaseModel):
10
10
  age: int = Field(ge=0, le=150)
11
11
  ```
12
12
 
13
+ ## Validation contract gotchas
14
+
15
+ These traps came from repeated corrections during a DTO-to-client validation
16
+ rollout. Apply them when a client mirrors server rules; they do not require a
17
+ new generator for every small form.
18
+
19
+ - **Two validators can still be two conflicting policies.** Derive portable
20
+ client checks from the server's authoritative schema or resolved metadata.
21
+ A shared schema is also valid when the backend actually enforces it. Bind
22
+ field feedback and serialized-payload preflight to that same contract.
23
+ Backend enforcement remains mandatory, including when client code is bypassed.
24
+ - **The entity may never be validated.** Trace the operation's real input object.
25
+ In API Platform, an input DTO can be validated before mapping to an entity.
26
+ Validation groups select constraints; denormalization groups select writable
27
+ fields. Output-only constraints add no protection to the request path.
28
+ - **PATCH does not make every property optional.** An update hydrated from
29
+ existing state and a newly constructed input DTO have different missing-field
30
+ semantics. Preserve actual defaults and distinguish omitted keys, null and
31
+ empty values. Resolve this per operation, not from the HTTP method alone.
32
+ - **A constraint name is not a finite bound.** Length(min), Count(min),
33
+ All(Email) and an unbounded regex do not cap input size. Check actual maximum
34
+ values, collection size, item shape/type and nested validation separately.
35
+ In Symfony, retain All/Collection/Valid and sequential evaluation semantics.
36
+ Bound body size and violation output too; stop expensive item validation
37
+ after an oversized list is refused.
38
+ - **A field name does not identify its storage or wire format.** Read the
39
+ mapper/processor and owning storage before choosing limits. An Id field can
40
+ accept an IRI; a timestamp ceiling does not validate a real date. Feature
41
+ limits and established domain error codes can be stricter or more specific
42
+ than a generic ceiling. Do not replace them accidentally in a bulk pass.
43
+ - **Unicode length has several units.** Specify bytes, code units, codepoints
44
+ or grapheme clusters and the normalization order. Test decomposed accents,
45
+ non-BMP characters, joined emoji and IME composition. A client must not
46
+ silently truncate a value the server accepts; preserve input when showing an
47
+ error. Do not normalize passwords or apply identifier alphabet rules to
48
+ names without a documented product policy.
49
+ - **Rule names hide runtime options.** Email modes, URL schemes, numeric
50
+ coercion, regex dialects and conditional validation can differ across
51
+ languages. Export supported options explicitly. Conditional rules are
52
+ audited data, never arbitrary server expressions evaluated in the client.
53
+ - **Some checks depend on server state or libraries.** Keep authorization,
54
+ availability, uniqueness and domain invariants on the server. If a portable
55
+ equivalent is unavailable, record the exact operation/field/rule and reason
56
+ in reviewed policy. Newly unsupported rules must surface as a failed
57
+ generation/check step, not become an always-valid adapter. Bodyless routes
58
+ and exclusions are separate measurements, not evidence of client coverage.
59
+ - **A local refusal is not an HTTP response.** Use a distinct local error type,
60
+ preserve field paths and rule codes, and show localized field feedback.
61
+ Do not invent an HTTP status or trigger token refresh/network retry for a
62
+ request that was never sent. Server refusals remain authoritative; keep
63
+ local/server error state distinguishable and test correction/resubmission.
64
+ - **Generation proves synchronization, not semantic equivalence.** Run shared
65
+ accepted/rejected fixtures through the real backend validator and client
66
+ evaluator, including both conditional branches and nested error paths.
67
+ Check deterministic generation and reviewed exclusions in CI. A snapshot
68
+ of the exporter tested against itself does not prove parity. HTTP tests
69
+ must also exercise deserialization and rejected-write persistence behavior.
70
+
71
+ Input validation does not replace parameterized queries or context-appropriate
72
+ output encoding. The backend may still reject a client-valid request because
73
+ state changed after local validation.
74
+
75
+ For regression selection, use the "Validation contract regressions" section
76
+ of the installed `testing-patterns` skill. For HTTP failure semantics, read
77
+ `reference/error-contracts.md` from the installed `api-patterns` skill.
78
+ Locate these skills through the current client's catalog because adapters may
79
+ namespace their directory names.
80
+
13
81
  ## SQL Injection Prevention
14
82
  ```python
15
83
  # Bad
@@ -53,6 +53,25 @@ tests/
53
53
 
54
54
  ---
55
55
 
56
+ ## Validation contract regressions
57
+
58
+ Mirrored validators can agree on a generated snapshot while disagreeing on real
59
+ inputs. Execute the same accepted/rejected fixtures against the real backend
60
+ validator and the client evaluator. Include N/N+1 boundaries, collection/item
61
+ limits, omitted/null/empty/default values, nested paths, conditional branches,
62
+ hydrated updates versus fresh DTOs, Unicode units and previously valid inputs.
63
+
64
+ Test the actual HTTP path for malformed input and unchanged persisted state
65
+ after a pre-write refusal. Test zero transport calls for a local refusal,
66
+ normal transport for a valid payload, and field correction/resubmission.
67
+ Run deterministic-generation and reviewed-exclusion checks separately from
68
+ behavioral parity. A schema/rule count is inventory, not assertion coverage.
69
+
70
+ For operation semantics and runtime mismatches, read
71
+ `reference/input-validation.md` from the installed `security-patterns` skill.
72
+ Resolve the skill through the current client's catalog; directory names may
73
+ carry an adapter-specific prefix.
74
+
56
75
  ## Language-Specific References
57
76
 
58
77
  | Language | Reference | Key Topics |
package/app/surface.json CHANGED
@@ -16,6 +16,7 @@
16
16
  "chaos",
17
17
  "ci",
18
18
  "ci-cd-patterns",
19
+ "claude-toolkit-rules",
19
20
  "clean-code",
20
21
  "command-creator",
21
22
  "commit",
@@ -36,12 +37,14 @@
36
37
  "docs",
37
38
  "documentation-standards",
38
39
  "ecommerce-patterns",
40
+ "edit-discipline",
39
41
  "evaluate",
40
42
  "evolve",
41
43
  "explain",
42
44
  "explore",
43
45
  "fix",
44
46
  "flutter-patterns",
47
+ "git-conventions",
45
48
  "git-mastery",
46
49
  "golang-rules",
47
50
  "grill-me",
@@ -68,6 +71,7 @@
68
71
  "observability-patterns",
69
72
  "onboard",
70
73
  "orchestrate",
74
+ "output-mode",
71
75
  "panic",
72
76
  "performance-profiling",
73
77
  "persona",
@@ -81,6 +85,7 @@
81
85
  "prompt-caching-patterns",
82
86
  "python-rules",
83
87
  "qa-session",
88
+ "quality-gates",
84
89
  "rag-patterns",
85
90
  "refactor",
86
91
  "refactor-plan",