@softspark/ai-toolkit 4.32.3 → 4.33.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/CHANGELOG.md CHANGED
@@ -7,6 +7,67 @@ Versioning follows [Semantic Versioning](https://semver.org/).
7
7
 
8
8
  ---
9
9
 
10
+ ## v4.33.0 - DSH refresh and maintenance contracts (2026-09-06)
11
+
12
+ ### Changed
13
+
14
+ - Explicit DSH profile lifecycle targets runtime `0.1.2-rc.1` and exact packages
15
+ `@softspark/dsh-codex@1.5.0` and `@softspark/dsh-orchestrator@2.0.0`.
16
+ Upgrade the DSH runtime before updating the managed profile packages.
17
+ - Refresh the ecosystem source references and retain the pnpm 11 support range
18
+ declared by the reviewed upstream release.
19
+
20
+ ### Fixed
21
+
22
+ - Automatically select Claude Agent SDK `0.3.263` for the DSH Claude provider,
23
+ replacing the bundled CLI rejected by current models. Preserve unrelated
24
+ profile settings and restore prior configuration after failed installation
25
+ or update; concurrent file replacements remain preserved for recovery.
26
+ - Align the security support table with the current 4.x release line and describe
27
+ the actual scope of hooks, network-aware commands, and managed installation files.
28
+ - Require documentation, generated surfaces, and counts to change in the same
29
+ pull request as the corresponding behavior.
30
+ - Cover migration from the previously shipped DSH package pair and retain
31
+ rollback tests that restore the exact recorded package versions.
32
+ - Canonicalize temporary test roots on macOS so recovery assertions compare
33
+ the same paths reported by the lifecycle without weakening the assertions.
34
+
35
+ ## v4.32.4 - Validation parity and API error contracts (2026-09-06)
36
+
37
+ ### Changed
38
+
39
+ - **Validation contracts:** Add practical guidance to `/api-patterns`,
40
+ `/security-patterns`, `/review` and `/testing-patterns` for a shared backend
41
+ and frontend rule source, operation-specific DTO groups/defaults, finite
42
+ collection bounds, Unicode units, explicit server-only checks and parity
43
+ fixtures. Local validation failures remain distinct from HTTP responses.
44
+ Cross-skill references resolve through the installed catalog so namespaced
45
+ adapters, including GitHub Copilot, can find the same guidance.
46
+ - **API error contracts:** Preserve the host's error representation and
47
+ distinguish domain refusals from technical failures. Cover actionable
48
+ messages, JSON types, background-job failures, unknown outcomes and safe
49
+ retries. Common security/testing rules retain shared-database test isolation.
50
+
51
+ ### Fixed
52
+
53
+ - **Schema guidance:** Correct JSON Schema conditional requirements and Ajv
54
+ strict-mode semantics, retain domain checks alongside boundary validation,
55
+ and import `Field` in the Pydantic example. The FastAPI example catches a
56
+ specific request refusal while preserving its exception cause.
57
+ - **Release verification:** Compare inventories with the current artifact,
58
+ run installation smoke in a disposable environment, and fail when an
59
+ expected generated JSON file is missing. Preserve the operator's install
60
+ and authentication state during candidate and published-package checks.
61
+ - **Package metadata:** Refresh skill and hook inventory descriptions.
62
+
63
+ ### Release checks
64
+
65
+ - **Ecosystem:** Refresh documentation hashes and local CLI version probes;
66
+ the review found no heading or capability-marker changes and changes no
67
+ generators, runtime integration paths or permission declarations.
68
+ - **Skill body budget:** Keep the existing 18,000-byte warning threshold;
69
+ the largest body remains 17,197 bytes, so the ratchet cannot tighten yet.
70
+
10
71
  ## v4.32.3 — Search-first stops firing on things nobody asked (2026-09-04)
11
72
 
12
73
  ### Fixed
package/README.md CHANGED
@@ -6,30 +6,21 @@
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-1978%20passing-success)](tests/)
10
-
11
- ## What's New in v4.32.3
12
-
13
- **v4.32.3** stops search-first enforcement firing on things nobody asked:
14
-
15
- - **A background task finishing no longer blocks `Stop`.** The search-required
16
- flag was set on any stdin over 30 characters, and the harness delivers
17
- background-task notifications, CI events and replayed slash-command output
18
- on the same channel as a prompt. Event envelopes no longer set it.
19
- - **A pasted credential no longer becomes a search query.** A 43-character API
20
- key on its own line cleared the length gate, and the block message names the
21
- prompt as the thing to search for — so the enforcement path asked for a live
22
- secret to be sent to a retrieval service. Single-token prompts no longer set
23
- the flag.
24
- - Nothing that was enforced before stops being enforced: a genuine one-word
25
- prompt is under the length gate anyway, and prose that merely mentions a task
26
- notification still sets the flag. That case is now a test.
27
- - Still in this train: exports land in the directory you ran the command in
28
- (v4.32.2); `plugin remove` leaves nothing behind, every skill script answers
29
- `--help` (v4.32.1); path-scoped common rules, project-scoped language skills,
30
- `doctor` context-budget and permission checks, one strict frontmatter parser,
31
- `git-team` for `--profile strict` (v4.32.0). Test count: 1974 -> 1978 bats +
32
- 348 pytest.
9
+ [![Tests](https://img.shields.io/badge/tests-1989%20passing-success)](tests/)
10
+
11
+ ## What's New in v4.33.0
12
+
13
+ **v4.33.0** refreshes the explicit DSH lifecycle and public maintenance policies:
14
+
15
+ - **Current DSH package pins.** Explicit profile installation and updates target
16
+ DSH `0.1.2-rc.1`, `dsh-codex 1.5.0`, and `dsh-orchestrator 2.0.0`.
17
+ - **Existing profile ownership survives the update.** Regression tests cover
18
+ refreshing the previously shipped package pair and restoring the recorded
19
+ versions when an update fails.
20
+ - **Accurate security scope.** The support table follows the current release
21
+ line, and the policy describes the actual installation and network boundaries.
22
+ - **Documentation travels with code.** Contributions update affected KB pages,
23
+ manifests, counts, and generated instructions in the same pull request.
33
24
 
34
25
  See [CHANGELOG.md](CHANGELOG.md) for full history.
35
26
 
@@ -102,7 +93,7 @@ ai-toolkit dsh doctor --profile web
102
93
  ai-toolkit dsh uninstall --profile web --yes
103
94
  ```
104
95
 
105
- DSH is excluded from `--editors all`, auto-detection, and defaults. Its only DSH-specific project output is `.agents/skills`; the normal `--local` Claude files, detected language rules, and other generic project outputs still apply. Project and profile `--dry-run` commands are read-only. The reviewed pins are DSH `0.1.1-rc.2`, `@softspark/dsh-codex@1.0.0`, and `@softspark/dsh-orchestrator@1.0.1`. Codex, Claude Code, and GitHub Copilot own their logins. ai-toolkit accepts no provider API keys, and GitHub AI credits apply to the Copilot Gemini route. Isolated pre-tag and exact-registry post-release qualification completed both Claude Code and Copilot Gemini marker roundtrips through the Codex parent. See [DSH Compatibility](kb/reference/dsh-compatibility.md).
96
+ DSH is excluded from `--editors all`, auto-detection, and defaults. Its only DSH-specific project output is `.agents/skills`; the normal `--local` Claude files, detected language rules, and other generic project outputs still apply. Project and profile `--dry-run` commands are read-only. The lifecycle targets DSH `0.1.2-rc.1`, `@softspark/dsh-codex@1.5.0`, and `@softspark/dsh-orchestrator@2.0.0`. Update the DSH runtime before updating this profile integration. Codex, Claude Code, and GitHub Copilot own their logins. ai-toolkit accepts no provider API keys, and GitHub AI credits apply to the Copilot Gemini route. See [DSH Compatibility](kb/reference/dsh-compatibility.md) for the exact contract and version-specific qualification evidence.
106
97
 
107
98
  ### Plugin Management
108
99
 
@@ -211,7 +202,7 @@ ai-toolkit/
211
202
  │ ├── agents/ # 44 agent definitions
212
203
  │ ├── skills/ # 114 skills (task / hybrid / knowledge)
213
204
  │ ├── rules/ # Source rules synced into Claude/editor rule files
214
- │ ├── hooks/ # Hook scripts (29 entries, 14 lifecycle events)
205
+ │ ├── hooks/ # Hook scripts (28 entries, 14 lifecycle events)
215
206
  │ ├── claude-app/ # Generated Chat/Cowork plugin rules, hooks, instructions
216
207
  │ ├── plugins/ # 2 experimental plugin packs (opt-in)
217
208
  │ ├── 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.3",
6
+ "version": "4.33.0",
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.
@@ -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 |