@mrciphersmith/keryx 0.3.5 → 0.3.7

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 (84) hide show
  1. package/README.md +47 -0
  2. package/dist/cli.js +3099 -1160
  3. package/dist/core.js +22 -3
  4. package/docs/README.md +2 -0
  5. package/package.json +1 -1
  6. package/src/gdskills/bundled/install-manifest.json +319 -76
  7. package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.md +19 -26
  8. package/src/gdskills/bundled/skills/orchestration/flow-orchestrator/SKILL.md +6 -6
  9. package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.md +4 -7
  10. package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.md +2 -4
  11. package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.detail.md +23 -0
  12. package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.md +17 -17
  13. package/src/gdskills/bundled/stacks/django/agent-refs.json +3 -0
  14. package/src/gdskills/bundled/stacks/django/governance/eval.json +1763 -0
  15. package/src/gdskills/bundled/stacks/django/governance/scout.json +40 -0
  16. package/src/gdskills/bundled/stacks/django/pack.json +43 -0
  17. package/src/gdskills/bundled/stacks/django/rules/coding-style.mdc +80 -0
  18. package/src/gdskills/bundled/stacks/django/rules/patterns.mdc +92 -0
  19. package/src/gdskills/bundled/stacks/django/rules/security.mdc +92 -0
  20. package/src/gdskills/bundled/stacks/django/rules/testing.mdc +89 -0
  21. package/src/gdskills/bundled/stacks/django/skills/django-build-fix/SKILL.md +149 -0
  22. package/src/gdskills/bundled/stacks/django/skills/django-build-fix/evals.json +49 -0
  23. package/src/gdskills/bundled/stacks/django/skills/django-code-review/SKILL.md +137 -0
  24. package/src/gdskills/bundled/stacks/django/skills/django-code-review/evals.json +48 -0
  25. package/src/gdskills/bundled/stacks/django/skills/django-implementation/SKILL.md +147 -0
  26. package/src/gdskills/bundled/stacks/django/skills/django-implementation/evals.json +75 -0
  27. package/src/gdskills/bundled/stacks/django/skills/django-migrate/SKILL.md +166 -0
  28. package/src/gdskills/bundled/stacks/django/skills/django-migrate/evals.json +49 -0
  29. package/src/gdskills/bundled/stacks/django/skills/django-testing/SKILL.md +130 -0
  30. package/src/gdskills/bundled/stacks/django/skills/django-testing/evals.json +48 -0
  31. package/src/gdskills/bundled/stacks/fastapi/agent-refs.json +3 -0
  32. package/src/gdskills/bundled/stacks/fastapi/governance/eval.json +1777 -0
  33. package/src/gdskills/bundled/stacks/fastapi/governance/scout.json +34 -0
  34. package/src/gdskills/bundled/stacks/fastapi/pack.json +43 -0
  35. package/src/gdskills/bundled/stacks/fastapi/rules/coding-style.mdc +68 -0
  36. package/src/gdskills/bundled/stacks/fastapi/rules/patterns.mdc +108 -0
  37. package/src/gdskills/bundled/stacks/fastapi/rules/security.mdc +99 -0
  38. package/src/gdskills/bundled/stacks/fastapi/rules/testing.mdc +85 -0
  39. package/src/gdskills/bundled/stacks/fastapi/skills/fastapi-build-fix/SKILL.md +157 -0
  40. package/src/gdskills/bundled/stacks/fastapi/skills/fastapi-build-fix/evals.json +76 -0
  41. package/src/gdskills/bundled/stacks/fastapi/skills/fastapi-code-review/SKILL.md +150 -0
  42. package/src/gdskills/bundled/stacks/fastapi/skills/fastapi-code-review/evals.json +74 -0
  43. package/src/gdskills/bundled/stacks/fastapi/skills/fastapi-implementation/SKILL.md +158 -0
  44. package/src/gdskills/bundled/stacks/fastapi/skills/fastapi-implementation/evals.json +75 -0
  45. package/src/gdskills/bundled/stacks/fastapi/skills/fastapi-testing/SKILL.md +146 -0
  46. package/src/gdskills/bundled/stacks/fastapi/skills/fastapi-testing/evals.json +74 -0
  47. package/src/gdskills/bundled/stacks/java-kotlin-spring/agent-refs.json +3 -0
  48. package/src/gdskills/bundled/stacks/java-kotlin-spring/governance/eval.json +2194 -0
  49. package/src/gdskills/bundled/stacks/java-kotlin-spring/governance/scout.json +39 -0
  50. package/src/gdskills/bundled/stacks/java-kotlin-spring/pack.json +40 -0
  51. package/src/gdskills/bundled/stacks/java-kotlin-spring/rules/coding-style.mdc +67 -0
  52. package/src/gdskills/bundled/stacks/java-kotlin-spring/rules/patterns.mdc +65 -0
  53. package/src/gdskills/bundled/stacks/java-kotlin-spring/rules/security.mdc +69 -0
  54. package/src/gdskills/bundled/stacks/java-kotlin-spring/rules/testing.mdc +80 -0
  55. package/src/gdskills/bundled/stacks/java-kotlin-spring/skills/java-kotlin-spring-build-fix/SKILL.md +144 -0
  56. package/src/gdskills/bundled/stacks/java-kotlin-spring/skills/java-kotlin-spring-build-fix/evals.json +74 -0
  57. package/src/gdskills/bundled/stacks/java-kotlin-spring/skills/java-kotlin-spring-code-review/SKILL.md +129 -0
  58. package/src/gdskills/bundled/stacks/java-kotlin-spring/skills/java-kotlin-spring-code-review/evals.json +74 -0
  59. package/src/gdskills/bundled/stacks/java-kotlin-spring/skills/java-kotlin-spring-implementation/SKILL.md +147 -0
  60. package/src/gdskills/bundled/stacks/java-kotlin-spring/skills/java-kotlin-spring-implementation/evals.json +75 -0
  61. package/src/gdskills/bundled/stacks/java-kotlin-spring/skills/java-kotlin-spring-migrate/SKILL.md +139 -0
  62. package/src/gdskills/bundled/stacks/java-kotlin-spring/skills/java-kotlin-spring-migrate/evals.json +74 -0
  63. package/src/gdskills/bundled/stacks/java-kotlin-spring/skills/java-kotlin-spring-testing/SKILL.md +128 -0
  64. package/src/gdskills/bundled/stacks/java-kotlin-spring/skills/java-kotlin-spring-testing/evals.json +73 -0
  65. package/src/gdskills/bundled/stacks/python/agent-refs.json +2 -1
  66. package/src/gdskills/bundled/stacks/python/pack.json +1 -1
  67. package/src/gdskills/bundled/stacks/rust/agent-refs.json +3 -0
  68. package/src/gdskills/bundled/stacks/rust/governance/eval.json +1823 -0
  69. package/src/gdskills/bundled/stacks/rust/governance/scout.json +32 -0
  70. package/src/gdskills/bundled/stacks/rust/pack.json +42 -0
  71. package/src/gdskills/bundled/stacks/rust/rules/coding-style.mdc +93 -0
  72. package/src/gdskills/bundled/stacks/rust/rules/patterns.mdc +85 -0
  73. package/src/gdskills/bundled/stacks/rust/rules/security.mdc +85 -0
  74. package/src/gdskills/bundled/stacks/rust/rules/testing.mdc +82 -0
  75. package/src/gdskills/bundled/stacks/rust/skills/rust-build-fix/SKILL.md +141 -0
  76. package/src/gdskills/bundled/stacks/rust/skills/rust-build-fix/evals.json +78 -0
  77. package/src/gdskills/bundled/stacks/rust/skills/rust-code-review/SKILL.md +127 -0
  78. package/src/gdskills/bundled/stacks/rust/skills/rust-code-review/evals.json +72 -0
  79. package/src/gdskills/bundled/stacks/rust/skills/rust-implementation/SKILL.md +133 -0
  80. package/src/gdskills/bundled/stacks/rust/skills/rust-implementation/evals.json +79 -0
  81. package/src/gdskills/bundled/stacks/rust/skills/rust-testing/SKILL.md +130 -0
  82. package/src/gdskills/bundled/stacks/rust/skills/rust-testing/evals.json +75 -0
  83. package/src/gdskills/bundled/agents/python-build-fixer.md +0 -52
  84. package/src/gdskills/bundled/agents/python-code-auditor.md +0 -49
