@solaqua/ghosts 0.2.0 → 0.2.2

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.
@@ -0,0 +1,11 @@
1
+ # Grill Me
2
+
3
+ An interview that stress-tests a plan, decision, or idea through question rounds
4
+ ordered by their dependencies, ending with a short, grouped decision summary.
5
+
6
+ ## Credits
7
+
8
+ Shout-out to [Matt Pocock](https://github.com/mattpocock) for the original
9
+ [grilling skill](https://github.com/mattpocock/skills/blob/main/skills/productivity/grilling/SKILL.md).
10
+ This adaptation keeps its interview flow and adds guidance for a concise,
11
+ readable result summary.
@@ -0,0 +1,66 @@
1
+ ---
2
+ name: grill-me
3
+ description: Grill the user relentlessly about a plan, decision, or idea. Use when the user wants to stress-test their thinking or asks to be grilled.
4
+ ---
5
+
6
+ Interview the user relentlessly until you reach a shared understanding. Map
7
+ this as a design tree: every decision branches into the decisions that depend
8
+ on it.
9
+
10
+ Work the tree in rounds. The frontier is every decision whose prerequisites
11
+ are already settled: questions you can ask now without guessing at answers you
12
+ have not heard yet. Ask the whole frontier in one round, number each question,
13
+ and give your recommended answer. Then wait for the user's answers.
14
+
15
+ Format a round like this:
16
+
17
+ ```md
18
+ ❓ **Q1** - **<Question title>**: <Question, with choices when useful>
19
+
20
+ ➡️ <Recommended answer>
21
+
22
+ ---
23
+
24
+ ❓ **Q2** - **<Question title>**: <Question, with choices when useful>
25
+
26
+ ➡️ <Recommended answer>
27
+ ```
28
+
29
+ Each round reshapes the tree. Settled decisions unblock their dependent
30
+ questions; recompute the frontier and ask the next round. A question depending
31
+ on another question still open in this round belongs to a later round.
32
+
33
+ Finding facts is your job, never the user's. When a question needs a fact from
34
+ the environment, dispatch a subagent to find it. A running exploration is an
35
+ unsettled prerequisite: only downstream questions wait; ask the rest of the
36
+ frontier now. The decisions are the user's: put each to them and wait.
37
+
38
+ The session is done when every branch within scope is resolved and nothing
39
+ remains silently assumed. An empty frontier while research is pending is not
40
+ completion. Do not act until the user confirms the shared understanding.
41
+
42
+ Present the result using this template in the user's language. Replace
43
+ placeholders, repeat decision groups as needed, and omit constraints when none:
44
+
45
+ ```md
46
+ ## Outcome
47
+
48
+ <Agreed goal and direction in 1–2 sentences>
49
+
50
+ ## Decisions
51
+
52
+ ### <Related decision group>
53
+
54
+ - <Agreed choice and its key reason or accepted trade-off>
55
+
56
+ ## Constraints
57
+
58
+ - <Material scope boundary or constraint>
59
+
60
+ Does this capture our shared understanding?
61
+ ```
62
+
63
+ Keep it readable in one screen without scrolling. Include only what someone
64
+ needs to understand the result; omit the interview transcript, question IDs,
65
+ and full decision tree. Use a compact table or diagram only when it makes the
66
+ decisions easier to understand.
@@ -58,7 +58,7 @@ fixed ceremony.
58
58
  ```text
59
59
  [Understand] → [Route unknowns]
60
60
  ├─ evidence → [$loop-research] ┐
61
- ├─ user decision → [$socratic-interview] ├→ [Frame + ledger] → [Draft]
61
+ ├─ user decision → [$grill-me] ├→ [Frame + ledger] → [Draft]
62
62
  └─ known →─────────────────────┘ ↓
63
63
  [Any high-impact uncertainty?]
64
64
  ├─ No → [Self challenge]
@@ -136,20 +136,20 @@ Route unresolved inputs before making design decisions. Use `$loop-research` for
136
136
  repository or document investigation, current or contested evidence,
137
137
  comparisons, feasibility checks, or source citations; it returns verified
138
138
  claims, provenance, uncertainty, and leads that could change the plan. Use
139
- `$socratic-interview` for choices only the user can make, such as outcome,
140
- scope, priority, risk tolerance, or external action; it asks one focused
141
- question at a time and returns an execution brief, but does not plan or edit.
142
- Do not ask the user for facts inspection or research can establish, and do not
143
- replace a user decision with an inferred preference. Carry both outputs into
144
- the decision ledger.
145
-
146
- Ask the smallest question when an unknown could change scope, success, or a
147
- costly decision; continue independent inspection while waiting. State ordinary
148
- assumptions and proceed. If feasibility remains unestablished, put the cheapest
149
- useful investigation first with a decision criterion and keep dependent work
150
- conditional rather than inventing certainty. State task-specific principles,
151
- rank decision drivers, resolve conflicts explicitly, and define observable
152
- success before choosing.
139
+ `$grill-me` for choices only the user can make, such as outcome, scope, priority,
140
+ risk tolerance, or external action. It asks the whole ready frontier in each
141
+ round and returns a short, grouped decision summary. Wait for the user to confirm
142
+ the shared understanding before carrying its decisions into the plan. Do not
143
+ ask the user for facts that inspection or research can establish, or infer a
144
+ material preference. Carry both outputs into the decision ledger.
145
+
146
+ Resolve material user choices through the interview's dependency-aware rounds;
147
+ continue independent inspection while waiting. State ordinary assumptions only
148
+ when they do not replace a material user decision. If feasibility remains
149
+ unestablished, put the cheapest useful investigation first with a decision
150
+ criterion and keep dependent work conditional rather than inventing certainty.
151
+ State task-specific principles, rank decision drivers, resolve conflicts
152
+ explicitly, and define observable success before choosing.
153
153
 
154
154
  ## Compare and draft
155
155
 
@@ -0,0 +1,23 @@
1
+ ---
2
+ name: test-review
3
+ description: Read-only review of tests in a change, PR, or path against shared, frontend, and backend testing standards.
4
+ disable-model-invocation: true
5
+ ---
6
+
7
+ # Test Review
8
+
9
+ Review the tests read-only. If no target is given, review the tests in the current branch's diff against the base branch. Write in the user's language.
10
+
11
+ Always apply [references/common.md](references/common.md). Add [references/frontend.md](references/frontend.md) for UI or browser tests and [references/backend.md](references/backend.md) for server, worker, or data tests; use both when the change spans them. For another stack, apply only `common.md`.
12
+
13
+ 1. Map each changed behavior to the existing and new tests that protect it. Flag behavior no test would catch if it broke, and removed tests whose protection is not covered elsewhere.
14
+ 2. Review each added, changed, or removed test against the guides' review checklists.
15
+ 3. Keep only findings with a location, a concrete failure scenario, and a guide rule behind them. Running existing tests to confirm a finding is allowed; editing code or tests is not.
16
+
17
+ Prioritize findings:
18
+
19
+ - `P1`: a changed behavior has no test that would fail if it broke, or a test cannot fail for the regression it claims to catch.
20
+ - `P2`: implementation coupling, test doubles replacing the behavior under test, nondeterminism, leaking state, or redundant protection.
21
+ - `P3`: naming, structure, or readability.
22
+
23
+ Write the review following [templates/review.md](templates/review.md), replacing each placeholder or omitting the section as it directs.
@@ -0,0 +1,280 @@
1
+ # Backend Testing Guide
2
+
3
+ This document defines the shared testing standards for backend teams. Tests are not written just to see `Pass`. They are written to fail quickly and clearly when important behavior is broken.
4
+
5
+ Repository structure, fixture names, and test commands may differ by repository. This guide focuses on shared judgment criteria for writing and reviewing backend tests, rather than repository-specific usage.
6
+
7
+ ## 1. Testing Philosophy
8
+
9
+ ### 1.1 Meaningful Failure
10
+
11
+ - A test should fail when the behavior it protects is broken.
12
+ - A test that keeps passing after a requirement change or bug does not provide safety. It only adds maintenance cost.
13
+ - If a refactor breaks a test while observable behavior remains the same, the test is likely coupled too tightly to implementation details.
14
+ - Regression tests and snapshot tests are useful when they protect contracts that must not change accidentally.
15
+ - If a test failure does not reveal which requirement was broken, improve the test name, setup, or assertions.
16
+
17
+ ### 1.2 Choose Test Targets By Value
18
+
19
+ Apply [Test Target Value](common.md#test-target-value), and:
20
+
21
+ - When an integration test already protects a behavior, do not automatically repeat it in unit tests for every participating layer. Add focused tests where they provide additional protection or clearer checks of important edge cases.
22
+
23
+ High-priority targets:
24
+
25
+ - Business-critical flows such as payment, authentication, authorization, subscriptions, and event publishing
26
+ - Tasks and workers that are expensive to recover from when they fail
27
+ - Complex queries, pagination, cursors, ordering, locks, and idempotency
28
+ - Logic that interprets external system responses and mutates local state
29
+ - Edge-case-heavy logic such as dates, timezones, market holidays, exchange rates, decimals, and discounts
30
+
31
+ Lower-priority targets:
32
+
33
+ - Simple getters, setters, and DTO declarations
34
+ - Behavior already guaranteed by the library
35
+ - Shape errors that static analysis, type checkers, or linters catch better
36
+ - Tests that only repeat the implementation without improving behavioral safety
37
+
38
+ ## 2. Test Scope Strategy
39
+
40
+ Prefer tests that keep the real collaboration needed to deliver a feature intact. A test can exercise an API, service, repository, and DB together while protecting one clearly defined behavior. The number of participating components and the number of concerns being tested are separate choices.
41
+
42
+ Apply [Focused Scenarios](common.md#focused-scenarios).
43
+
44
+ Recommended approach:
45
+
46
+ 1. First check whether the behavior can be tested through an existing public entrypoint from the perspective of a user or external system. Do not expand production interfaces solely for testing convenience.
47
+ 2. Prefer integration tests when API, service, repository, DB, and cache behavior can be verified together clearly.
48
+ 3. Narrow the scope one level when the integration test becomes too slow, too hard to set up, or too hard to debug.
49
+ 4. Use unit tests directly for naturally small units such as pure calculations, parsers, and validators.
50
+ 5. Replace external systems we do not control at the test boundary, such as third-party APIs, payment providers, email, and object storage.
51
+
52
+ Why we prefer larger tests:
53
+
54
+ - They catch real wiring, transaction, serialization, and dependency override issues.
55
+ - They remain stable when internal calls change but the feature behavior stays the same.
56
+ - They produce a signal closer to production behavior than tests with heavy mocking.
57
+
58
+ When to narrow the scope:
59
+
60
+ - The setup is so large that the important condition is hard to see.
61
+ - The failure cause is difficult to isolate.
62
+ - Replacing external dependencies takes up most of the test.
63
+ - Slow tests start hurting the feedback loop.
64
+ - One complex rule needs many focused edge-case checks.
65
+
66
+ ## 3. Test Naming And Grouping
67
+
68
+ Test names should reveal both the situation and the expected outcome.
69
+
70
+ Basic rules:
71
+
72
+ - Use `test_<situation>_<expected_outcome>` for test function names.
73
+ - Every test should have a docstring.
74
+ - For complex tests, use the docstring to describe the requirement or intent more specifically.
75
+ - Prefer capability- and outcome-oriented names over implementation-shaped names.
76
+ - Describe what the test actually exercises and observes. Do not claim to verify behavior that has been replaced by a test double.
77
+ - Use `pytest.mark.parametrize` with readable `ids` for cases of the same behavior that share an execution flow. Split cases when combining them introduces branches that obscure their different scenarios.
78
+
79
+ Example:
80
+
81
+ ```python
82
+ async def test_expired_subscription_removes_user_role(...) -> None:
83
+ """An expired subscription removes the user's role."""
84
+ ```
85
+
86
+ Avoid implementation-shaped names:
87
+
88
+ ```python
89
+ async def test_calls_delete_role(...) -> None:
90
+ ...
91
+ ```
92
+
93
+ ### 3.1 Class Grouping
94
+
95
+ Use classes when related tests are easier to read as one group. Standalone test functions are appropriate when grouping adds no clarity.
96
+
97
+ - Group tests by feature or public entrypoint.
98
+ - Do not use classes to hide important setup or build complex lifecycles.
99
+ - Each test inside the class should still be understandable on its own.
100
+
101
+ ## 4. AAA Pattern
102
+
103
+ Write test bodies in the `given / when / then` flow.
104
+
105
+ - `given`: Data, fixtures, mocks, and fixed time required for the scenario
106
+ - `when`: The behavior under test
107
+ - `then`: Observable results of `when`, such as the response, DB state, events, and external calls
108
+
109
+ Example:
110
+
111
+ ```python
112
+ async def test_expired_subscription_removes_user_role(...) -> None:
113
+ """An expired subscription removes the user's role."""
114
+ # given
115
+ subscription = create_subscription(status="active", expired=True)
116
+
117
+ # when
118
+ await expire_subscriptions()
119
+
120
+ # then
121
+ assert not user_has_role(subscription.user_id, "premium")
122
+ ```
123
+
124
+ Notes:
125
+
126
+ - If `when` contains several independent actions, it becomes unclear which action caused the failure.
127
+ - One test may verify multiple results, but they should all come from the same behavior.
128
+ - Use helpers when setup becomes long, but keep the important scenario conditions visible in the test body.
129
+
130
+ ## 5. Layer-Specific Guidelines
131
+
132
+ Use the largest layer that still gives a clear signal.
133
+
134
+ - API tests are a good default when behavior is observable through HTTP. Verify the response contract and important side effects.
135
+ - Service or domain tests are useful when the business rule is the main concern and an API test would be too heavy.
136
+ - Repository or DAO behavior is usually covered through a higher-level test. Test it directly only when the query contract itself is the main risk.
137
+ - Task and worker tests should focus on operational concerns such as duplicate handling, retries, partial failures, and idempotency.
138
+ - Avoid API loops for large setup. Insert data directly or use helpers/factories when setup is not the behavior under test.
139
+
140
+ ## 6. Fixture And Test Data
141
+
142
+ Fixtures are tools for readability. They should not hide the important conditions of a test.
143
+
144
+ Good fixtures:
145
+
146
+ - Hide repeated infrastructure setup.
147
+ - Provide common dependencies such as DB sessions, clients, caches, repositories, and services.
148
+ - Make their provided state clear from the name.
149
+
150
+ Fixtures to be careful with:
151
+
152
+ - Fixtures that automatically create too many rows and make test conditions hard to trace.
153
+ - Fixtures that hide the core condition of a scenario.
154
+ - Fixtures that accept many scenario-specific parameters and become a small DSL.
155
+ - Deep fixture dependency chains that make failures hard to diagnose.
156
+
157
+ Recommended approach:
158
+
159
+ - Promote helpers or fixtures to a shared place when multiple domains reuse them.
160
+ - Keep domain-specific helpers near that domain.
161
+ - Keep scenario-specific core conditions in the test body.
162
+ - Reuse established setup helpers when their behavior is outside the test's concern.
163
+ - Extract helpers to make scenarios easier to understand, not just to remove repeated lines. A little duplication is preferable to an abstraction that hides the important conditions or execution flow.
164
+ - Use helper and factory names that reveal intent. Examples: `create_expired_subscription`, `create_verified_user`
165
+ - Create only the minimum rows required by the test.
166
+
167
+ ### 6.1 DB Setup
168
+
169
+ - Isolate DB changes with transaction rollback whenever possible.
170
+ - Use `flush` when DB-generated values or query-visible rows are needed.
171
+ - Use `commit` only when verifying behavior that is visible only after commit, across separate transactions, or through task/worker entrypoints.
172
+ - Do not clean tables manually in tests. Before adding cleanup fixtures, confirm why transaction rollback cannot solve the problem.
173
+ - Prefer DB bulk inserts over API loops for large setup.
174
+
175
+ ### 6.2 Cache And External State
176
+
177
+ - Prefer shared fixtures for cache cleanup, dependency overrides, monkeypatches, and environment overrides.
178
+ - Do not repeat cleanup setup in individual tests when the repository already handles it globally.
179
+ - Verify cache keys, TTLs, and session values directly when they are part of the contract.
180
+
181
+ ## 7. Test Double Policy
182
+
183
+ Use test doubles to move the external world and unstable inputs to the test boundary.
184
+
185
+ Good targets for test doubles:
186
+
187
+ - Payment providers, Google Play, Apple, Firebase, S3, Discord, email, and Kafka producers
188
+ - Other servers or external HTTP clients
189
+ - Current time, randomness, and UUIDs
190
+ - Very slow or nondeterministic library calls
191
+
192
+ Avoid replacing when possible:
193
+
194
+ - Services, repositories, and DAOs in the same process
195
+ - Internal helpers called by the function under test
196
+ - Private properties or private methods
197
+ - The DB session itself
198
+ - The entire service behind an endpoint
199
+
200
+ Guidelines:
201
+
202
+ - Test doubles should make the test condition clearer.
203
+ - If replacing internal collaborators makes the test fragile during refactoring, switch to a larger-scope test.
204
+ - If a dependency is already mocked at an upper boundary, do not also mock the lower DAO unnecessarily.
205
+ - Use `spec` or real-object-based `patch.object` for `Mock` and `AsyncMock` whenever possible.
206
+ - Patch where the object is used, not where it is defined.
207
+
208
+ Partial replacement can be better than replacing the whole object. For example, instead of replacing an entire external client, create the client through the real factory and patch only the failing method. This keeps more framework wiring intact.
209
+
210
+ ## 8. Time, Randomness, And Determinism
211
+
212
+ Tests should produce the same result every time they run.
213
+
214
+ - Freeze or patch the current time.
215
+ - When timezone behavior matters, assert the base timezone, UTC conversion, and epoch values explicitly.
216
+ - Use fixed values when randomness, UUIDs, or current time appear in DB values or responses.
217
+ - For ordering-sensitive tests, make `created_at`, `id`, and cursor values explicit.
218
+ - External API response fixtures should contain only the fields needed, but enough fields to represent the edge case.
219
+
220
+ ## 9. Assertions
221
+
222
+ Good assertions make it clear what broke when the test fails.
223
+
224
+ Recommended:
225
+
226
+ - In API tests, assert `status_code` first.
227
+ - Use snapshots or exact dictionaries when the whole response is the contract.
228
+ - Assert only the important fields explicitly when only some fields are contractual.
229
+ - Verify DB side effects by reading them back through a repository or query.
230
+ - Assert downstream contracts directly, such as event payloads, unique keys, and published flags.
231
+ - For lists, assert length, order, and important identifiers together.
232
+
233
+ Avoid:
234
+
235
+ - Assertions that provide little signal, such as `assert response.json() is not None`
236
+ - Overly broad snapshots that fail on unrelated field changes
237
+ - Tests that only assert mock call counts without verifying actual state changes
238
+ - Recomputing the expected value with the same logic as the implementation under test
239
+
240
+ ## 10. Snapshot Usage
241
+
242
+ Snapshots are useful when protecting large JSON responses or complex payload contracts.
243
+
244
+ Use snapshots when:
245
+
246
+ - The entire response structure is part of the API contract.
247
+ - Volatile values are stabilized with matchers, fixed time, or fixed IDs.
248
+ - Snapshot updates are intentional and reviewed in the same PR as the code change.
249
+ - The diff is small enough for reviewers to understand.
250
+
251
+ Avoid snapshots when:
252
+
253
+ - Only a few fields matter.
254
+ - The collection ordering is not stable.
255
+ - Timestamps, UUIDs, or ordering change on every run.
256
+ - The payload is so large that failures are hard to interpret.
257
+
258
+ ## 11. Coverage
259
+
260
+ Coverage is a signal, not the quality goal itself.
261
+
262
+ - Whether changed code protects important branches and failure cases matters more than total coverage.
263
+ - If PR coverage is low, explain why the code does not need tests or which higher-level test covers it.
264
+ - Use missing lines in the coverage report to find candidate test targets.
265
+ - Do not add meaningless tests just to increase coverage.
266
+
267
+ ## 12. Review Checklist
268
+
269
+ Apply the [Review Checklist](common.md#review-checklist), and check the implementation and remaining risks:
270
+
271
+ - If the scope was narrowed, is the reason clear?
272
+ - Do the name and docstring accurately describe what the test exercises and observes?
273
+ - Is the `given / when / then` flow clear?
274
+ - Can DB, cache, and dependency overrides leak between tests?
275
+ - Are time, UUIDs, randomness, and ordering deterministic?
276
+ - Are necessary failure, permission, and authentication cases covered?
277
+ - Are backend-specific concerns such as pagination, cursors, idempotency, and transactions covered?
278
+ - If a snapshot is used, can reviewers understand the diff?
279
+ - If test doubles keep increasing, should the test move to a larger scope?
280
+ - If fixtures hide too many conditions, should those conditions move back into the test body?
@@ -0,0 +1,30 @@
1
+ # Common Testing Standards
2
+
3
+ Sections shared by [frontend.md](frontend.md) and [backend.md](backend.md).
4
+
5
+ ## Test Target Value
6
+
7
+ Do not try to test all code at the same density. Evaluate the protection each test adds to the existing suite against the cost of writing, reading, running, and maintaining it. The goal is reliable protection of important behavior, not the fewest tests or the highest coverage number.
8
+
9
+ - Before adding or retaining a test, identify the important regression it catches that existing tests do not adequately cover.
10
+ - When a new feature reuses shared behavior, check what existing tests already protect. Add tests for how the feature uses that behavior and for any differences in its contract, rather than copying all tests for the shared behavior.
11
+ - Before removing a test, confirm that important requirements and failure cases remain protected.
12
+
13
+ ## Focused Scenarios
14
+
15
+ Keep each scenario focused on its intended behavior. Combine multiple actions when their sequence is itself the requirement, not merely because they can be placed in one workflow. Do not extend an existing test with unrelated assertions for a new feature when that behavior belongs in another test.
16
+
17
+ ## Review Checklist
18
+
19
+ Start with the test's purpose and contribution to the suite:
20
+
21
+ 1. What important behavior does this test protect beyond the protection already provided by other tests?
22
+ 2. Does it contain only the setup, actions, and assertions needed for that behavior?
23
+ 3. Can a reader immediately understand the conditions, action, and expected outcome?
24
+
25
+ Then check:
26
+
27
+ - Would this test actually fail if that behavior broke?
28
+ - Does it preserve useful collaboration between real components while keeping its concern focused?
29
+ - Does the test verify externally observable behavior instead of internal implementation?
30
+ - Do test doubles replace external or unstable dependencies without replacing the behavior under test?
@@ -0,0 +1,301 @@
1
+ # Frontend Testing Guide
2
+
3
+ This guide defines the frontend testing standards our team uses when deciding what to test, how to structure tests, and which tradeoffs to make in this codebase.
4
+
5
+ ## 1. Testing Philosophy And Value
6
+
7
+ ### 1.1 Meaningful Failure
8
+
9
+ Tests are not written just to see `Pass`. They should fail when the behavior they protect is broken.
10
+
11
+ - A test that can keep passing after a meaningful regression does not improve safety. It only increases maintenance cost.
12
+ - When requirements change or a bug is introduced, the test must fail and provide a signal.
13
+ - Regression tests and snapshot tests are still valuable when they protect behavior that would fail after an unintended change.
14
+ - If a test does not meaningfully fail for any realistic change, reconsider whether it should exist at all. In many cases, deleting it is the better choice.
15
+
16
+ ### 1.2 Value-Driven Target Selection
17
+
18
+ Apply [Test Target Value](common.md#test-target-value), and:
19
+
20
+ - When an integration test already protects a behavior, do not automatically repeat it in unit tests for every participating component or composable. Add focused tests where they provide additional protection or clearer checks of important edge cases.
21
+
22
+ Prioritize behaviors that matter most to users and the business:
23
+
24
+ - Core business logic
25
+ Example: payment, authentication
26
+ - Complex data transformation logic
27
+ - Utility functions with frequent edge cases
28
+
29
+ ## 2. Testing Strategy: Testing Trophy
30
+
31
+ We follow Kent C. Dodds' `Testing Trophy` model, with the strongest emphasis on integration tests.
32
+
33
+ Prefer tests that keep the real collaboration needed to deliver a feature intact. A test can exercise components, composables, and state management together while protecting one clearly defined behavior. The number of participating components and the number of concerns being tested are separate choices.
34
+
35
+ Apply [Focused Scenarios](common.md#focused-scenarios).
36
+
37
+ 1. `Static (Lint/Type)`
38
+ Use TypeScript and ESLint to catch typos, type mismatches, and other basic mistakes. Do not duplicate test coverage for things that static analysis already verifies well.
39
+ 2. `Unit`
40
+ Verify highly complex calculations or isolated pure functions.
41
+ 3. `Integration`
42
+ This is our primary tactic. Verify whether multiple components work together to deliver a feature. Prefer sociable tests with minimal mocking.
43
+ 4. `E2E`
44
+ Verify user scenarios in a real browser environment.
45
+ Example: sign in -> search -> purchase
46
+ This is the safest level, but also the most expensive, so apply it only to critical paths.
47
+
48
+ ## 3. File Structure And Naming Conventions
49
+
50
+ - Place specs for simple modules or component-local behavior next to the source file.
51
+ - If a feature spans multiple components, place the spec under the feature's `tests/` directory or next to the representative entry component.
52
+ - Even when the filename follows a representative component name, the test scope should cover the user-observable behavior delivered with its child components and composables.
53
+
54
+ ```text
55
+ src/
56
+ ├── components/
57
+ │ ├── BaseButton.vue
58
+ │ ├── BaseButton.spec.ts
59
+ │ └── Cart/
60
+ │ ├── Cart.vue
61
+ │ ├── CartRow.vue
62
+ │ ├── useCart.ts
63
+ │ └── tests/
64
+ │ └── Cart.spec.ts
65
+ └── utils/
66
+ ├── price.ts
67
+ ├── date.ts
68
+ └── tests/
69
+ ├── price.spec.ts
70
+ └── date.spec.ts
71
+ ```
72
+
73
+ ### `describe` Block Structure
74
+
75
+ Use `describe` to show the shared subject of the tests, not the internal implementation structure. Keep it flat by default instead of nesting it.
76
+
77
+ ```tsx
78
+ // Components / classes
79
+ describe('Cart', () => {
80
+ it('수량을 증가시키면 총액이 재계산된다', () => {
81
+ // ...
82
+ });
83
+
84
+ it('수량은 1 미만으로 감소하지 않는다', () => {
85
+ // ...
86
+ });
87
+
88
+ it('삭제 버튼을 누르면 해당 상품이 목록에서 사라진다', () => {
89
+ // ...
90
+ });
91
+ });
92
+
93
+ // Public functions
94
+ describe('formatPrice', () => {
95
+ it('정수를 원화 문자열로 변환한다', () => {
96
+ // ...
97
+ });
98
+ });
99
+
100
+ describe('applyDiscount', () => {
101
+ it('할인율만큼 차감된 금액을 반환한다', () => {
102
+ // ...
103
+ });
104
+ });
105
+ ```
106
+
107
+ - Name each `describe` block after the test target. Example: component name, hook name, public function name
108
+ - Write each `it` or `test` block as a sentence that describes the expected outcome in that context.
109
+ - Do not nest child `describe` blocks. If you need another context, split it into another top-level `describe` block instead.
110
+ - Make test names specific enough to reveal both the situation and the expected result.
111
+ - Prefer capability-oriented names over implementation-shaped names.
112
+ Example: `user can checkout with a valid cart` is better than `calls submitOrder with transformed payload`.
113
+ - Describe what the test actually exercises and observes. Do not claim to verify behavior that has been replaced by a test double.
114
+ - Use parameterized tests with readable case names for cases of the same behavior that share an execution flow. Split cases when combining them introduces branches that obscure their different scenarios.
115
+
116
+ ## 4. Test Writing Principles
117
+
118
+ ### 4.1 Black-box Tests
119
+
120
+ Test externally observable behavior, not internal implementation details.
121
+
122
+ - Do not read private properties or component-internal state directly.
123
+ - Write tests from a black-box perspective. If input A is applied, does the user observe result B?
124
+ - If a refactor breaks the test without changing behavior, the test was too coupled to the implementation.
125
+
126
+ ### 4.2 Avoid Designing Only For Tests
127
+
128
+ Be careful not to damage production code readability or introduce unnecessary abstractions just to make tests easier to write.
129
+
130
+ - First check whether the behavior can be verified through an existing public entrypoint before changing production code for testability.
131
+ - Change the design only when the testing value clearly outweighs the design cost.
132
+ - In most cases, improving a function interface is better than making the design more complex for testability.
133
+
134
+ ## 5. Practical Guide
135
+
136
+ ### 5.1 AAA Pattern
137
+
138
+ For consistency, every test follows the `AAA (Given-When-Then)` pattern, and each section is marked with comments.
139
+
140
+ - `Given`: Initial state, dependencies, test doubles, and fixed inputs required for the scenario
141
+ - `When`: The behavior under test
142
+ - `Then`: Observable results of `When`, such as rendered UI, user-visible state, and outgoing requests or events
143
+
144
+ One test may verify multiple results, but they should all come from the same behavior.
145
+
146
+ ```tsx
147
+ it('상품 수량을 변경하면 장바구니 총액이 재계산된다', async () => {
148
+ // Given
149
+ const product = { id: 1, name: 'T-Shirt', price: 10000 };
150
+
151
+ render(Cart, {
152
+ props: {
153
+ initialItems: [{ ...product, quantity: 1 }],
154
+ },
155
+ });
156
+
157
+ // When
158
+ const incrementButton = screen.getByRole('button', { name: /increase quantity/i });
159
+ await userEvent.click(incrementButton);
160
+
161
+ // Then
162
+ expect(screen.getByText(/total: 20,000 KRW/i)).toBeInTheDocument();
163
+ });
164
+ ```
165
+
166
+ Add supporting comments when they improve readability.
167
+
168
+ ```tsx
169
+ it('쿠폰을 적용하면 최소 주문 금액 조건을 만족할 때만 할인 금액이 반영된다', async () => {
170
+ // Given: 장바구니 총액은 30,000원이고, 5,000원 할인 쿠폰은 최소 주문 금액 50,000원이 필요하다.
171
+ // 따라서 초기 상태에서는 쿠폰을 선택해도 할인은 적용되면 안 된다.
172
+
173
+ // When: 사용자가 수량을 늘려 총액을 50,000원 이상으로 만든 뒤 같은 쿠폰을 적용하면.
174
+
175
+ // Then: 할인 금액이 반영되고, 주문 요약 영역에 최종 결제 금액이 다시 계산되어 보여야 한다.
176
+ });
177
+ ```
178
+
179
+ ### 5.2 Mocking Policy
180
+
181
+ Limit mocking to the external world that we do not control. Compared with unit tests that heavily mock internal collaborators, integration tests with real collaboration boundaries are more resilient to refactoring and more trustworthy from the user's perspective.
182
+
183
+ - Mock API calls at the network boundary with `MSW (Mock Service Worker)` instead of mocking server logic directly.
184
+ - Prefer testing the integrated state where parent and child components actually collaborate.
185
+
186
+ #### 5.2.1 Shared MSW Testing Standard
187
+
188
+ For MSW-based tests, do not repeat the same setup in every spec. Build tests on top of a shared test environment, then override only the conditions required for the scenario with `server.use(...)`.
189
+
190
+ - Control behavior at the network boundary.
191
+ - Do not default to fine-grained `vi.mock()` calls against internal composables or API modules.
192
+ - Write overrides so that they reveal only the condition the test is trying to validate.
193
+ - Prefer helpers with obvious intent over large and noisy fixture payloads.
194
+
195
+ #### 5.2.2 Shared Assumptions For Component Tests
196
+
197
+ Reuse established setup helpers for dependencies outside the test's concern. For example, query client, store, injectables, and browser storage are usually better provided by the default test environment than recreated in each spec.
198
+
199
+ Keep scenario-specific conditions and the execution flow visible in the test body. Extract helpers to make scenarios easier to understand, not just to remove repeated lines. A little duplication is preferable to an abstraction that hides those details.
200
+
201
+ #### 5.2.3 Test Double Usage Standard
202
+
203
+ In black-box component tests, keep real rendering whenever possible.
204
+
205
+ - Do not increase test double usage just to make the test easier to write.
206
+ - Consider stubs, spies, or mocks only when unrelated side effects make the test unstable or too noisy.
207
+ - If a test double is necessary, its reason should be easy to explain.
208
+
209
+ ### 5.3 Async UI Waiting Principles
210
+
211
+ In component tests, the synchronization point should not be "has the request finished?" It should be "has the DOM reached the state the user is supposed to see?"
212
+
213
+ - Wait for appearance with `findBy*`.
214
+ - Wait for disappearance with `waitForElementToBeRemoved`.
215
+ - Use `waitFor(() => expect(...))` only when `findBy*` or dedicated removal APIs cannot express the expectation cleanly.
216
+ - If `waitFor` is required, keep only one assertion inside the callback.
217
+
218
+ `findBy*`, `waitFor`, and `waitForElementToBeRemoved` do not wait forever. They fail if the condition is not satisfied within the default timeout. Increase the timeout only when the specific test needs it.
219
+
220
+ ### 5.4 Handling Non-Deterministic Inputs
221
+
222
+ Move unstable values such as current time or randomness to the test boundary.
223
+
224
+ ```ts
225
+ // Before
226
+ const getDeadline = () => {
227
+ const now = new Date();
228
+ return addDays(now, 7);
229
+ };
230
+
231
+ // After
232
+ const getDeadline = (baseDate = new Date()) => {
233
+ return addDays(baseDate, 7);
234
+ };
235
+
236
+ it('마감일은 기준일로부터 7일 후다', () => {
237
+ const fixedDate = new Date('2024-01-01');
238
+ expect(getDeadline(fixedDate)).toEqual(new Date('2024-01-08'));
239
+ });
240
+ ```
241
+
242
+ ## 6. UI Verification And Accessibility
243
+
244
+ We avoid pixel-perfect visual regression tests by default. Screen structure and styles change often, and their maintenance cost is high. Instead, prioritize stable properties that users actually perceive and interact with.
245
+
246
+ ### What To Validate
247
+
248
+ Write tests against attributes that are relatively stable from a user perspective, rather than against volatile implementation details.
249
+
250
+ ### Query Priority
251
+
252
+ | Priority | Query | When To Use |
253
+ | --- | --- | --- |
254
+ | 1 | `getByRole` | Most semantic UI elements such as buttons, inputs, links, headings, and dialogs. Use with `name` whenever possible |
255
+ | 2 | `getByLabelText` | Form fields connected to labels |
256
+ | 3 | `getByDisplayValue` | When the current selected value is the best identifier |
257
+ | 4 | `getByText` | When visible text is the most meaningful query |
258
+ | 5 | `getByTestId` | Only when the other options are not practical |
259
+
260
+ ### `data-testid` Usage Standard
261
+
262
+ Use `data-testid` only for elements that cannot be identified well through accessibility roles or similar user-facing semantics. Do not add `aria-label` only to make tests easier to query.
263
+
264
+ ```vue
265
+ <button data-testid="submit-button">Submit</button>
266
+ ```
267
+
268
+ ```ts
269
+ screen.getByTestId('submit-button'); // BAD
270
+ screen.getByRole('button', { name: /submit/i }); // GOOD
271
+ ```
272
+
273
+ ```vue
274
+ <div data-testid="product-card-skeleton">...</div>
275
+ ```
276
+
277
+ ```ts
278
+ screen.getByTestId('product-card-skeleton');
279
+ ```
280
+
281
+ For repeated UI, narrow by user-visible context with `within(...)` before falling back to `data-testid`.
282
+
283
+ ```ts
284
+ const row = screen.getByRole('row', { name: /Samsung Electronics/i });
285
+ expect(within(row).getByText('72,000')).toBeInTheDocument();
286
+ ```
287
+
288
+ ### Prefer To Avoid
289
+
290
+ - CSS class names
291
+ - DOM structure itself
292
+ - Positional selectors such as `nth-child`
293
+ - Auto-generated or meaningless `id` values
294
+ - Text that is likely to change often
295
+ - Broad snapshot comparisons
296
+
297
+ ## 7. Review Checklist
298
+
299
+ Apply the [Review Checklist](common.md#review-checklist), and check:
300
+
301
+ - Do the name and description accurately reflect what the test exercises and observes?
@@ -0,0 +1,21 @@
1
+ # Test review: <Target>
2
+
3
+ **Verdict:** <tests protect the change | gaps need attention | rework needed> — <One-sentence reason>
4
+
5
+ ## Missing protection
6
+ - **P1 <Changed behavior>** — <Why no existing or new test would fail if it broke>; <smallest test that would cover it; omit this section when none>
7
+
8
+ ## Findings
9
+ - **`<test file>`**
10
+ - **<P1 | P2 | P3> <Title>** (`<path:line>`) — <Issue and the failure scenario it allows>; <smallest fix; omit this section when none>
11
+
12
+ ---
13
+
14
+ ## Details
15
+ Supporting context for reviewers; omit any subsection with nothing material to add.
16
+
17
+ ### Reviewed without findings
18
+ - `<test file>` — <Behavior it protects>
19
+
20
+ ### Scope
21
+ - <Guides applied, and any rule judged inapplicable with its reason>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solaqua/ghosts",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Reusable agent skills and reviewers.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1,22 +0,0 @@
1
- ---
2
- name: socratic-interview
3
- description: Conduct a Socratic interview to resolve only material uncertainty, then provide a concise execution brief.
4
- disable-model-invocation: true
5
- ---
6
-
7
- Conduct a Socratic interview only when missing information could materially change the outcome, scope, behavior, safety, cost, or external action. Inspect context first and make normal in-scope assumptions when sufficient.
8
-
9
- Ask the smallest useful question, one at a time. Do not collect background that will not change the work. Stop when remaining uncertainty is immaterial; state assumptions and proceed unless doing so could cause an unwanted or costly result.
10
-
11
- Do not plan, implement, create tasks, write specs, or edit files during the interview.
12
-
13
- When sufficient, summarize only the decisions needed to execute:
14
-
15
- ```md
16
- # Execution Brief
17
-
18
- - Outcome:
19
- - Decisions:
20
- - Scope and constraints:
21
- - Assumptions:
22
- ```