@yuaone/core 0.8.5 → 0.9.1
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/README.md +73 -2
- package/dist/agent-loop.d.ts +8 -0
- package/dist/agent-loop.d.ts.map +1 -1
- package/dist/agent-loop.js +34 -0
- package/dist/agent-loop.js.map +1 -1
- package/dist/dag-orchestrator.d.ts +3 -0
- package/dist/dag-orchestrator.d.ts.map +1 -1
- package/dist/dag-orchestrator.js +1 -0
- package/dist/dag-orchestrator.js.map +1 -1
- package/dist/execution-engine.d.ts.map +1 -1
- package/dist/execution-engine.js +1 -0
- package/dist/execution-engine.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/language-detector.d.ts.map +1 -1
- package/dist/language-detector.js +43 -122
- package/dist/language-detector.js.map +1 -1
- package/dist/language-registry.d.ts +45 -0
- package/dist/language-registry.d.ts.map +1 -0
- package/dist/language-registry.js +893 -0
- package/dist/language-registry.js.map +1 -0
- package/dist/llm-client.d.ts +7 -0
- package/dist/llm-client.d.ts.map +1 -1
- package/dist/llm-client.js +58 -8
- package/dist/llm-client.js.map +1 -1
- package/dist/skill-loader.d.ts +9 -16
- package/dist/skill-loader.d.ts.map +1 -1
- package/dist/skill-loader.js +116 -52
- package/dist/skill-loader.js.map +1 -1
- package/dist/skill-mode-bridge.d.ts +17 -0
- package/dist/skill-mode-bridge.d.ts.map +1 -0
- package/dist/skill-mode-bridge.js +27 -0
- package/dist/skill-mode-bridge.js.map +1 -0
- package/dist/skills/code-review.md +58 -0
- package/dist/skills/debug.md +45 -0
- package/dist/skills/languages/bash.md +74 -0
- package/dist/skills/languages/c.md +76 -0
- package/dist/skills/languages/cpp.md +75 -0
- package/dist/skills/languages/csharp.md +77 -0
- package/dist/skills/languages/cuda.md +80 -0
- package/dist/skills/languages/dart.md +75 -0
- package/dist/skills/languages/docker.md +80 -0
- package/dist/skills/languages/elixir.md +80 -0
- package/dist/skills/languages/gdscript.md +80 -0
- package/dist/skills/languages/go.md +77 -0
- package/dist/skills/languages/haskell.md +80 -0
- package/dist/skills/languages/java.md +77 -0
- package/dist/skills/languages/javascript.md +73 -0
- package/dist/skills/languages/kotlin.md +75 -0
- package/dist/skills/languages/lua.md +79 -0
- package/dist/skills/languages/php.md +73 -0
- package/dist/skills/languages/python.md +89 -0
- package/dist/skills/languages/r.md +80 -0
- package/dist/skills/languages/react.md +86 -0
- package/dist/skills/languages/ruby.md +78 -0
- package/dist/skills/languages/rust.md +77 -0
- package/dist/skills/languages/solidity.md +81 -0
- package/dist/skills/languages/sql.md +74 -0
- package/dist/skills/languages/svelte.md +74 -0
- package/dist/skills/languages/swift.md +74 -0
- package/dist/skills/languages/terraform.md +80 -0
- package/dist/skills/languages/typescript.md +110 -0
- package/dist/skills/languages/verilog.md +80 -0
- package/dist/skills/languages/vue.md +73 -0
- package/dist/skills/plan.md +49 -0
- package/dist/skills/refactor.md +46 -0
- package/dist/skills/security-scan.md +59 -0
- package/dist/skills/test-driven.md +51 -0
- package/dist/strategy-selector.d.ts +11 -0
- package/dist/strategy-selector.d.ts.map +1 -0
- package/dist/strategy-selector.js +85 -0
- package/dist/strategy-selector.js.map +1 -0
- package/dist/sub-agent.d.ts +3 -0
- package/dist/sub-agent.d.ts.map +1 -1
- package/dist/sub-agent.js +10 -0
- package/dist/sub-agent.js.map +1 -1
- package/dist/system-prompt.d.ts +2 -0
- package/dist/system-prompt.d.ts.map +1 -1
- package/dist/system-prompt.js +469 -94
- package/dist/system-prompt.js.map +1 -1
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
## Identity
|
|
2
|
+
- domain: cpp
|
|
3
|
+
- type: language
|
|
4
|
+
- confidence: 0.93
|
|
5
|
+
|
|
6
|
+
# C++ — Error Pattern Reference
|
|
7
|
+
|
|
8
|
+
Compile with `-Wall -Wextra -Werror -std=c++17` (or `c++20`). Use sanitizers (`-fsanitize=address,undefined`) and run under Valgrind. Prefer RAII and smart pointers to manual memory management.
|
|
9
|
+
|
|
10
|
+
## Compiler Warning Quick Reference
|
|
11
|
+
- **-Wdelete-non-virtual-dtor** — Deleting through base pointer without virtual destructor.
|
|
12
|
+
- **-Wunused-variable / -Wunused-parameter** — Declared but never used.
|
|
13
|
+
- **-Wreorder** — Member initializer order does not match declaration order.
|
|
14
|
+
- **-Wshadow** — Local variable shadows outer scope variable.
|
|
15
|
+
- **-Wreturn-type** — Non-void function missing return.
|
|
16
|
+
- **-Wnull-dereference** — Pointer that may be null is dereferenced.
|
|
17
|
+
|
|
18
|
+
## Known Error Patterns
|
|
19
|
+
|
|
20
|
+
### Use-After-Free — Dangling Pointer
|
|
21
|
+
- **Symptom**: ASan reports `heap-use-after-free`. Program crashes with random values or corrupt behavior.
|
|
22
|
+
- **Cause**: A raw pointer is used after `delete` (or `delete[]`) has been called on it. Common after ownership transfer without setting the old pointer to `nullptr`, or returning a reference to a local variable.
|
|
23
|
+
- **Strategy**: 1. Run with ASan: `g++ -fsanitize=address -g`. 2. Identify the delete and the subsequent access. 3. After deleting a raw pointer, set it to `nullptr`. 4. Prefer `std::unique_ptr` or `std::shared_ptr` — they prevent use-after-free by tying lifetime to scope. 5. Never return a reference or pointer to a local variable.
|
|
24
|
+
- **Tool sequence**: shell_exec (`g++ -fsanitize=address -g && ./program`) → file_read (reported lines) → file_edit (replace raw pointer with smart pointer or add nullptr assignment)
|
|
25
|
+
- **Pitfall**: Do NOT check `if (ptr != nullptr)` after use-after-free — once freed, the pointer is invalid even if not null.
|
|
26
|
+
|
|
27
|
+
### Double Free
|
|
28
|
+
- **Symptom**: ASan reports `double-free`. `glibc` detected "double free or corruption". Crash in heap management code.
|
|
29
|
+
- **Cause**: `delete` or `free` called twice on the same pointer. Happens when multiple owners hold raw pointers to the same allocation, or a copy constructor/assignment is missing (Rule of Three/Five).
|
|
30
|
+
- **Strategy**: 1. Grep for `delete` and `free` on the affected pointer. 2. Ensure only one owner calls delete. 3. Use `std::unique_ptr` to enforce single ownership. 4. If a class manages memory, implement the Rule of Five (destructor, copy ctor, copy assign, move ctor, move assign) or use `= delete` for copy.
|
|
31
|
+
- **Tool sequence**: grep (`delete\|free`) → file_read (class definition) → file_edit (add Rule of Five or use smart pointer)
|
|
32
|
+
- **Pitfall**: Setting pointer to `nullptr` after delete prevents double-free on the same pointer variable, but does NOT help if the pointer was copied first.
|
|
33
|
+
|
|
34
|
+
### ODR Violation — Multiple Definitions
|
|
35
|
+
- **Symptom**: Linker error: `multiple definition of 'foo'`; or subtle wrong-behavior bugs with no error (when violations occur across translation units with different definitions).
|
|
36
|
+
- **Cause**: A function or non-inline variable defined (not just declared) in a header file that is included in multiple translation units. Or the same name defined twice in different `.cpp` files.
|
|
37
|
+
- **Strategy**: 1. For functions defined in headers, mark them `inline` or move the definition to a `.cpp` file. 2. For variables in headers, use `inline` variable (C++17): `inline int g_count = 0;` or declare `extern` in header and define in one `.cpp`. 3. For templates, definitions in headers are allowed and do not violate ODR as long as they are identical.
|
|
38
|
+
- **Tool sequence**: grep (function/variable name across headers) → file_read → file_edit (add `inline` or move to .cpp)
|
|
39
|
+
- **Pitfall**: Do NOT add `static` to a header function to suppress the linker error — this gives each TU its own copy, which is a different bug.
|
|
40
|
+
|
|
41
|
+
### std::vector Iterator Invalidation
|
|
42
|
+
- **Symptom**: Crash or undefined behavior after modifying a `std::vector` while iterating it. Sanitizers may report heap-use-after-free.
|
|
43
|
+
- **Cause**: `push_back`, `insert`, `erase`, or `resize` may reallocate the vector's internal buffer, invalidating all iterators, references, and pointers into the vector. Continuing to use old iterators causes UB.
|
|
44
|
+
- **Strategy**: 1. Identify any modification to the vector inside a loop that iterates it. 2. For `erase`: use the erase-remove idiom or capture the returned iterator: `it = vec.erase(it)`. 3. For `push_back` during iteration: collect new elements in a separate vector and append after the loop. 4. If indices are used instead of iterators, re-read the index from the start after mutation.
|
|
45
|
+
- **Tool sequence**: grep (`push_back\|erase\|insert`) → file_read (enclosing loop) → file_edit (fix iteration pattern)
|
|
46
|
+
- **Pitfall**: Do NOT cache `vec.end()` before a loop that modifies the vector — recompute it or use index-based iteration.
|
|
47
|
+
|
|
48
|
+
### Template Instantiation Error
|
|
49
|
+
- **Symptom**: Long compiler error message starting with "note: required from here" with a chain of template instantiations. The actual error is at the bottom of the chain.
|
|
50
|
+
- **Cause**: A template is instantiated with a type that does not satisfy the template's requirements (missing method, wrong type, etc.). Concept violations (C++20) or SFINAE failures.
|
|
51
|
+
- **Strategy**: 1. Read the error from the BOTTOM UP — the root cause is at the last "error:" line. 2. Identify the type being substituted. 3. Check what operations the template body performs on `T` and confirm the substituted type supports them. 4. In C++20, use concepts to get clearer error messages.
|
|
52
|
+
- **Tool sequence**: file_read (full error output, bottom first) → grep (template definition) → file_read → file_edit (constrain template or fix calling type)
|
|
53
|
+
- **Pitfall**: Do NOT add template specializations to paper over a type mismatch. Fix the type or add proper constraints.
|
|
54
|
+
|
|
55
|
+
### Missing Virtual Destructor
|
|
56
|
+
- **Symptom**: Valgrind reports memory leaks for derived class members. Objects deleted through base pointer only partially destroy the object.
|
|
57
|
+
- **Cause**: A base class with virtual methods lacks a virtual destructor. Deleting a derived object through a base pointer calls only the base destructor, skipping derived class cleanup.
|
|
58
|
+
- **Strategy**: 1. Grep all base classes (classes with virtual methods). 2. Check if they have `virtual ~ClassName() = default;` or a defined virtual destructor. 3. Add it if missing.
|
|
59
|
+
- **Tool sequence**: grep (`virtual`) → file_read (class definitions) → file_edit (add `virtual ~ClassName() = default;`)
|
|
60
|
+
- **Pitfall**: Do NOT make a class non-polymorphic just to avoid the destructor — if it has any virtual methods, it needs a virtual destructor.
|
|
61
|
+
|
|
62
|
+
## Verification
|
|
63
|
+
Compile: `g++ -std=c++17 -Wall -Wextra -Werror -g -fsanitize=address,undefined -o program *.cpp`
|
|
64
|
+
- Run Valgrind in CI: `valgrind --error-exitcode=1 --leak-check=full ./program`
|
|
65
|
+
- For large projects: integrate with `clang-tidy` and `cppcheck`.
|
|
66
|
+
|
|
67
|
+
## Validation Checklist
|
|
68
|
+
- [ ] All base classes with virtual methods have a virtual destructor
|
|
69
|
+
- [ ] No raw `new`/`delete` — prefer `std::make_unique`/`std::make_shared`
|
|
70
|
+
- [ ] No iterator or pointer used after vector/container modification
|
|
71
|
+
- [ ] No function or variable defined (not declared) in a header without `inline`
|
|
72
|
+
- [ ] Rule of Five implemented for any class that manages a resource
|
|
73
|
+
- [ ] Template errors read from bottom up before attempting a fix
|
|
74
|
+
- [ ] `-fsanitize=address,undefined` run during development
|
|
75
|
+
- [ ] No dangling references returned from functions (local variable reference)
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
## Identity
|
|
2
|
+
- domain: csharp
|
|
3
|
+
- type: language
|
|
4
|
+
- confidence: 0.95
|
|
5
|
+
|
|
6
|
+
# C# — Error Pattern Reference
|
|
7
|
+
|
|
8
|
+
Read the full compiler error including the CS error code and the stack trace. C#'s compiler messages are precise and the `CS` error code narrows the problem category immediately.
|
|
9
|
+
|
|
10
|
+
## Error Code Quick Reference
|
|
11
|
+
- **CS0103** — The name 'x' does not exist in the current context.
|
|
12
|
+
- **CS0019** — Operator cannot be applied to operands of type.
|
|
13
|
+
- **CS1998** — Async method lacks 'await' operators (sync disguised as async).
|
|
14
|
+
- **CS4014** — Call not awaited — fire and forget.
|
|
15
|
+
- **CS0246** — Type or namespace not found (missing using directive or assembly).
|
|
16
|
+
- **CS0266** — Cannot implicitly convert — explicit cast required.
|
|
17
|
+
- **CS8600** — Converting null literal or possible null value to non-nullable type.
|
|
18
|
+
- **CS8602** — Dereference of a possibly null reference.
|
|
19
|
+
- **CS8618** — Non-nullable field must contain a non-null value when exiting constructor.
|
|
20
|
+
- **NullReferenceException** — Runtime null dereference.
|
|
21
|
+
|
|
22
|
+
## Known Error Patterns
|
|
23
|
+
|
|
24
|
+
### Pattern: NullReferenceException
|
|
25
|
+
|
|
26
|
+
- **symptom**: `System.NullReferenceException: Object reference not set to an instance of an object` at a specific stack frame
|
|
27
|
+
- **cause**: A reference-type variable is `null` when a member access (method, property, indexer) is attempted. Common sources: uninitialized properties, LINQ `FirstOrDefault()` returning null, missing dependency injection registration, uninitialized optional fields.
|
|
28
|
+
- **strategy**: 1. Read the stack trace line. 2. Identify which object is null — use the NullReferenceException helper in .NET 6+ which names the null variable. 3. Trace backwards to where the variable was assigned. 4. Add a null check using the null-conditional operator `x?.Method()` or null coalescing `x ?? defaultValue`. 5. For method parameters, use `ArgumentNullException.ThrowIfNull(param)` at method entry. 6. Enable nullable reference types (`<Nullable>enable</Nullable>`) in the project to get CS8602 compile-time warnings.
|
|
29
|
+
- **toolSequence**: file_read (stack trace line) → grep (variable assignment) → file_edit (add null check or enable nullable)
|
|
30
|
+
- **pitfall**: Do NOT add `!` (null-forgiving operator) to suppress CS8602 warnings without proof — it silences the warning but keeps the runtime crash.
|
|
31
|
+
|
|
32
|
+
### Pattern: async void (fire-and-forget)
|
|
33
|
+
|
|
34
|
+
- **symptom**: Exceptions thrown inside `async void` methods silently crash the application or are swallowed. `CS1998` warning on async methods without await. Errors not catchable at the call site.
|
|
35
|
+
- **cause**: `async void` methods cannot be awaited, so exceptions propagate to the synchronization context and crash the app (in most contexts) or are silently lost. They are intended only for event handlers.
|
|
36
|
+
- **strategy**: 1. Grep for `async void` in the codebase. 2. For all non-event-handler `async void` methods, change the return type to `async Task`. 3. Update call sites to `await` the returned `Task`. 4. For event handlers that must be `async void`, wrap the body in try-catch: `try { await DoWorkAsync(); } catch (Exception ex) { _logger.LogError(ex, ...); }`. 5. Use `Task.Run` for fire-and-forget scenarios and log exceptions: `_ = Task.Run(async () => { try { await work(); } catch (ex) { log(ex); } });`.
|
|
37
|
+
- **toolSequence**: grep (`async void`) → file_read (each occurrence) → file_edit (change to `async Task` or add try-catch wrapper)
|
|
38
|
+
- **pitfall**: Do NOT change event handlers (`Button_Click`, `OnReceived`) from `async void` to `async Task` — the event system requires `void` return. Wrap the body instead.
|
|
39
|
+
|
|
40
|
+
### Pattern: LINQ deferred execution surprise
|
|
41
|
+
|
|
42
|
+
- **symptom**: A LINQ query appears to produce different results on multiple enumerations, or is re-executed (including side effects) every time it is iterated. Database queries run more times than expected.
|
|
43
|
+
- **cause**: LINQ queries (`Where`, `Select`, `OrderBy`, etc.) return `IEnumerable<T>` which is evaluated lazily — the query runs again each time the sequence is enumerated. Storing an `IQueryable` or `IEnumerable` and iterating it multiple times re-executes the underlying operation.
|
|
44
|
+
- **strategy**: 1. Find LINQ queries that are iterated more than once (look for multiple `foreach`, `Count()`, `ToList()` calls on the same variable). 2. Materialize the query exactly once with `.ToList()` or `.ToArray()` when the results will be used multiple times. 3. For EF Core queries, always call `.ToList()` or `.ToListAsync()` before the DbContext scope ends. 4. Be explicit: if single enumeration is intentional, document it; if multiple, materialize.
|
|
45
|
+
- **toolSequence**: grep (variable name used in multiple enumerations) → file_read → file_edit (add `.ToList()` after first enumeration)
|
|
46
|
+
- **pitfall**: Do NOT call `.ToList()` on every LINQ query blindly — for large datasets piped into further LINQ operators, deferred execution is more memory-efficient. Materialize only when the result is reused.
|
|
47
|
+
|
|
48
|
+
### Pattern: IDisposable not disposed (missing using statement)
|
|
49
|
+
|
|
50
|
+
- **symptom**: Memory/handle leak; `ObjectDisposedException` thrown after the object is used later; resource exhaustion (too many open file handles, database connections).
|
|
51
|
+
- **cause**: Objects implementing `IDisposable` (StreamReader, HttpClient, DbContext, SqlConnection, etc.) are not disposed when done. Their resources (file handles, network sockets, unmanaged memory) are not freed until the finalizer runs — which may be never or much later.
|
|
52
|
+
- **strategy**: 1. Grep for `new StreamReader`, `new SqlConnection`, `new HttpClient`, etc. — anything implementing `IDisposable`. 2. Wrap each in a `using` statement: `using var reader = new StreamReader(path);` (C# 8+) or `using (var reader = new StreamReader(path)) { ... }`. 3. For class-level `IDisposable` fields, implement `IDisposable` on the containing class and dispose fields in `Dispose()`. 4. For `HttpClient`, do NOT create one per request — use a singleton or `IHttpClientFactory` to avoid socket exhaustion.
|
|
53
|
+
- **toolSequence**: grep (`new .*Disposable\|new Stream\|new SqlConnection\|new DbContext`) → file_read → file_edit (wrap in `using` statement)
|
|
54
|
+
- **pitfall**: Do NOT create a new `HttpClient` inside a `using` block per request — `HttpClient` is designed to be reused; disposing it per request causes socket exhaustion.
|
|
55
|
+
|
|
56
|
+
### Pattern: ambiguous method overload
|
|
57
|
+
|
|
58
|
+
- **symptom**: `CS0121: The call is ambiguous between the following methods or properties: 'Foo(int)' and 'Foo(long)'`
|
|
59
|
+
- **cause**: Two or more overloads are equally applicable for the given arguments. Common causes: numeric literal promotion (int can match both int and long), implicit conversions, optional parameters making two overloads identical in call site.
|
|
60
|
+
- **strategy**: 1. Read the two (or more) ambiguous overloads listed in the error. 2. Make the call explicit by casting the argument to the desired type: `Foo((int)value)` or `Foo((long)value)`. 3. If you own the overloaded methods and the ambiguity is a design problem, rename or consolidate the overloads. 4. For extension method ambiguity, use a static method call syntax to specify the type explicitly: `Extensions.Foo(obj, arg)`.
|
|
61
|
+
- **toolSequence**: file_read (error line) → grep (overload definitions) → file_edit (add explicit cast at call site)
|
|
62
|
+
- **pitfall**: Do NOT remove one of the overloads without checking all call sites — removing an overload may break other callers that relied on it.
|
|
63
|
+
|
|
64
|
+
## Verification
|
|
65
|
+
Run: `dotnet build` or `msbuild /p:Configuration=Debug`
|
|
66
|
+
- Exit 0 = no compile errors.
|
|
67
|
+
- For tests: `dotnet test`
|
|
68
|
+
- For nullable analysis: ensure `<Nullable>enable</Nullable>` in .csproj
|
|
69
|
+
|
|
70
|
+
## Validation Checklist
|
|
71
|
+
- [ ] `dotnet build` exits 0 with no errors
|
|
72
|
+
- [ ] No `async void` except event handlers (all others return `Task`)
|
|
73
|
+
- [ ] All `IDisposable` objects wrapped in `using` statements
|
|
74
|
+
- [ ] LINQ queries materialized with `.ToList()` when iterated multiple times
|
|
75
|
+
- [ ] Nullable reference types enabled; no `!` null-forgiving without proof
|
|
76
|
+
- [ ] Method overload ambiguities resolved with explicit casts
|
|
77
|
+
- [ ] `HttpClient` reused via DI / `IHttpClientFactory`, not created per request
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
## Identity
|
|
2
|
+
- domain: cuda
|
|
3
|
+
- type: language
|
|
4
|
+
- confidence: 0.90
|
|
5
|
+
|
|
6
|
+
# CUDA — Error Pattern Reference
|
|
7
|
+
|
|
8
|
+
Read the exact CUDA error code and the line from `cudaGetLastError()` or the assertion. CUDA errors are asynchronous — the error may surface on a different CUDA call than the one that caused it.
|
|
9
|
+
|
|
10
|
+
## Error Code Quick Reference
|
|
11
|
+
- **cudaErrorIllegalAddress (700)** — Out-of-bounds memory access inside a kernel.
|
|
12
|
+
- **cudaErrorLaunchFailure (4)** — Kernel launch configuration invalid or device exception.
|
|
13
|
+
- **cudaErrorInvalidValue (1)** — Null pointer or invalid argument passed to CUDA API.
|
|
14
|
+
- **cudaErrorMemoryAllocation (2)** — cudaMalloc failed; GPU out of memory.
|
|
15
|
+
- **cudaErrorNoKernelImageForDevice (209)** — Kernel compiled for different GPU architecture.
|
|
16
|
+
- **cudaErrorSynchronizationError** — cudaDeviceSynchronize() found a pending kernel error.
|
|
17
|
+
- **cudaErrorInvalidDeviceFunction** — Kernel function pointer is invalid or not compiled for device.
|
|
18
|
+
- **CUDA_ERROR_NOT_INITIALIZED** — cuInit() not called before cu* API calls (Driver API).
|
|
19
|
+
|
|
20
|
+
## Known Error Patterns
|
|
21
|
+
|
|
22
|
+
### CUDA_ERROR_ILLEGAL_ADDRESS — Out-of-Bounds Memory Access
|
|
23
|
+
- **Symptom**: `cudaErrorIllegalAddress` on `cudaDeviceSynchronize()` or `cudaMemcpy`; Nsight Compute shows "Memory access fault". The error appears after the actual faulty kernel launch.
|
|
24
|
+
- **Cause**: A thread accesses device memory outside allocated bounds. Common causes: wrong index calculation (`blockIdx.x * blockDim.x + threadIdx.x` exceeds array size), pointer arithmetic error, or accessing freed device memory.
|
|
25
|
+
- **Strategy**: 1. Add bounds checks inside the kernel: `if (idx >= N) return;` at the kernel entry. 2. Use CUDA Compute Sanitizer: `compute-sanitizer --tool memcheck ./my_program` — it pinpoints the exact line and thread. 3. Verify index calculation: `size_t idx = blockIdx.x * blockDim.x + threadIdx.x;` — ensure total threads >= N but kernel guards against idx >= N. 4. Check that `cudaMalloc` allocated sufficient bytes: `N * sizeof(float)` not just `N`. 5. After launch, always call `cudaDeviceSynchronize()` and check its return value during debugging.
|
|
26
|
+
- **Tool sequence**: file_read (kernel code) → file_edit (add bounds check `if (idx >= N) return`) → shell_exec (`compute-sanitizer ./program`)
|
|
27
|
+
- **Pitfall**: Do NOT disable bounds checking in production without proving the launch configuration always generates valid indices — off-by-one in grid size causes silent data corruption.
|
|
28
|
+
|
|
29
|
+
### Race Condition — Missing __syncthreads()
|
|
30
|
+
- **Symptom**: Non-deterministic results; kernel produces correct output on small inputs but fails on large ones; different runs of the same program give different answers.
|
|
31
|
+
- **Cause**: Threads in the same block share shared memory but execute independently. Without `__syncthreads()`, some threads may read shared memory values written by other threads that have not completed their write yet.
|
|
32
|
+
- **Strategy**: 1. Identify every pattern where threads write to shared memory and other threads subsequently read from it. 2. Place `__syncthreads()` between the write phase and the read phase. 3. Ensure `__syncthreads()` is called by ALL threads in the block — placing it inside a conditional (`if (threadIdx.x < N) __syncthreads()`) causes a deadlock if some threads skip it. 4. For reduction operations, use warp-level primitives (`__shfl_down_sync`) for the last 32 threads instead of `__syncthreads()`.
|
|
33
|
+
- **Tool sequence**: grep (`__shared__`) → file_read (kernel) → file_edit (add __syncthreads() between write and read phases)
|
|
34
|
+
- **Pitfall**: Do NOT put `__syncthreads()` inside a branch that only some threads take — all threads in the block must reach the barrier or the program deadlocks.
|
|
35
|
+
|
|
36
|
+
### GPU Memory Leak — cudaMalloc Not Freed
|
|
37
|
+
- **Symptom**: GPU memory usage grows with each iteration; eventually `cudaMalloc` returns `cudaErrorMemoryAllocation`; `nvidia-smi` shows GPU memory at 100%.
|
|
38
|
+
- **Cause**: Device memory allocated with `cudaMalloc` is not freed with `cudaFree` when no longer needed. Unlike CPU memory, CUDA does not have automatic garbage collection — every `cudaMalloc` needs a matching `cudaFree`.
|
|
39
|
+
- **Strategy**: 1. Grep all `cudaMalloc` calls and verify each has a corresponding `cudaFree` in all exit paths (including error paths). 2. Use RAII wrappers: Thrust `thrust::device_vector` auto-frees on destruction, or write a simple CUDA RAII class. 3. Use Compute Sanitizer memcheck: `compute-sanitizer --leak-check full ./program` to detect unreleased memory. 4. In error handling, ensure `cudaFree` is called before returning even on error paths.
|
|
40
|
+
- **Tool sequence**: grep (`cudaMalloc`) → file_read → file_edit (add cudaFree in all exit paths or convert to RAII wrapper)
|
|
41
|
+
- **Pitfall**: Do NOT call `cudaFree(nullptr)` expecting it to be a no-op in all contexts — while CUDA spec says it is safe, some older driver versions behave differently. Explicitly track which pointers need freeing.
|
|
42
|
+
|
|
43
|
+
### Kernel Launch Failure — Block/Grid Size Mismatch
|
|
44
|
+
- **Symptom**: `cudaErrorLaunchFailure` or `cudaErrorInvalidValue` immediately after kernel invocation; sometimes silent failure with zero output.
|
|
45
|
+
- **Cause**: Invalid launch configuration: `blockDim.x` exceeds device maximum (usually 1024), `gridDim.x` exceeds `INT_MAX` for 1D grids, total registers per block exceeded, or shared memory requested exceeds device limit (typically 48KB or 96KB).
|
|
46
|
+
- **Strategy**: 1. Query device limits: `cudaDeviceGetAttribute(&maxThreadsPerBlock, cudaDevAttrMaxThreadsPerBlock, device)`. 2. Print the launch configuration before the `<<<>>>` call to verify dimensions. 3. For large N, calculate grid: `int gridSize = (N + blockSize - 1) / blockSize;` where `blockSize <= 1024`. 4. Check shared memory: `size_t sharedMem = blockSize * sizeof(float); if (sharedMem > 49152) { /* reduce blockSize */ }`. 5. Use `cudaOccupancyMaxPotentialBlockSize()` to auto-tune block size for maximum occupancy.
|
|
47
|
+
- **Tool sequence**: file_read (kernel launch) → file_edit (add device property query + bounds check on blockSize/gridSize)
|
|
48
|
+
- **Pitfall**: Do NOT hardcode `blockSize = 1024` — older or embedded GPU devices may have lower limits. Always query and validate against device properties.
|
|
49
|
+
|
|
50
|
+
### Device/Host Memory Confusion — Dereferencing Device Pointer on CPU
|
|
51
|
+
- **Symptom**: Segfault or access violation in CPU code; `cudaMemcpy` direction is wrong; kernel receives a host pointer and crashes with illegal address.
|
|
52
|
+
- **Cause**: Device pointers (from `cudaMalloc`) cannot be dereferenced on the host. Host pointers (from `malloc` or stack) cannot be passed directly to kernels as array pointers. Passing a host pointer where a device pointer is expected causes `CUDA_ERROR_ILLEGAL_ADDRESS`.
|
|
53
|
+
- **Strategy**: 1. Use a clear naming convention: `d_` prefix for device pointers (`d_input`, `d_output`), `h_` prefix for host pointers. 2. Before any kernel call, verify all array arguments are device pointers obtained from `cudaMalloc`. 3. For data transfer: host→device is `cudaMemcpy(d_ptr, h_ptr, size, cudaMemcpyHostToDevice)`; device→host is the reverse. 4. Use Unified Memory (`cudaMallocManaged`) during debugging to eliminate explicit transfers and isolate logic errors.
|
|
54
|
+
- **Tool sequence**: grep (`cudaMalloc`, `cudaMemcpy`) → file_read → file_edit (add d_/h_ naming convention + verify memcpy directions)
|
|
55
|
+
- **Pitfall**: Do NOT use `cudaMallocManaged` in performance-critical production code — unified memory adds overhead from page migration. Use it only for prototyping or debugging.
|
|
56
|
+
|
|
57
|
+
### Missing Error Check — Silent CUDA Failure
|
|
58
|
+
- **Symptom**: Program runs without crashing but produces incorrect results; errors go undetected because CUDA API calls are not checked.
|
|
59
|
+
- **Cause**: CUDA API functions return `cudaError_t` — if not checked, errors are silently ignored. Kernel launches return void — errors only surface on the next synchronization point.
|
|
60
|
+
- **Strategy**: 1. Wrap all CUDA calls with a macro: `#define CUDA_CHECK(call) { cudaError_t err = (call); if (err != cudaSuccess) { fprintf(stderr, "CUDA error %s at %s:%d\n", cudaGetErrorString(err), __FILE__, __LINE__); exit(1); } }`. 2. After every kernel launch, add `CUDA_CHECK(cudaGetLastError()); CUDA_CHECK(cudaDeviceSynchronize());` during development. 3. Remove synchronize calls in production (they serialize execution) but keep `cudaGetLastError()` checks.
|
|
61
|
+
- **Tool sequence**: grep (`cudaMalloc`, `cudaMemcpy`, kernel launches) → file_edit (wrap all calls with CUDA_CHECK macro)
|
|
62
|
+
- **Pitfall**: Do NOT only check `cudaDeviceSynchronize()` at the end of a large function — by then, you have lost information about which specific operation failed.
|
|
63
|
+
|
|
64
|
+
## Verification
|
|
65
|
+
Run: `compute-sanitizer --tool memcheck ./your_program`
|
|
66
|
+
- No memory errors, no out-of-bounds accesses = clean run.
|
|
67
|
+
- Profile with Nsight Systems: `nsys profile ./your_program` — check for unintended synchronizations and memory transfer bottlenecks.
|
|
68
|
+
- `nvidia-smi` should show GPU memory returning to baseline after program exit.
|
|
69
|
+
|
|
70
|
+
## Validation Checklist
|
|
71
|
+
- [ ] All kernels have bounds check: `if (idx >= N) return;`
|
|
72
|
+
- [ ] `__syncthreads()` placed between shared memory writes and reads
|
|
73
|
+
- [ ] `__syncthreads()` never inside a conditional branch
|
|
74
|
+
- [ ] Every `cudaMalloc` has a corresponding `cudaFree` in all code paths
|
|
75
|
+
- [ ] All CUDA API calls wrapped with error checking macro
|
|
76
|
+
- [ ] `cudaDeviceSynchronize()` + `cudaGetLastError()` called after kernels during debugging
|
|
77
|
+
- [ ] Device pointers use `d_` prefix, host pointers use `h_` prefix
|
|
78
|
+
- [ ] Launch configuration validated against `cudaDevAttrMaxThreadsPerBlock`
|
|
79
|
+
- [ ] `compute-sanitizer --tool memcheck` passes with no errors
|
|
80
|
+
- [ ] Shared memory usage per block verified to be within device limit
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
## Identity
|
|
2
|
+
- domain: dart
|
|
3
|
+
- type: language
|
|
4
|
+
- confidence: 0.95
|
|
5
|
+
|
|
6
|
+
# Dart / Flutter — Error Pattern Reference
|
|
7
|
+
|
|
8
|
+
Read the full Dart analyzer error and the Flutter stack trace. Flutter errors often include a widget ancestry chain — read it to find where the bad state originates.
|
|
9
|
+
|
|
10
|
+
## Error Code Quick Reference
|
|
11
|
+
- **Null check operator used on a null value** — `!` used on null (null safety violation at runtime).
|
|
12
|
+
- **setState() called after dispose()** — State method called on unmounted widget.
|
|
13
|
+
- **A build function returned null** — Widget build() returned null instead of a Widget.
|
|
14
|
+
- **'context' is not a valid BuildContext** — BuildContext used across async gap.
|
|
15
|
+
- **type 'X' is not a subtype of type 'Y'** — Type mismatch from platform channel or JSON.
|
|
16
|
+
- **Late initialization error: LateInitializationError** — `late` variable read before assignment.
|
|
17
|
+
- **RenderFlex overflowed** — Layout overflow (not a Dart error, but a Flutter render error).
|
|
18
|
+
- **MissingPluginException** — Platform channel plugin not registered.
|
|
19
|
+
|
|
20
|
+
## Known Error Patterns
|
|
21
|
+
|
|
22
|
+
### Pattern: Null safety migration (late keyword misuse)
|
|
23
|
+
|
|
24
|
+
- **symptom**: `LateInitializationError: Field 'x' has not been initialized.` at runtime, or `Null check operator used on a null value` — despite null safety being enabled
|
|
25
|
+
- **cause**: The `late` keyword promises to the compiler that a variable will be initialized before its first read — but there is no runtime enforcement until the read happens. If the variable is read in a code path that runs before initialization, it crashes.
|
|
26
|
+
- **strategy**: 1. Find the `late` variable declaration. 2. Trace all code paths to its first read — check `initState`, async callbacks, and conditional initialization. 3. If initialization depends on async work, use a nullable type (`String?`) and guard reads with `if (x != null)` or `?`. 4. For `late` fields initialized in `initState`, ensure `initState` runs before the first build that reads them. 5. Replace `late` with proper initialization in the constructor or with a nullable type if initialization is not guaranteed.
|
|
27
|
+
- **toolSequence**: grep (`late `) → file_read (variable and its first read) → file_edit (replace with nullable or ensure guaranteed initialization)
|
|
28
|
+
- **pitfall**: Do NOT use `late` for all non-nullable fields just to satisfy the analyzer — it moves the error from compile time to runtime.
|
|
29
|
+
|
|
30
|
+
### Pattern: setState after dispose
|
|
31
|
+
|
|
32
|
+
- **symptom**: `setState() called after dispose()` — Flutter prints this as a warning/error in debug mode; in release mode, it may silently corrupt state or crash
|
|
33
|
+
- **cause**: An async operation (HTTP call, Timer, animation) completes and calls `setState()` after the widget has been removed from the tree (disposed). The `State` object is still referenced by the callback closure.
|
|
34
|
+
- **strategy**: 1. Find async operations (HTTP calls, `Future.delayed`, `Timer`, stream subscriptions) inside `State` classes. 2. Add a mounted check before `setState`: `if (mounted) setState(() { ... });`. 3. Cancel timers and stream subscriptions in `dispose()`. 4. For `Future` callbacks, capture the result and check mounted: `final data = await fetchData(); if (!mounted) return; setState(() { _data = data; });`. 5. For streams, store the `StreamSubscription` and call `subscription.cancel()` in `dispose()`.
|
|
35
|
+
- **toolSequence**: grep (`setState`) → file_read (async context around each call) → file_edit (add `if (mounted)` guard) + file_edit (cancel subscriptions in dispose)
|
|
36
|
+
- **pitfall**: Do NOT omit the `dispose()` override — always cancel timers, animations, and stream subscriptions in `dispose()`.
|
|
37
|
+
|
|
38
|
+
### Pattern: widget rebuild loop (setState in build)
|
|
39
|
+
|
|
40
|
+
- **symptom**: Widget rebuilds continuously (high CPU, hot reload doesn't settle, "build called 60 times/sec" in profile). Often caused by calling `setState` inside `build` or in a listener that triggers rebuild which triggers the listener again.
|
|
41
|
+
- **cause**: Calling `setState()` during a `build()` call, or setting up a listener in `build()` that immediately fires and calls `setState()`, causing an infinite loop of rebuilds.
|
|
42
|
+
- **strategy**: 1. Check the `build()` method for any direct `setState()` calls — remove them. 2. Move listener setup to `initState()` so it runs once. 3. For `addListener` patterns, ensure the listener does not call `setState` synchronously in the first frame. 4. Use `WidgetsBinding.instance.addPostFrameCallback(() { setState(() {}); })` when state update must happen after a build. 5. For `AnimationController`, initialize in `initState`, not in `build`.
|
|
43
|
+
- **toolSequence**: file_read (build method) → grep (`setState` inside build) → file_edit (move initialization to initState)
|
|
44
|
+
- **pitfall**: Do NOT use `setState` inside `build` — use `initState`, `didUpdateWidget`, or `addPostFrameCallback` for post-build state updates.
|
|
45
|
+
|
|
46
|
+
### Pattern: async gap (BuildContext across async)
|
|
47
|
+
|
|
48
|
+
- **symptom**: `Don't use 'BuildContext's across async gaps` — lint warning. In release builds, may cause `FlutterError` or incorrect navigation/dialog behavior when context is no longer valid.
|
|
49
|
+
- **cause**: After an `await` point, the widget may have been disposed or rebuilt, making the captured `BuildContext` stale. Using it for `Navigator.push`, `showDialog`, `ScaffoldMessenger.of(context)`, etc., can fail silently or crash.
|
|
50
|
+
- **strategy**: 1. Find every `await` inside event handlers or async methods that also use `context`. 2. Check the `use_build_context_synchronously` lint — enable it in `analysis_options.yaml`. 3. Before the `await`, save any context-dependent values (e.g., `Navigator.of(context)` → `final nav = Navigator.of(context);`). 4. After the `await`, add a `if (!mounted) return;` check before using `context` again. 5. Use `ref` (Riverpod) or BLoC events that don't capture context for post-async navigation.
|
|
51
|
+
- **toolSequence**: grep (`await`) in widget files → file_read (context usage after await) → file_edit (save navigator before await, add mounted check)
|
|
52
|
+
- **pitfall**: Do NOT suppress the lint with `// ignore:` — it exists to prevent real crashes. Fix the async context usage instead.
|
|
53
|
+
|
|
54
|
+
### Pattern: platform channel type mismatch
|
|
55
|
+
|
|
56
|
+
- **symptom**: `type 'int' is not a subtype of type 'double'` or `PlatformException: argument type 'LinkedHashMap<Object?, Object?>' is not a subtype of type 'Map<String, dynamic>'` — from platform channel callbacks
|
|
57
|
+
- **cause**: Platform channel (MethodChannel, EventChannel) passes types that differ between Dart and the native platform. Android/iOS may return `int` where Dart expects `double`, or a generic `Map<Object?, Object?>` where `Map<String, dynamic>` is expected. The codec does not automatically coerce types.
|
|
58
|
+
- **strategy**: 1. Read the exact types in the error message. 2. Cast the received value explicitly: `(result as num).toDouble()` for int/double mismatch. 3. For Map type issues, cast recursively: `Map<String, dynamic>.from(result as Map)`. 4. For nested maps, use a deep-cast helper or `jsonDecode(jsonEncode(result))` to normalize types. 5. Add type validation at the channel boundary with descriptive error messages.
|
|
59
|
+
- **toolSequence**: file_read (channel callback) → file_edit (add explicit cast with `.from()` or `as num`)
|
|
60
|
+
- **pitfall**: Do NOT use `dynamic` throughout the app to avoid casts — cast once at the channel boundary and use typed models internally.
|
|
61
|
+
|
|
62
|
+
## Verification
|
|
63
|
+
Run: `flutter analyze` and `dart analyze`
|
|
64
|
+
- `flutter analyze` exit 0 = no analyzer errors.
|
|
65
|
+
- For tests: `flutter test`
|
|
66
|
+
- For build: `flutter build apk --debug` or `flutter build ios --debug`
|
|
67
|
+
|
|
68
|
+
## Validation Checklist
|
|
69
|
+
- [ ] `flutter analyze` exits 0 with no errors
|
|
70
|
+
- [ ] All `setState` calls guarded with `if (mounted)` when in async callbacks
|
|
71
|
+
- [ ] All `late` variables have guaranteed initialization path before first read
|
|
72
|
+
- [ ] No `setState` inside `build()` — initialization moved to `initState`
|
|
73
|
+
- [ ] `context` not used after `await` without mounted check
|
|
74
|
+
- [ ] Platform channel values cast explicitly at the boundary
|
|
75
|
+
- [ ] `StreamSubscription`, `Timer`, and `AnimationController` cancelled/disposed in `dispose()`
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
## Identity
|
|
2
|
+
- domain: docker
|
|
3
|
+
- type: language
|
|
4
|
+
- confidence: 0.90
|
|
5
|
+
|
|
6
|
+
# Docker — Error Pattern Reference
|
|
7
|
+
|
|
8
|
+
Read the exact build error output and layer number first. Docker errors are often caused by layer ordering, missing context files, or base image issues — not the instruction itself.
|
|
9
|
+
|
|
10
|
+
## Error Code Quick Reference
|
|
11
|
+
- **"no such file or directory"** — COPY/ADD source path doesn't exist in build context.
|
|
12
|
+
- **"failed to solve: failed to read dockerfile"** — Dockerfile not found at specified path.
|
|
13
|
+
- **"exec format error"** — Binary built for wrong architecture (arm vs amd64).
|
|
14
|
+
- **"permission denied"** — File permission issue inside container, often USER switch.
|
|
15
|
+
- **"layer does not exist"** — Corrupted local image cache; `docker system prune` needed.
|
|
16
|
+
- **"Dockerfile parse error"** — Syntax error in instruction (e.g., missing space after FROM).
|
|
17
|
+
- **"unknown instruction"** — Typo in Dockerfile instruction keyword.
|
|
18
|
+
|
|
19
|
+
## Known Error Patterns
|
|
20
|
+
|
|
21
|
+
### Image Layer Bloat — RUN apt-get Without Cleanup
|
|
22
|
+
- **Symptom**: Final image size is hundreds of MB larger than expected; `docker history <image>` shows a single large layer.
|
|
23
|
+
- **Cause**: `RUN apt-get update && apt-get install -y <pkg>` creates a layer that includes the package index and cached `.deb` files. If `apt-get clean && rm -rf /var/lib/apt/lists/*` is in a separate `RUN` instruction, the bloat layer is already committed and cannot be removed.
|
|
24
|
+
- **Strategy**: 1. Run `docker history <image> --no-trunc` to find the bloated layer. 2. Merge all apt operations into a single `RUN` instruction ending with `&& apt-get clean && rm -rf /var/lib/apt/lists/*`. 3. Check if a smaller base image is appropriate (`alpine` vs `ubuntu`). 4. Use multi-stage builds to copy only the final artifact, leaving build tools behind.
|
|
25
|
+
- **Tool sequence**: shell_exec (`docker history`) → file_read (Dockerfile) → file_edit (merge RUN commands + cleanup)
|
|
26
|
+
- **Pitfall**: Do NOT put cleanup in a separate `RUN` layer — Docker layers are immutable, and a cleanup layer only adds overhead without reducing size.
|
|
27
|
+
|
|
28
|
+
### Non-Root User Missing — Security Risk
|
|
29
|
+
- **Symptom**: Container process runs as UID 0 (root); security scanners flag the image; Kubernetes PSP/PSA rejects it.
|
|
30
|
+
- **Cause**: No `USER` instruction in the Dockerfile. Default user is root, which means a compromised process inside the container has root privileges on the host filesystem if volumes are mounted.
|
|
31
|
+
- **Strategy**: 1. Grep the Dockerfile for `USER` instruction. 2. Before the final CMD/ENTRYPOINT, add: `RUN groupadd -r appuser && useradd -r -g appuser appuser` then `USER appuser`. 3. Ensure all application files are readable by this user (`COPY --chown=appuser:appuser . .`). 4. If the app needs specific capabilities (e.g., binding to port <1024), use `setcap` rather than running as root.
|
|
32
|
+
- **Tool sequence**: grep (`USER`) → file_read (Dockerfile) → file_edit (add user creation and USER instruction)
|
|
33
|
+
- **Pitfall**: Do NOT set `USER 0` to fix permission errors — that defeats the purpose. Fix the file permissions or use `--chown` in COPY instead.
|
|
34
|
+
|
|
35
|
+
### COPY vs ADD Confusion — Unexpected Behavior
|
|
36
|
+
- **Symptom**: Tar archives are auto-extracted when they should be copied as-is; remote URLs fail or succeed unexpectedly.
|
|
37
|
+
- **Cause**: `ADD` has two special behaviors that `COPY` does not: (1) it auto-extracts local tar archives, (2) it accepts URLs. Using `ADD` for regular files is confusing and considered bad practice by Docker best practices.
|
|
38
|
+
- **Strategy**: 1. Grep all `ADD` instructions in the Dockerfile. 2. Replace `ADD <local-file> <dest>` with `COPY <local-file> <dest>` unless tar auto-extraction is explicitly needed. 3. For remote URL downloads, use `RUN curl -o <dest> <url>` instead of `ADD <url>` — gives more control over the download (checksums, error handling). 4. Only use `ADD` when intentionally extracting a local tar archive.
|
|
39
|
+
- **Tool sequence**: grep (`^ADD `) → file_read (Dockerfile) → file_edit (replace ADD with COPY or RUN curl)
|
|
40
|
+
- **Pitfall**: Do NOT use `ADD` for everything just because it seems more powerful — the implicit auto-extraction is a common source of bugs when archive filenames change.
|
|
41
|
+
|
|
42
|
+
### Build Context Too Large — .dockerignore Missing
|
|
43
|
+
- **Symptom**: `docker build` is slow to start; "Sending build context to Docker daemon" reports gigabytes; `node_modules` or `.git` is sent to the daemon.
|
|
44
|
+
- **Cause**: Docker sends the entire build context directory to the daemon before processing the Dockerfile. Without a `.dockerignore` file, `node_modules`, `.git`, `dist`, large data files, and secrets are all included.
|
|
45
|
+
- **Strategy**: 1. Check if `.dockerignore` exists in the build context directory. 2. Create or update `.dockerignore` to exclude: `node_modules`, `.git`, `*.log`, `dist`, `.env*`, `__pycache__`, `.pytest_cache`, coverage reports, and any large binary assets. 3. Verify: run `docker build` again and check the "Sending build context" line — it should be kilobytes or a few megabytes. 4. Never COPY secrets (`.env`, credentials) into an image — use Docker secrets or build args.
|
|
46
|
+
- **Tool sequence**: shell_exec (`ls .dockerignore`) → file_edit (create/update .dockerignore) → shell_exec (docker build, check context size)
|
|
47
|
+
- **Pitfall**: Do NOT add `.dockerignore` entries after `COPY . .` instructions — the ignore happens at context-sending time, not at COPY time. Both must be correct.
|
|
48
|
+
|
|
49
|
+
### Health Check Missing — Orchestrator Cannot Detect Unhealthy Containers
|
|
50
|
+
- **Symptom**: Container shows as `Up` but application is not responding; Kubernetes/ECS marks pods healthy when they are not; rolling deployments shift traffic to broken instances.
|
|
51
|
+
- **Cause**: No `HEALTHCHECK` instruction in the Dockerfile. Without it, Docker only checks if the process is running (PID exists), not if the application is actually serving requests.
|
|
52
|
+
- **Strategy**: 1. Grep the Dockerfile for `HEALTHCHECK`. 2. Add a health check appropriate for the service type: HTTP services: `HEALTHCHECK --interval=30s --timeout=3s --retries=3 CMD curl -f http://localhost:8080/health || exit 1`. TCP services: `CMD nc -z localhost 8080`. Custom scripts: `CMD ["/app/healthcheck.sh"]`. 3. Ensure the health endpoint is lightweight and does not trigger side effects. 4. Test: `docker inspect --format='{{.State.Health}}' <container>`.
|
|
53
|
+
- **Tool sequence**: grep (`HEALTHCHECK`) → file_read (Dockerfile) → file_edit (add HEALTHCHECK instruction)
|
|
54
|
+
- **Pitfall**: Do NOT use a health check that exercises a database query unless the app genuinely requires DB connectivity at startup — this causes false unhealthy states during DB maintenance.
|
|
55
|
+
|
|
56
|
+
### Layer Cache Invalidation — Dependency Install Repeated
|
|
57
|
+
- **Symptom**: `npm install` or `pip install` runs every build even when dependencies haven't changed; builds take minutes instead of seconds.
|
|
58
|
+
- **Cause**: `COPY . .` before `RUN npm install` copies all source files first. Any source change invalidates the cache at that layer, causing npm install to re-run. Docker cache works top-to-bottom — each instruction invalidates all subsequent layers.
|
|
59
|
+
- **Strategy**: 1. Read the Dockerfile and identify where dependency manifests are copied vs. source files. 2. Restructure: copy ONLY the dependency manifests first (`COPY package.json package-lock.json ./`), run the install, THEN copy source (`COPY . .`). 3. This way the install layer is only invalidated when manifests change, not on every source edit.
|
|
60
|
+
- **Tool sequence**: file_read (Dockerfile) → file_edit (reorder COPY and RUN install instructions)
|
|
61
|
+
- **Pitfall**: Do NOT copy `package.json` and `package-lock.json` in the same instruction as all other files — split the COPY into two instructions.
|
|
62
|
+
|
|
63
|
+
## Verification
|
|
64
|
+
Run: `docker build -t test-image . && docker run --rm test-image`
|
|
65
|
+
- Successful build and run = baseline passing.
|
|
66
|
+
- Check image size: `docker images test-image` — compare against expected baseline.
|
|
67
|
+
- Run Hadolint: `hadolint Dockerfile` — no warnings at DL3 level or above.
|
|
68
|
+
- Inspect layers: `docker history test-image --no-trunc`
|
|
69
|
+
|
|
70
|
+
## Validation Checklist
|
|
71
|
+
- [ ] `.dockerignore` exists and excludes `node_modules`, `.git`, `.env*`, large binaries
|
|
72
|
+
- [ ] All `RUN apt-get install` instructions include `&& apt-get clean && rm -rf /var/lib/apt/lists/*`
|
|
73
|
+
- [ ] Non-root `USER` instruction present before CMD/ENTRYPOINT
|
|
74
|
+
- [ ] `COPY` used instead of `ADD` for local files that are not tar archives
|
|
75
|
+
- [ ] `HEALTHCHECK` instruction present with appropriate interval and timeout
|
|
76
|
+
- [ ] Dependency manifests copied before source files (layer cache optimization)
|
|
77
|
+
- [ ] Multi-stage build used if build tools should not be in the final image
|
|
78
|
+
- [ ] No secrets or `.env` files copied into the image
|
|
79
|
+
- [ ] Base image pinned to a specific digest or version tag, not `latest`
|
|
80
|
+
- [ ] Hadolint passes with no high-severity warnings
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
## Identity
|
|
2
|
+
- domain: elixir
|
|
3
|
+
- type: language
|
|
4
|
+
- confidence: 0.90
|
|
5
|
+
|
|
6
|
+
# Elixir — Error Pattern Reference
|
|
7
|
+
|
|
8
|
+
Read the exact exception type, message, and stacktrace. Elixir errors are structured — the module, function, and arity in the stacktrace tell you exactly where the failure occurred.
|
|
9
|
+
|
|
10
|
+
## Error Code Quick Reference
|
|
11
|
+
- **FunctionClauseError** — No function clause matched the given arguments.
|
|
12
|
+
- **MatchError** — Pattern match failed in `=` or `case`/`with`.
|
|
13
|
+
- **UndefinedFunctionError** — Module or function not found (missing import, wrong arity).
|
|
14
|
+
- **ArgumentError** — Invalid argument to a function (often nil where value expected).
|
|
15
|
+
- **KeyError** — Key not found in map (use `Map.get/3` with default instead of `map[key]!`).
|
|
16
|
+
- **RuntimeError** — Explicit `raise "message"` or `raise RuntimeError, message: "..."`.
|
|
17
|
+
- **EXIT from #PID** — A linked/monitored process crashed; check the reason.
|
|
18
|
+
- **** (Ecto.NoResultsError)** — `Repo.get!` or `Repo.one!` found no records.
|
|
19
|
+
|
|
20
|
+
## Known Error Patterns
|
|
21
|
+
|
|
22
|
+
### GenServer Timeout — handle_call/cast Not Returning
|
|
23
|
+
- **Symptom**: `** (exit) exited in: GenServer.call/3 with reason: :timeout`; caller process crashes after 5000ms default timeout; server appears unresponsive.
|
|
24
|
+
- **Cause**: `handle_call/3` is blocking on a slow operation (database query, HTTP request, file I/O) and does not return within the timeout. Or `handle_call` is calling another GenServer which itself is blocked, creating a deadlock.
|
|
25
|
+
- **Strategy**: 1. Read the `handle_call` implementation for the timed-out message type. 2. Move slow operations out of `handle_call`: reply immediately with `:noreply`, spawn a task, and send the result back via `GenServer.reply/2`. 3. For truly long operations, increase the timeout: `GenServer.call(server, msg, 30_000)`. 4. Use `Task.async/await` or `Task.Supervisor` for concurrent work. 5. Avoid chained `GenServer.call` between processes that can form circular waits.
|
|
26
|
+
- **Tool sequence**: grep (`handle_call`, `GenServer.call`) → file_read → file_edit (extract slow work to Task, use cast + send instead of call)
|
|
27
|
+
- **Pitfall**: Do NOT increase timeout indefinitely as the primary fix — a timed-out call indicates a design issue. The GenServer's inbox continues to fill while it's blocked.
|
|
28
|
+
|
|
29
|
+
### Process Leak — No Supervision Tree
|
|
30
|
+
- **Symptom**: Memory usage grows over time; `Process.list() |> length` increases; `:observer.start()` shows thousands of processes with no parents.
|
|
31
|
+
- **Cause**: Processes spawned with `spawn/1` or `Task.start/1` are not supervised. If they crash, they leave no trace. If they leak, nothing cleans them up. Long-running work done in unsupervised processes accumulates.
|
|
32
|
+
- **Strategy**: 1. Grep all `spawn(`, `Task.start(`, and `Process.spawn(` calls. 2. Replace fire-and-forget tasks with `Task.Supervisor.start_child(MyApp.TaskSupervisor, fn -> ... end)`. 3. For recurring workers, use `GenServer` under a `Supervisor`. 4. Define a supervision tree in `application.ex` and add all long-lived processes to it. 5. Use `DynamicSupervisor` for runtime-spawned processes.
|
|
33
|
+
- **Tool sequence**: grep (`spawn(`, `Task\.start(`) → file_read → file_edit (replace with Task.Supervisor or add to supervision tree)
|
|
34
|
+
- **Pitfall**: Do NOT add processes to a supervision tree without understanding restart strategies — a `one_for_all` supervisor will restart all children when one crashes.
|
|
35
|
+
|
|
36
|
+
### Pattern Match Failure — FunctionClauseError
|
|
37
|
+
- **Symptom**: `** (FunctionClauseError) no function clause matching in MyModule.my_fun/2`; occurs at runtime even though the function is defined.
|
|
38
|
+
- **Cause**: The function has multiple clauses with pattern matches in the arguments. The call arguments don't match any clause. Common causes: nil passed where a struct is expected, wrong map keys, incorrect atom.
|
|
39
|
+
- **Strategy**: 1. Read the full error — it shows the arguments that failed to match. 2. Read all clauses of the function in order — Elixir matches top to bottom. 3. Add a catch-all clause at the bottom for graceful error handling: `def my_fun(arg), do: {:error, {:unexpected_argument, arg}}`. 4. Use `IO.inspect(args, label: "my_fun args")` temporarily to log incoming values. 5. If nil is possible, add a nil-handling clause before the main clause.
|
|
40
|
+
- **Tool sequence**: file_read (all clauses of the failing function) → file_edit (add missing clause or fix call site to pass correct arguments)
|
|
41
|
+
- **Pitfall**: Do NOT add a catch-all clause that silently ignores errors — always return an error tuple or raise with context so the problem is visible.
|
|
42
|
+
|
|
43
|
+
### Ecto Changeset Validation Error Ignored
|
|
44
|
+
- **Symptom**: Data is saved to the database with invalid or incomplete values; validation rules defined in the changeset have no effect; tests pass but production data is corrupted.
|
|
45
|
+
- **Cause**: The result of `Repo.insert/2` or `Repo.update/2` returns `{:error, changeset}` on validation failure, but the caller only pattern-matches the success case: `{:ok, record} = Repo.insert(changeset)`. The match error causes a crash, or the error is ignored entirely.
|
|
46
|
+
- **Strategy**: 1. Grep all `Repo.insert`, `Repo.update`, `Repo.delete` calls. 2. Verify each uses a `case` or `with` expression that handles both `{:ok, record}` and `{:error, changeset}`. 3. Never use `Repo.insert!` in production code paths that can fail validation — use `Repo.insert/2` with explicit error handling. 4. In Phoenix controllers, use `render(conn, :new, changeset: changeset)` to re-render the form with errors.
|
|
47
|
+
- **Tool sequence**: grep (`Repo\.insert`, `Repo\.update`) → file_read → file_edit (add case/with error handling)
|
|
48
|
+
- **Pitfall**: Do NOT use `{:ok, _} = Repo.insert!(...)` — `insert!` raises on failure. Use `insert/2` + case expression for user-facing operations.
|
|
49
|
+
|
|
50
|
+
### Atom Exhaustion — Dynamic Atom Creation
|
|
51
|
+
- **Symptom**: `** (SystemLimitError) a system limit has been reached`; BEAM VM crashes after processing many unique strings as atoms; memory grows until VM limit (~1 million atoms by default).
|
|
52
|
+
- **Cause**: Atoms in Elixir are never garbage collected. Converting arbitrary user input to atoms with `String.to_atom/1` or `:erlang.binary_to_atom/2` exhausts the atom table. This is a denial-of-service vector if user input drives atom creation.
|
|
53
|
+
- **Strategy**: 1. Grep all `String.to_atom(`, `:erlang.binary_to_atom(`, and `:"#{var}"` interpolations. 2. Replace with `String.to_existing_atom/1` for known atoms (raises if atom doesn't exist — safe). 3. For map keys from external data (JSON, APIs), use string keys instead of atom keys. 4. Use `Jason.decode!(json, keys: :strings)` instead of `keys: :atoms` for JSON parsing. 5. For finite, known sets of atoms, define a conversion function with a `case` statement.
|
|
54
|
+
- **Tool sequence**: grep (`String\.to_atom`, `binary_to_atom`) → file_read → file_edit (replace with String.to_existing_atom or keep as strings)
|
|
55
|
+
- **Pitfall**: Do NOT use `String.to_existing_atom/1` for user input that could send arbitrary strings — it still creates atoms at compile time for every match clause, and it raises on unknown atoms which may be handled incorrectly.
|
|
56
|
+
|
|
57
|
+
### Hot Code Reload State Loss — GenServer State Schema Change
|
|
58
|
+
- **Symptom**: After deploying a new version, GenServer crashes immediately with `FunctionClauseError` or `MatchError` on the state; rollback restores stability.
|
|
59
|
+
- **Cause**: The state data structure of a GenServer was changed (e.g., a map field added/removed, a struct field renamed) without implementing `code_change/3`. Old processes running the old code have state in the old format; new code expects the new format.
|
|
60
|
+
- **Strategy**: 1. Implement `code_change/3` in the GenServer to migrate old state to new state format. 2. For OTP releases, use `:sys.replace_state/2` during deployment to update running processes. 3. Prefer maps over structs for GenServer state to allow forward compatibility. 4. During blue-green deployments, drain old processes before starting new ones.
|
|
61
|
+
- **Tool sequence**: file_read (GenServer module) → file_edit (add code_change/3 with state migration)
|
|
62
|
+
- **Pitfall**: Do NOT rely on process restart (via supervisor) alone to fix state schema changes — the new process will start with empty/default state, losing all in-memory data.
|
|
63
|
+
|
|
64
|
+
## Verification
|
|
65
|
+
Run: `mix compile --warnings-as-errors && mix test`
|
|
66
|
+
- All tests must pass with zero compilation warnings.
|
|
67
|
+
- Run dialyzer: `mix dialyzer` — no type errors.
|
|
68
|
+
- Check supervision tree: `Application.started_applications()` and `:observer.start()`.
|
|
69
|
+
|
|
70
|
+
## Validation Checklist
|
|
71
|
+
- [ ] All `Repo.insert/update/delete` calls handle `{:error, changeset}` case
|
|
72
|
+
- [ ] No `String.to_atom/1` called with user input or external data
|
|
73
|
+
- [ ] All long-lived processes are under a supervision tree
|
|
74
|
+
- [ ] No `spawn/1` without corresponding supervisor or `Task.Supervisor`
|
|
75
|
+
- [ ] All `GenServer.call/3` callers handle `:timeout` gracefully
|
|
76
|
+
- [ ] `mix dialyzer` passes with no type errors
|
|
77
|
+
- [ ] All function clauses have a catch-all or explicit error-returning clause
|
|
78
|
+
- [ ] No blocking I/O inside `handle_call/3` — use async reply pattern
|
|
79
|
+
- [ ] `code_change/3` implemented for GenServers with non-trivial state
|
|
80
|
+
- [ ] `mix test --cover` shows all critical paths covered
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
## Identity
|
|
2
|
+
- domain: gdscript
|
|
3
|
+
- type: language
|
|
4
|
+
- confidence: 0.88
|
|
5
|
+
|
|
6
|
+
# GDScript — Error Pattern Reference
|
|
7
|
+
|
|
8
|
+
Read the exact Godot error output including script path, line number, and the full message. GDScript errors often come from the editor Output panel or runtime debugger — check both.
|
|
9
|
+
|
|
10
|
+
## Error Code Quick Reference
|
|
11
|
+
- **"Invalid get index 'x' on base 'null instance'"** — Accessing property on a null node reference.
|
|
12
|
+
- **"Node not found: 'NodePath'"** — get_node() path is wrong or the node doesn't exist yet.
|
|
13
|
+
- **"Cannot call method 'connect' on null value"** — Signal target node is null.
|
|
14
|
+
- **"Nonexistent signal 'signal_name'"** — Signal not defined or misspelled.
|
|
15
|
+
- **"Type mismatch: expected built-in 'int', got 'String'"** — Export variable type mismatch in inspector.
|
|
16
|
+
- **"Parse error: expected 'end of statement', got..."** — Indentation or syntax error.
|
|
17
|
+
- **"Function 'func_name' already has a body"** — Duplicate function definition.
|
|
18
|
+
- **"Identifier 'name' is not declared"** — Variable used before declaration or out of scope.
|
|
19
|
+
|
|
20
|
+
## Known Error Patterns
|
|
21
|
+
|
|
22
|
+
### Signal Not Connected — connect() Missing
|
|
23
|
+
- **Symptom**: An event (button press, timer timeout, area entered) fires but nothing happens; no error is shown. Or `Error: Signal 'pressed' is already connected` on duplicate connects.
|
|
24
|
+
- **Cause**: The signal was defined with `signal my_signal` or exists on a node (e.g., `Button.pressed`) but `connect()` was never called, or it was connected in the wrong node's `_ready()`. In Godot 4, the callable syntax changed from `connect("signal_name", self, "method_name")` to `signal_name.connect(method)`.
|
|
25
|
+
- **Strategy**: 1. Grep the script for the signal name to find where it should be connected. 2. Verify `connect()` is called in `_ready()` of the node that owns the signal or a parent. 3. In Godot 4, use `$NodeName.signal_name.connect(_on_signal)` syntax. 4. Alternatively, connect signals in the Godot editor (Scene panel → Node tab → Signals) and check the editor connection icons. 5. Add `assert($NodeName != null)` before connect calls to catch null nodes early.
|
|
26
|
+
- **Tool sequence**: grep (`connect(`, `signal `) → file_read → file_edit (add connect() call in _ready())
|
|
27
|
+
- **Pitfall**: Do NOT connect signals inside `_process()` or `_physics_process()` — you will create thousands of duplicate connections per second, causing severe performance degradation.
|
|
28
|
+
|
|
29
|
+
### Null Node Reference — get_node Path Wrong
|
|
30
|
+
- **Symptom**: `Invalid get index 'x' on base 'null instance'` or `Node not found: 'Player/Weapon'`; crash occurs the moment a node is accessed.
|
|
31
|
+
- **Cause**: `get_node("path")` or `$NodePath` shorthand points to a node that doesn't exist at that path, was renamed, or was not yet added to the scene tree when `_ready()` ran.
|
|
32
|
+
- **Strategy**: 1. Open the scene in the Godot editor and verify the exact node path using the Scene tree panel. 2. Check for typos in the path — paths are case-sensitive. 3. If the node is added dynamically, access it after it is added, not in `_ready()`. 4. Use `@onready var node = $NodePath` (Godot 4) or `onready var node = $NodePath` (Godot 3) to defer node lookup to scene tree entry. 5. Add null checks: `if node != null:` before accessing properties.
|
|
33
|
+
- **Tool sequence**: file_read (script) → shell_exec (check scene .tscn file for node names) → file_edit (fix path or add @onready)
|
|
34
|
+
- **Pitfall**: Do NOT use `get_node()` in `_init()` — the node is not in the scene tree yet. Always use `_ready()` or `@onready` for node references.
|
|
35
|
+
|
|
36
|
+
### _ready vs _init Timing Issue — Node Not in Tree
|
|
37
|
+
- **Symptom**: Accessing sibling nodes, calling `get_parent()`, or reading export variables in `_init()` returns null or wrong values; works fine when moved to `_ready()`.
|
|
38
|
+
- **Cause**: `_init()` is called when the object is created in memory, before it is added to the scene tree. `_ready()` is called after the node and all its children have entered the scene tree. Scene-dependent operations must be in `_ready()`.
|
|
39
|
+
- **Strategy**: 1. Grep for `_init()` functions in scripts. 2. Identify any node access, `get_node()`, `get_parent()`, or signal connections inside `_init()`. 3. Move scene-dependent initialization to `_ready()`. 4. Keep `_init()` for pure in-memory object initialization (setting default values for non-node properties). 5. For class instantiation via `ClassName.new(args)`, pass data through `_init()` parameters only — do not access the tree.
|
|
40
|
+
- **Tool sequence**: grep (`func _init`) → file_read → file_edit (move scene access to _ready())
|
|
41
|
+
- **Pitfall**: Do NOT add `await get_tree().process_frame` in `_init()` to "wait" for the tree — use `_ready()` which is guaranteed to fire after tree entry.
|
|
42
|
+
|
|
43
|
+
### Export Variable Type Mismatch — Inspector Corruption
|
|
44
|
+
- **Symptom**: `Type mismatch: expected int, got String`; inspector shows wrong value type; game behavior is wrong even though code looks correct.
|
|
45
|
+
- **Cause**: The `@export` variable type annotation was changed after the scene was saved. The `.tscn` scene file stores the serialized value in the old type. On load, Godot tries to assign the old value to the new type.
|
|
46
|
+
- **Strategy**: 1. Open the `.tscn` file in a text editor and find the serialized property value. 2. Correct the value to match the new type (e.g., change `"10"` to `10` for int). 3. Or reset the property in the Godot inspector by right-clicking and selecting "Reset to Default". 4. When changing export types, always update all scenes that use the script. 5. Use `@export_enum` for string enums instead of raw strings to avoid type confusion.
|
|
47
|
+
- **Tool sequence**: file_read (.tscn file) → file_edit (fix serialized property type) → shell_exec (open in Godot to verify)
|
|
48
|
+
- **Pitfall**: Do NOT ignore type mismatch warnings — Godot may silently coerce the value, leading to subtle bugs (e.g., `"0"` being truthy when `0` is falsy).
|
|
49
|
+
|
|
50
|
+
### Scene Instancing Memory Leak — queue_free Missing
|
|
51
|
+
- **Symptom**: Game stutters over time; memory usage grows with every level load; profiler shows thousands of orphaned nodes.
|
|
52
|
+
- **Cause**: Dynamically instanced scenes (via `PackedScene.instantiate()`) are added to the tree but never removed. When the parent scene is unloaded, child instances are orphaned if not properly freed. Circular references between nodes also prevent garbage collection.
|
|
53
|
+
- **Strategy**: 1. For every `instantiate()` and `add_child()` call, verify there is a corresponding `queue_free()` or `remove_child()` + `free()` call when the instance is no longer needed. 2. For bullets, enemies, and particles: use an object pool instead of instantiating/freeing per-frame. 3. Connect the `tree_exited` signal to a cleanup function. 4. Use Godot's built-in profiler (Debug → Monitors) to track node count over time. 5. Avoid keeping references to freed nodes — use `is_instance_valid(node)` before accessing.
|
|
54
|
+
- **Tool sequence**: grep (`instantiate()`, `add_child(`) → file_read → file_edit (add queue_free() in appropriate lifecycle location)
|
|
55
|
+
- **Pitfall**: Do NOT call `free()` directly on nodes that are in the scene tree — use `queue_free()` which safely defers deletion to the end of the current frame.
|
|
56
|
+
|
|
57
|
+
### Infinite Loop in _process — Frame Freeze
|
|
58
|
+
- **Symptom**: Game freezes completely; editor becomes unresponsive; CPU spikes to 100%.
|
|
59
|
+
- **Cause**: A `while` loop or recursive call inside `_process()` or `_physics_process()` never terminates. These callbacks are called every frame and must return quickly.
|
|
60
|
+
- **Strategy**: 1. Grep for `while` loops and deep recursion inside `_process` and `_physics_process`. 2. Convert iterative work to state machines that progress one step per frame. 3. For async work, use `await` with signals or coroutines. 4. Add a frame counter or timeout as a safety valve: `if iterations > MAX_ITERATIONS: break`.
|
|
61
|
+
- **Tool sequence**: grep (`func _process`, `func _physics_process`) → file_read → file_edit (convert while loop to state machine or coroutine)
|
|
62
|
+
- **Pitfall**: Do NOT use `while true: await get_tree().process_frame` in production logic — use proper state machines or timers.
|
|
63
|
+
|
|
64
|
+
## Verification
|
|
65
|
+
Run the scene in the Godot editor with the Debugger panel open.
|
|
66
|
+
- No errors or warnings in the Output panel on startup.
|
|
67
|
+
- Memory usage (Debug → Monitors → Memory Used) should remain stable over time.
|
|
68
|
+
- Frame rate should be consistent — check Debug → Monitors → FPS.
|
|
69
|
+
|
|
70
|
+
## Validation Checklist
|
|
71
|
+
- [ ] All signals are connected in `_ready()`, not `_init()` or `_process()`
|
|
72
|
+
- [ ] All `get_node()` / `$Node` references verified against the actual scene tree
|
|
73
|
+
- [ ] `@onready` used for node references that must exist when ready
|
|
74
|
+
- [ ] All dynamically instantiated scenes have a corresponding `queue_free()` path
|
|
75
|
+
- [ ] No scene-dependent code in `_init()`
|
|
76
|
+
- [ ] Export variable types match the values serialized in `.tscn` files
|
|
77
|
+
- [ ] No `while` loops in `_process()` that could block the frame
|
|
78
|
+
- [ ] `is_instance_valid()` used before accessing potentially freed node references
|
|
79
|
+
- [ ] Memory monitor checked for leaks during extended play sessions
|
|
80
|
+
- [ ] No duplicate signal connections (check with `is_connected()` guard if connecting dynamically)
|