@@ -0,0 +1,146 @@
1
+ ---
2
+ name: fastapi-testing
3
+ description: "Use when you write, extend, or fix a FastAPI project's test suite -- driving synchronous or async HTTP requests against the running app in-process, app.dependency_overrides for auth/DB fixtures, asserting response status codes and response_model filtering, and mocking an external HTTP call. Scoped to a project that actually has a FastAPI HTTP layer under test, not a generic Python test suite with none."
4
+ triggers:
5
+ - "write a pytest test for this FastAPI endpoint"
6
+ - "drive an in-process request against this FastAPI route in a test"
7
+ - "add dependency_overrides for the current user in this test"
8
+ - "test that this endpoint rejects a malformed request body with a 422"
9
+ - "mock the external API call in this FastAPI test"
10
+ - "write tests for this FastAPI router's additional endpoints"
11
+ metadata:
12
+ origin: authored
13
+ category: test
14
+ version: "1.0.0"
15
+ compatible_harnesses: "claude,codex,cursor,zed,opencode"
16
+ license: "MIT"
17
+ ---
18
+
19
+ # FastAPI testing
20
+
21
+ Write, extend, or fix a FastAPI project's test suite: exercising path
22
+ operations through the real `app` instance via `TestClient`/
23
+ `httpx.AsyncClient`, overriding dependencies the FastAPI-supported way, and
24
+ asserting the response contract (status code, body shape, `response_model`
25
+ filtering) rather than just that a call didn't raise. Scoped to FastAPI's
26
+ own test-client and dependency-override conventions — for testing a plain
27
+ Python function with no HTTP layer use `python-testing`; for reviewing test
28
+ conventions without changing files use `review-testing-practices`.
29
+
30
+ ## Workflow
31
+
32
+ ### Step 1: Discover the project's FastAPI test conventions
33
+
34
+ 1. Read `pyproject.toml`/`pytest.ini` for pytest config, and check whether
35
+ `httpx`'s `ASGITransport` or `fastapi.testclient.TestClient` is already
36
+ in use — match whichever the project already uses rather than
37
+ introducing the other.
38
+ 2. Find the existing `app`/`client` fixture (usually in `conftest.py`) and
39
+ any existing `dependency_overrides` pattern for auth or the DB session —
40
+ reuse it rather than building a parallel one.
41
+ 3. Read 1-2 neighboring endpoint tests for: how the DB is set up for tests
42
+ (throwaway SQLite, a test-scoped Postgres schema, a fixture-provided
43
+ session), how auth is faked, and the project's assertion style.
44
+
45
+ ### Step 2: Plan fixtures before test cases
46
+
47
+ - A `client`/`app` fixture belongs in the narrowest `conftest.py` that
48
+ covers every test file needing it, matching `python`'s own fixture-scope
49
+ guidance.
50
+ - Plan `dependency_overrides` per test (or per fixture, yielding and
51
+ clearing) rather than setting them once at module import time with no
52
+ teardown — an override left set leaks into unrelated tests.
53
+ - Decide the DB fixture strategy: a real throwaway test database behind a
54
+ `get_db` override for integration-level coverage, reserving a mocked
55
+ session only for the rare case where isolating from the DB entirely is
56
+ the actual point of the test.
57
+
58
+ ### Step 3: Plan test cases
59
+
60
+ **Per path operation:** the success path (status code + body shape),
61
+ the validation-failure path (a `422` when the request body fails Pydantic
62
+ validation), any documented error path (a `404`/`403`/`409` the endpoint
63
+ raises deliberately), and — when the endpoint requires auth — both an
64
+ authenticated and an unauthenticated case via `dependency_overrides`.
65
+
66
+ **`response_model` filtering:** when a path operation declares
67
+ `response_model`, at least one test asserts that a field the model
68
+ excludes (e.g. a password hash) is actually absent from the response body.
69
+
70
+ **External calls:** mock at the client boundary (`httpx`/`requests` mock,
71
+ or the project's configured HTTP-mocking library) — never mock an internal
72
+ collaborator in the same package, matching `python-testing`'s own rule.
73
+
74
+ ### Step 4: Write
75
+
76
+ 1. Build the request through `TestClient(app)`/`AsyncClient(transport=
77
+ ASGITransport(app=app), base_url="http://test")`, matching the
78
+ project's own style from Step 1.
79
+ 2. Override dependencies with `app.dependency_overrides[real_dep] =
80
+ fake_dep`, clearing them in teardown (fixture `yield` + cleanup, or an
81
+ explicit `.clear()`), never by monkeypatching the dependency's module
82
+ attribute.
83
+ 3. Assert `response.status_code` and the parsed `response.json()` body,
84
+ not merely that the call completed without raising.
85
+ 4. Use `pytest.mark.parametrize` for input variations across the same
86
+ endpoint (e.g. several invalid-body shapes that should each 422).
87
+
88
+ ### Step 5: Run and fix
89
+
90
+ ```bash
91
+ keryx test run --changed --strict
92
+ ```
93
+
94
+ `src/testing/service.ts` detects the project's own test runner — do not
95
+ hard-code `pytest` invocation flags beyond what Step 1 discovered. With no
96
+ keryx testing config, run the project's own configured `pytest` invocation.
97
+
98
+ Fix failing tests (max 3 iterations) — fix the test, not the source under
99
+ test.
100
+
101
+ ### Step 6: Report
102
+
103
+ ```
104
+ Generated: tests/routers/test_users.py
105
+ - 6 test cases: success, 422 on bad body, 404 on missing user,
106
+ 401 without auth override, 200 with auth override, response_model
107
+ excludes hashed_password
108
+ ```
109
+
110
+ ## Rules
111
+
112
+ - ALWAYS test through `TestClient`/`AsyncClient` against the real `app`,
113
+ not by calling the path operation function directly.
114
+ - ALWAYS clear any `app.dependency_overrides` entry the test set, so it
115
+ cannot leak into a later test in the same process.
116
+ - NEVER modify source code — only test files and `conftest.py`.
117
+ - Mock genuinely external dependencies (a third-party HTTP API, an email
118
+ provider), not internal modules under the same package.
119
+
120
+ ## Red Flags
121
+
122
+ | Rationalization | Why it is wrong |
123
+ |---|---|
124
+ | "I'll call `read_user(user_id=1, db=fake_db)` directly, it's faster than spinning up TestClient" | Skips FastAPI's own request parsing, dependency resolution, and response filtering — exactly where most path-operation bugs live |
125
+ | "I'll set `app.dependency_overrides[get_current_user] = fake_user` once at the top of the file" | With no teardown, this override leaks into every other test in the same process, including ones that meant to test the unauthenticated path |
126
+ | "I'll use `mocker.patch(\"myapp.dependencies.get_current_user\", ...)` instead of `dependency_overrides`" | Patches the function at its module attribute instead of using FastAPI's own supported override mechanism; `app.dependency_overrides` is what keeps the fake scoped to requests actually routed through the app |
127
+ | "The endpoint didn't raise, so the test passes" | A wrong status code or a malformed body with no exception is a real bug a bare no-exception check misses; assert the actual response contract |
128
+ | "I'll mock the DB session so the test doesn't need a real database" | A mocked ORM session only checks the code called the mock the way the mock expects; use a real throwaway test database behind a `get_db` override for anything beyond the simplest unit test |
129
+
130
+ ## Verification
131
+
132
+ Do not report the work done until all of the following hold:
133
+
134
+ - Every new/extended test exercises the path operation through
135
+ `TestClient`/`AsyncClient` against the real `app`, not the function
136
+ directly.
137
+ - Any `app.dependency_overrides` entry set by a test is cleared afterward
138
+ (fixture teardown or explicit `.clear()`).
139
+ - `keryx test run --changed --strict` — or, with no keryx testing config,
140
+ the project's own discovered `pytest` command — exits 0.
141
+ - `git status` shows only test files (and `conftest.py`, if touched)
142
+ added or modified; no source file under test changed.
143
+ - Every new/touched path operation identified in Step 1 has coverage for
144
+ its success path, its validation-failure path, and, when it requires
145
+ auth, both an authenticated and unauthenticated case — or the report
146
+ says why one is missing.
@@ -0,0 +1,74 @@
1
+ {
2
+ "triggers": {
3
+ "positive": [
4
+ "What should the pytest suite look like for the new FastAPI order-creation route -- I want to make sure a valid POST actually persists the order.",
5
+ "Test this FastAPI route with TestClient to confirm it returns a 404",
6
+ "How do I fake being logged in when testing this FastAPI route, without hitting the real auth flow?",
7
+ "Test that this FastAPI endpoint returns a 422 on an invalid request body",
8
+ "This test keeps making a real network call to Stripe during CI -- how do I stub that out properly?",
9
+ "Add test coverage for this FastAPI router's signup endpoint"
10
+ ],
11
+ "negative": [
12
+ "Write a pytest test for this plain Python function with no HTTP layer at all",
13
+ "Add pytest-django coverage for this Django view, not a FastAPI route",
14
+ "Implement this FastAPI endpoint that creates an order",
15
+ "Review this FastAPI test file for testing convention violations without changing it",
16
+ "Fix this pytest collection error in our FastAPI test suite",
17
+ "Write a Jest test for this React component",
18
+ "Review this FastAPI diff for security issues before merging"
19
+ ]
20
+ },
21
+ "scenarios": [
22
+ {
23
+ "id": "dependency-overrides-not-monkeypatch",
24
+ "prompt": "Write a pytest test for a FastAPI endpoint `GET /me` that requires an authenticated user via a `get_current_user` dependency. How should the test supply a fake authenticated user?",
25
+ "strictness": "high",
26
+ "expected_behavior": [
27
+ {
28
+ "grader": "judge",
29
+ "rubric": "A correct answer overrides the dependency with `app.dependency_overrides[get_current_user] = fake_dependency`, sent through TestClient against the real app, and clears the override afterward -- rather than monkeypatching the dependency function's module attribute.",
30
+ "pass_criteria": [
31
+ "sets `app.dependency_overrides[get_current_user] = <fake>` (or the equivalent for the project's actual dependency), shown as concrete code, to supply the fake authenticated user",
32
+ "sends the request through `TestClient(app)` (or `httpx.AsyncClient` against the app) so the override is exercised through FastAPI's own request pipeline",
33
+ "clears the override afterward (e.g. `app.dependency_overrides.clear()` in a fixture teardown or at the end of the test) so it does not leak into other tests"
34
+ ],
35
+ "fail_criteria": [
36
+ "uses `mocker.patch`/`monkeypatch.setattr` on the `get_current_user` function's module attribute instead of `app.dependency_overrides`. Mentioning that this would be the wrong approach is not itself a failure."
37
+ ]
38
+ }
39
+ ],
40
+ "calibration": {
41
+ "known_right": "```python\nfrom myapp.main import app\nfrom myapp.dependencies import get_current_user\nfrom myapp.schemas import User\n\n\ndef fake_current_user() -> User:\n return User(id=1, username=\"testuser\")\n\n\ndef test_me_returns_authenticated_user(client):\n app.dependency_overrides[get_current_user] = fake_current_user\n try:\n response = client.get(\"/me\")\n finally:\n app.dependency_overrides.clear()\n assert response.status_code == 200\n assert response.json()[\"username\"] == \"testuser\"\n```\nThe fake user is supplied by setting `app.dependency_overrides[get_current_user]`, FastAPI's own supported override mechanism, so the real `/me` request still goes through `TestClient(app)` and exercises the actual request pipeline -- routing, other dependencies, response_model filtering -- with only that one dependency swapped. The `try`/`finally` clears the override afterward so it can't leak into a later test that expects the real dependency or a different fake.",
42
+ "known_wrong": "```python\ndef test_me_returns_authenticated_user(client, mocker):\n mocker.patch(\"myapp.dependencies.get_current_user\", return_value=User(id=1, username=\"testuser\"))\n response = client.get(\"/me\")\n assert response.status_code == 200\n assert response.json()[\"username\"] == \"testuser\"\n```\nUsing `mocker.patch` on `get_current_user` directly is a familiar pattern from regular Python unit testing -- patch the function, call the code, assert the result -- and it avoids having to learn FastAPI's own override API. Since `mocker.patch` automatically undoes itself at the end of the test via pytest-mock's fixture teardown, there's no extra cleanup to write either.",
43
+ "vague": "Swap in a fake user for the auth dependency using FastAPI's own mechanism for overriding it in tests, and make sure the override doesn't stick around for other tests.",
44
+ "subtle_wrong": "```python\ndef test_me_returns_authenticated_user(client, mocker):\n mocker.patch(\"myapp.routers.me.get_current_user\", return_value=User(id=1, username=\"testuser\"))\n response = client.get(\"/me\")\n assert response.status_code == 200\n assert response.json()[\"username\"] == \"testuser\"\n```\nPatches `get_current_user` at its point of use in the `me` router module, following the same 'patch at the point of use' guidance as any other pytest mock, rather than patching FastAPI's dependency-injection system directly -- since `Depends(get_current_user)` resolves the name from the router module's own namespace, patching it there should intercept the same call the endpoint actually makes."
45
+ },
46
+ "anti_patterns": ["mocker.patch"]
47
+ },
48
+ {
49
+ "id": "response-model-excludes-field",
50
+ "prompt": "Write a pytest test for a FastAPI `GET /users/{id}` endpoint that declares `response_model=UserRead`, where the underlying `User` object also carries a `hashed_password` field that `UserRead` does not include. What should the test verify beyond a 200 status code?",
51
+ "strictness": "high",
52
+ "expected_behavior": [
53
+ {
54
+ "grader": "judge",
55
+ "rubric": "A correct answer asserts, in addition to the 200 status code, that the response JSON body does not contain the hashed_password field -- verifying response_model actually filtered it out -- rather than stopping at the status code check.",
56
+ "pass_criteria": [
57
+ "asserts `response.status_code == 200` (or equivalent) as part of the test",
58
+ "additionally asserts that `hashed_password` (or the specific excluded field) is absent from `response.json()`, shown as concrete code such as `assert \"hashed_password\" not in response.json()`",
59
+ "explains that this verifies `response_model` actually filtered the field out of the response, not merely that the request succeeded"
60
+ ],
61
+ "fail_criteria": [
62
+ "only asserts the status code (or that the call didn't raise) without checking the response body for the excluded field. Mentioning that a field could leak is not itself a failure."
63
+ ]
64
+ }
65
+ ],
66
+ "calibration": {
67
+ "known_right": "```python\ndef test_get_user_excludes_hashed_password(client, seeded_user):\n response = client.get(f\"/users/{seeded_user.id}\")\n assert response.status_code == 200\n body = response.json()\n assert \"hashed_password\" not in body\n assert body[\"username\"] == seeded_user.username\n```\nBeyond the 200 status code, the test explicitly asserts `\"hashed_password\" not in body`. `response_model=UserRead` is supposed to filter the returned `User` object down to just the fields `UserRead` declares, but that's a property of the schema definition matching the endpoint's actual intent -- a typo or a forgotten field removal in `UserRead` would otherwise ship a leaked field with every unit test still green, since a status-code-only check can't catch it. Checking `username` too confirms the response still carries the fields it should.",
68
+ "known_wrong": "```python\ndef test_get_user_returns_ok(client, seeded_user):\n response = client.get(f\"/users/{seeded_user.id}\")\n assert response.status_code == 200\n```\nA 200 status code confirms the endpoint found the user and returned a response without erroring, which is the main thing this test needs to verify. Since `response_model=UserRead` is declared on the path operation, FastAPI handles the field filtering automatically at the framework level -- that's exactly what response_model is for -- so there's no need for the test itself to re-check which fields ended up in the body.",
69
+ "vague": "Check more than just the status code -- make sure the response body actually reflects what response_model is supposed to filter down to.",
70
+ "subtle_wrong": "```python\ndef test_get_user_returns_ok(client, seeded_user):\n response = client.get(f\"/users/{seeded_user.id}\")\n assert response.status_code == 200\n body = response.json()\n assert body[\"username\"] == seeded_user.username\n assert body[\"id\"] == seeded_user.id\n```\nChecks the status code and confirms the expected fields (`username`, `id`) are present and correct in the response body, which is the main behavior the endpoint needs to get right -- if `UserRead` is declared correctly, verifying the fields that should be there is enough evidence the schema is working as intended."
71
+ }
72
+ }
73
+ ]
74
+ }