sdd-full 4.6.2 → 4.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin.js +1 -1
- package/package.json +1 -1
- package/skills/.agents/skills/flutter-add-integration-test/SKILL.md +165 -0
- package/skills/.agents/skills/flutter-add-widget-preview/SKILL.md +147 -0
- package/skills/.agents/skills/flutter-add-widget-test/SKILL.md +156 -0
- package/skills/.agents/skills/flutter-apply-architecture-best-practices/SKILL.md +164 -0
- package/skills/.agents/skills/flutter-build-responsive-layout/SKILL.md +141 -0
- package/skills/.agents/skills/flutter-fix-layout-issues/SKILL.md +132 -0
- package/skills/.agents/skills/flutter-implement-json-serialization/SKILL.md +155 -0
- package/skills/.agents/skills/flutter-setup-declarative-routing/SKILL.md +257 -0
- package/skills/.agents/skills/flutter-setup-localization/SKILL.md +212 -0
- package/skills/.agents/skills/flutter-use-http-package/SKILL.md +177 -0
- package/skills/VERSION.md +176 -62
- package/skills/design-planning/ai-coding-rules/SKILL.md +5 -13
- package/skills/design-planning/design-to-code/SKILL.md +5 -14
- package/skills/design-planning/enterprise-spec/SKILL.md +5 -13
- package/skills/design-planning/flutter-av/SKILL.md +5 -16
- package/skills/design-planning/flutter-map/SKILL.md +5 -14
- package/skills/design-planning/function-sdd/SKILL.md +5 -13
- package/skills/design-planning/global-overlay-stack-standard/SKILL.md +73 -0
- package/skills/design-planning/ui-motion-interaction-standard/SKILL.md +69 -0
- package/skills/design-planning/ui-sdd-specialized/SKILL.md +5 -14
- package/skills/development-execution/flutter-errors/SKILL.md +5 -15
- package/skills/flutter-skills/.github/dependabot.yaml +15 -0
- package/skills/flutter-skills/.github/workflows/dart_skills_lint_workflow.yaml +68 -0
- package/skills/flutter-skills/.github/workflows/skills_tool.yaml +51 -0
- package/skills/flutter-skills/CODE_OF_CONDUCT.md +3 -0
- package/skills/flutter-skills/CONTRIBUTING.md +36 -0
- package/skills/flutter-skills/LICENSE +26 -0
- package/skills/flutter-skills/README.md +50 -0
- package/skills/flutter-skills/pubspec.yaml +9 -0
- package/skills/flutter-skills/resources/flutter_skills.yaml +434 -0
- package/skills/flutter-skills/skills/flutter-add-integration-test/SKILL.md +163 -0
- package/skills/flutter-skills/skills/flutter-add-widget-preview/SKILL.md +145 -0
- package/skills/flutter-skills/skills/flutter-add-widget-test/SKILL.md +154 -0
- package/skills/flutter-skills/skills/flutter-apply-architecture-best-practices/SKILL.md +162 -0
- package/skills/flutter-skills/skills/flutter-build-responsive-layout/SKILL.md +139 -0
- package/skills/flutter-skills/skills/flutter-fix-layout-issues/SKILL.md +130 -0
- package/skills/flutter-skills/skills/flutter-implement-json-serialization/SKILL.md +153 -0
- package/skills/flutter-skills/skills/flutter-setup-declarative-routing/SKILL.md +255 -0
- package/skills/flutter-skills/skills/flutter-setup-localization/SKILL.md +210 -0
- package/skills/flutter-skills/skills/flutter-use-http-package/SKILL.md +175 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/add-dart-lint-validation-rule/SKILL.md +196 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-best-practices/SKILL.md +65 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-checks-migration/SKILL.md +158 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-cli-app-best-practices/SKILL.md +168 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-doc-validation/SKILL.md +87 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-long-lines/SKILL.md +101 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-matcher-best-practices/SKILL.md +136 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-modern-features/SKILL.md +266 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-package-maintenance/SKILL.md +92 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-coverage/SKILL.md +92 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-coverage/example/lib/src/calculator.dart +7 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-coverage/example/pubspec.yaml +8 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-coverage/example/test/calculator_test.dart +11 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-coverage/scripts/interpret_coverage.dart +95 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-coverage/scripts/pubspec.yaml +6 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-coverage/scripts/test/interpret_coverage_test.dart +93 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/dart-test-fundamentals/SKILL.md +173 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/definition-of-done/SKILL.md +27 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/flutter_skills_ignore.json +3 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/grill-me/SKILL.md +10 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/ignore.json +3 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/test-driven-development/SKILL.md +371 -0
- package/skills/flutter-skills/tool/dart_skills_lint/.agents/skills/test-driven-development/testing-anti-patterns.md +299 -0
- package/skills/flutter-skills/tool/dart_skills_lint/AUTHORS +7 -0
- package/skills/flutter-skills/tool/dart_skills_lint/CHANGELOG.md +12 -0
- package/skills/flutter-skills/tool/dart_skills_lint/CONTRIBUTING.md +51 -0
- package/skills/flutter-skills/tool/dart_skills_lint/LICENSE +27 -0
- package/skills/flutter-skills/tool/dart_skills_lint/README.md +203 -0
- package/skills/flutter-skills/tool/dart_skills_lint/analysis_options.yaml +296 -0
- package/skills/flutter-skills/tool/dart_skills_lint/bench/README.md +23 -0
- package/skills/flutter-skills/tool/dart_skills_lint/bench/baseline_throughput.dart +230 -0
- package/skills/flutter-skills/tool/dart_skills_lint/bin/cli.dart +10 -0
- package/skills/flutter-skills/tool/dart_skills_lint/dart_skills_lint.yaml +14 -0
- package/skills/flutter-skills/tool/dart_skills_lint/documentation/feature_design_docs/PRODUCTION_READYNESS.md +48 -0
- package/skills/flutter-skills/tool/dart_skills_lint/documentation/feature_design_docs/completion_migration_plan.md +99 -0
- package/skills/flutter-skills/tool/dart_skills_lint/documentation/feature_design_docs/legacy_patterns_report.md +110 -0
- package/skills/flutter-skills/tool/dart_skills_lint/documentation/feature_design_docs/pub_vs_skill_report.md +56 -0
- package/skills/flutter-skills/tool/dart_skills_lint/documentation/knowledge/SPECIFICATION.md +79 -0
- package/skills/flutter-skills/tool/dart_skills_lint/documentation/knowledge/architecture_overview.md +64 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/dart_skills_lint.dart +11 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/config_parser.dart +156 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/entry_point.dart +354 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/fixable_rule.dart +20 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/analysis_severity.dart +15 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/check_type.dart +17 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/ignore_entry.dart +34 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/ignore_entry.g.dart +19 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/skill_context.dart +27 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/skill_rule.dart +27 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/skills_ignores.dart +26 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/skills_ignores.g.dart +24 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/models/validation_error.dart +31 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rule_registry.dart +79 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rules/absolute_paths_rule.dart +74 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rules/description_length_rule.dart +49 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rules/disallowed_field_rule.dart +61 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rules/name_format_rule.dart +167 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rules/relative_paths_rule.dart +72 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rules/trailing_whitespace_rule.dart +93 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/rules/valid_yaml_metadata_rule.dart +74 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/skills_ignores_storage.dart +36 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/validation_session.dart +559 -0
- package/skills/flutter-skills/tool/dart_skills_lint/lib/src/validator.dart +238 -0
- package/skills/flutter-skills/tool/dart_skills_lint/pubspec.yaml +28 -0
- package/skills/flutter-skills/tool/dart_skills_lint/skills/README.md +10 -0
- package/skills/flutter-skills/tool/dart_skills_lint/skills/dart-skills-lint-validation/SKILL.md +195 -0
- package/skills/flutter-skills/tool/dart_skills_lint/skills-lock.json +75 -0
- package/skills/flutter-skills/tool/dart_skills_lint/test/absolute_paths_test.dart +167 -0
- package/skills/flutter-skills/tool/dart_skills_lint/test/cli_integration_test.dart +683 -0
- package/skills/flutter-skills/tool/dart_skills_lint/test/config_file_test.dart +292 -0
- package/skills/flutter-skills/tool/dart_skills_lint/test/custom_rule_test.dart +122 -0
- package/skills/flutter-skills/tool/dart_skills_lint/test/directory_structure_test.dart +163 -0
- package/skills/flutter-skills/tool/dart_skills_lint/test/field_constraints_test.dart +178 -0
- package/skills/flutter-skills/tool/dart_skills_lint/test/fixer_test.dart +172 -0
- package/skills/flutter-skills/tool/dart_skills_lint/test/ignore_models_test.dart +63 -0
- package/skills/flutter-skills/tool/dart_skills_lint/test/metadata_validation_test.dart +116 -0
- package/skills/flutter-skills/tool/dart_skills_lint/test/relative_path_flag_test.dart +70 -0
- package/skills/flutter-skills/tool/dart_skills_lint/test/relative_paths_test.dart +172 -0
- package/skills/flutter-skills/tool/dart_skills_lint/test/resolve_rules_test.dart +82 -0
- package/skills/flutter-skills/tool/dart_skills_lint/test/rule_naming_test.dart +29 -0
- package/skills/flutter-skills/tool/dart_skills_lint/test/skills_ignores_storage_test.dart +89 -0
- package/skills/flutter-skills/tool/dart_skills_lint/test/test_utils.dart +19 -0
- package/skills/flutter-skills/tool/dart_skills_lint/test/trailing_whitespace_test.dart +152 -0
- package/skills/flutter-skills/tool/generator/README.md +150 -0
- package/skills/flutter-skills/tool/generator/analysis_options.yaml +143 -0
- package/skills/flutter-skills/tool/generator/bin/skills.dart +73 -0
- package/skills/flutter-skills/tool/generator/lib/src/commands/base_skill_command.dart +87 -0
- package/skills/flutter-skills/tool/generator/lib/src/commands/base_yaml_command.dart +83 -0
- package/skills/flutter-skills/tool/generator/lib/src/commands/generate_skill_command.dart +92 -0
- package/skills/flutter-skills/tool/generator/lib/src/commands/update_readme_command.dart +150 -0
- package/skills/flutter-skills/tool/generator/lib/src/commands/update_skill_command.dart +97 -0
- package/skills/flutter-skills/tool/generator/lib/src/commands/validate_skill_command.dart +284 -0
- package/skills/flutter-skills/tool/generator/lib/src/models/skill_params.dart +41 -0
- package/skills/flutter-skills/tool/generator/lib/src/services/gemini_service.dart +310 -0
- package/skills/flutter-skills/tool/generator/lib/src/services/markdown_converter.dart +226 -0
- package/skills/flutter-skills/tool/generator/lib/src/services/prompts.dart +72 -0
- package/skills/flutter-skills/tool/generator/lib/src/services/resource_fetcher_service.dart +84 -0
- package/skills/flutter-skills/tool/generator/lib/src/services/skill_instructions.dart +30 -0
- package/skills/flutter-skills/tool/generator/pubspec.yaml +32 -0
- package/skills/flutter-skills/tool/generator/test/commands/base_skill_command_test.dart +131 -0
- package/skills/flutter-skills/tool/generator/test/commands/validate_skills_input_test.dart +263 -0
- package/skills/flutter-skills/tool/generator/test/custom_skill_rules/last_modified_rule.dart +32 -0
- package/skills/flutter-skills/tool/generator/test/generate_skills_retry_test.dart +105 -0
- package/skills/flutter-skills/tool/generator/test/generate_skills_test.dart +519 -0
- package/skills/flutter-skills/tool/generator/test/lint_skills_test.dart +34 -0
- package/skills/flutter-skills/tool/generator/test/markdown_converter_test.dart +103 -0
- package/skills/flutter-skills/tool/generator/test/markdown_table_test.dart +131 -0
- package/skills/flutter-skills/tool/generator/test/models/skill_params_test.dart +37 -0
- package/skills/flutter-skills/tool/generator/test/services/gemini_service_test.dart +291 -0
- package/skills/flutter-skills/tool/generator/test/services/markdown_converter_test.dart +156 -0
- package/skills/flutter-skills/tool/generator/test/services/resource_fetcher_service_test.dart +188 -0
- package/skills/flutter-skills/tool/generator/test/update_skills_test.dart +241 -0
- package/skills/flutter-skills/tool/generator/test/validate_skills_test.dart +728 -0
- package/skills/quality-assurance/bdd-acceptance/SKILL.md +5 -14
- package/skills/quality-assurance/flutter-test/SKILL.md +5 -16
- package/skills/rules/project_rules.md +538 -127
- package/skills/special-tools/env-check/SKILL.md +5 -13
- package/skills/special-tools/ios-full-auto-debug/SKILL.md +5 -15
- package/skills/writing-skills/SKILL.md +654 -0
- package/skills/writing-skills/anthropic-best-practices.md +1149 -0
- package/skills/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
- package/skills/writing-skills/graphviz-conventions.dot +172 -0
- package/skills/writing-skills/persuasion-principles.md +187 -0
- package/skills/writing-skills/render-graphs.js +168 -0
- package/skills/writing-skills/testing-skills-with-subagents.md +384 -0
- package/skills/checklist.md +0 -154
- package/skills/rules/user_rules.md +0 -263
- package/skills//345/256/214/346/225/264/345/274/200/345/217/221/346/265/201/347/250/213/346/211/213/345/206/214.md +0 -454
- package/skills//346/212/200/350/203/275/344/275/223/347/263/273/345/256/214/345/226/204/345/273/272/350/256/256.md +0 -308
- package/skills//346/212/200/350/203/275/344/275/277/347/224/250/346/214/207/345/215/227.md +0 -309
- package/skills//346/212/200/350/203/275/345/206/263/347/255/226/346/240/221.md +0 -338
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
# Testing Anti-Patterns
|
|
2
|
+
|
|
3
|
+
**Load this reference when:** writing or changing tests, adding mocks, or tempted to add test-only methods to production code.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
Tests must verify real behavior, not mock behavior. Mocks are a means to isolate, not the thing being tested.
|
|
8
|
+
|
|
9
|
+
**Core principle:** Test what the code does, not what the mocks do.
|
|
10
|
+
|
|
11
|
+
**Following strict TDD prevents these anti-patterns.**
|
|
12
|
+
|
|
13
|
+
## The Iron Laws
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
1. NEVER test mock behavior
|
|
17
|
+
2. NEVER add test-only methods to production classes
|
|
18
|
+
3. NEVER mock without understanding dependencies
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Anti-Pattern 1: Testing Mock Behavior
|
|
22
|
+
|
|
23
|
+
**The violation:**
|
|
24
|
+
```typescript
|
|
25
|
+
// ❌ BAD: Testing that the mock exists
|
|
26
|
+
test('renders sidebar', () => {
|
|
27
|
+
render(<Page />);
|
|
28
|
+
expect(screen.getByTestId('sidebar-mock')).toBeInTheDocument();
|
|
29
|
+
});
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Why this is wrong:**
|
|
33
|
+
- You're verifying the mock works, not that the component works
|
|
34
|
+
- Test passes when mock is present, fails when it's not
|
|
35
|
+
- Tells you nothing about real behavior
|
|
36
|
+
|
|
37
|
+
**your human partner's correction:** "Are we testing the behavior of a mock?"
|
|
38
|
+
|
|
39
|
+
**The fix:**
|
|
40
|
+
```typescript
|
|
41
|
+
// ✅ GOOD: Test real component or don't mock it
|
|
42
|
+
test('renders sidebar', () => {
|
|
43
|
+
render(<Page />); // Don't mock sidebar
|
|
44
|
+
expect(screen.getByRole('navigation')).toBeInTheDocument();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// OR if sidebar must be mocked for isolation:
|
|
48
|
+
// Don't assert on the mock - test Page's behavior with sidebar present
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Gate Function
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
BEFORE asserting on any mock element:
|
|
55
|
+
Ask: "Am I testing real component behavior or just mock existence?"
|
|
56
|
+
|
|
57
|
+
IF testing mock existence:
|
|
58
|
+
STOP - Delete the assertion or unmock the component
|
|
59
|
+
|
|
60
|
+
Test real behavior instead
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Anti-Pattern 2: Test-Only Methods in Production
|
|
64
|
+
|
|
65
|
+
**The violation:**
|
|
66
|
+
```typescript
|
|
67
|
+
// ❌ BAD: destroy() only used in tests
|
|
68
|
+
class Session {
|
|
69
|
+
async destroy() { // Looks like production API!
|
|
70
|
+
await this._workspaceManager?.destroyWorkspace(this.id);
|
|
71
|
+
// ... cleanup
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// In tests
|
|
76
|
+
afterEach(() => session.destroy());
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**Why this is wrong:**
|
|
80
|
+
- Production class polluted with test-only code
|
|
81
|
+
- Dangerous if accidentally called in production
|
|
82
|
+
- Violates YAGNI and separation of concerns
|
|
83
|
+
- Confuses object lifecycle with entity lifecycle
|
|
84
|
+
|
|
85
|
+
**The fix:**
|
|
86
|
+
```typescript
|
|
87
|
+
// ✅ GOOD: Test utilities handle test cleanup
|
|
88
|
+
// Session has no destroy() - it's stateless in production
|
|
89
|
+
|
|
90
|
+
// In test-utils/
|
|
91
|
+
export async function cleanupSession(session: Session) {
|
|
92
|
+
const workspace = session.getWorkspaceInfo();
|
|
93
|
+
if (workspace) {
|
|
94
|
+
await workspaceManager.destroyWorkspace(workspace.id);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// In tests
|
|
99
|
+
afterEach(() => cleanupSession(session));
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Gate Function
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
BEFORE adding any method to production class:
|
|
106
|
+
Ask: "Is this only used by tests?"
|
|
107
|
+
|
|
108
|
+
IF yes:
|
|
109
|
+
STOP - Don't add it
|
|
110
|
+
Put it in test utilities instead
|
|
111
|
+
|
|
112
|
+
Ask: "Does this class own this resource's lifecycle?"
|
|
113
|
+
|
|
114
|
+
IF no:
|
|
115
|
+
STOP - Wrong class for this method
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Anti-Pattern 3: Mocking Without Understanding
|
|
119
|
+
|
|
120
|
+
**The violation:**
|
|
121
|
+
```typescript
|
|
122
|
+
// ❌ BAD: Mock breaks test logic
|
|
123
|
+
test('detects duplicate server', () => {
|
|
124
|
+
// Mock prevents config write that test depends on!
|
|
125
|
+
vi.mock('ToolCatalog', () => ({
|
|
126
|
+
discoverAndCacheTools: vi.fn().mockResolvedValue(undefined)
|
|
127
|
+
}));
|
|
128
|
+
|
|
129
|
+
await addServer(config);
|
|
130
|
+
await addServer(config); // Should throw - but won't!
|
|
131
|
+
});
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**Why this is wrong:**
|
|
135
|
+
- Mocked method had side effect test depended on (writing config)
|
|
136
|
+
- Over-mocking to "be safe" breaks actual behavior
|
|
137
|
+
- Test passes for wrong reason or fails mysteriously
|
|
138
|
+
|
|
139
|
+
**The fix:**
|
|
140
|
+
```typescript
|
|
141
|
+
// ✅ GOOD: Mock at correct level
|
|
142
|
+
test('detects duplicate server', () => {
|
|
143
|
+
// Mock the slow part, preserve behavior test needs
|
|
144
|
+
vi.mock('MCPServerManager'); // Just mock slow server startup
|
|
145
|
+
|
|
146
|
+
await addServer(config); // Config written
|
|
147
|
+
await addServer(config); // Duplicate detected ✓
|
|
148
|
+
});
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Gate Function
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
BEFORE mocking any method:
|
|
155
|
+
STOP - Don't mock yet
|
|
156
|
+
|
|
157
|
+
1. Ask: "What side effects does the real method have?"
|
|
158
|
+
2. Ask: "Does this test depend on any of those side effects?"
|
|
159
|
+
3. Ask: "Do I fully understand what this test needs?"
|
|
160
|
+
|
|
161
|
+
IF depends on side effects:
|
|
162
|
+
Mock at lower level (the actual slow/external operation)
|
|
163
|
+
OR use test doubles that preserve necessary behavior
|
|
164
|
+
NOT the high-level method the test depends on
|
|
165
|
+
|
|
166
|
+
IF unsure what test depends on:
|
|
167
|
+
Run test with real implementation FIRST
|
|
168
|
+
Observe what actually needs to happen
|
|
169
|
+
THEN add minimal mocking at the right level
|
|
170
|
+
|
|
171
|
+
Red flags:
|
|
172
|
+
- "I'll mock this to be safe"
|
|
173
|
+
- "This might be slow, better mock it"
|
|
174
|
+
- Mocking without understanding the dependency chain
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Anti-Pattern 4: Incomplete Mocks
|
|
178
|
+
|
|
179
|
+
**The violation:**
|
|
180
|
+
```typescript
|
|
181
|
+
// ❌ BAD: Partial mock - only fields you think you need
|
|
182
|
+
const mockResponse = {
|
|
183
|
+
status: 'success',
|
|
184
|
+
data: { userId: '123', name: 'Alice' }
|
|
185
|
+
// Missing: metadata that downstream code uses
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
// Later: breaks when code accesses response.metadata.requestId
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**Why this is wrong:**
|
|
192
|
+
- **Partial mocks hide structural assumptions** - You only mocked fields you know about
|
|
193
|
+
- **Downstream code may depend on fields you didn't include** - Silent failures
|
|
194
|
+
- **Tests pass but integration fails** - Mock incomplete, real API complete
|
|
195
|
+
- **False confidence** - Test proves nothing about real behavior
|
|
196
|
+
|
|
197
|
+
**The Iron Rule:** Mock the COMPLETE data structure as it exists in reality, not just fields your immediate test uses.
|
|
198
|
+
|
|
199
|
+
**The fix:**
|
|
200
|
+
```typescript
|
|
201
|
+
// ✅ GOOD: Mirror real API completeness
|
|
202
|
+
const mockResponse = {
|
|
203
|
+
status: 'success',
|
|
204
|
+
data: { userId: '123', name: 'Alice' },
|
|
205
|
+
metadata: { requestId: 'req-789', timestamp: 1234567890 }
|
|
206
|
+
// All fields real API returns
|
|
207
|
+
};
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Gate Function
|
|
211
|
+
|
|
212
|
+
```
|
|
213
|
+
BEFORE creating mock responses:
|
|
214
|
+
Check: "What fields does the real API response contain?"
|
|
215
|
+
|
|
216
|
+
Actions:
|
|
217
|
+
1. Examine actual API response from docs/examples
|
|
218
|
+
2. Include ALL fields system might consume downstream
|
|
219
|
+
3. Verify mock matches real response schema completely
|
|
220
|
+
|
|
221
|
+
Critical:
|
|
222
|
+
If you're creating a mock, you must understand the ENTIRE structure
|
|
223
|
+
Partial mocks fail silently when code depends on omitted fields
|
|
224
|
+
|
|
225
|
+
If uncertain: Include all documented fields
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Anti-Pattern 5: Integration Tests as Afterthought
|
|
229
|
+
|
|
230
|
+
**The violation:**
|
|
231
|
+
```
|
|
232
|
+
✅ Implementation complete
|
|
233
|
+
❌ No tests written
|
|
234
|
+
"Ready for testing"
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
**Why this is wrong:**
|
|
238
|
+
- Testing is part of implementation, not optional follow-up
|
|
239
|
+
- TDD would have caught this
|
|
240
|
+
- Can't claim complete without tests
|
|
241
|
+
|
|
242
|
+
**The fix:**
|
|
243
|
+
```
|
|
244
|
+
TDD cycle:
|
|
245
|
+
1. Write failing test
|
|
246
|
+
2. Implement to pass
|
|
247
|
+
3. Refactor
|
|
248
|
+
4. THEN claim complete
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## When Mocks Become Too Complex
|
|
252
|
+
|
|
253
|
+
**Warning signs:**
|
|
254
|
+
- Mock setup longer than test logic
|
|
255
|
+
- Mocking everything to make test pass
|
|
256
|
+
- Mocks missing methods real components have
|
|
257
|
+
- Test breaks when mock changes
|
|
258
|
+
|
|
259
|
+
**your human partner's question:** "Do we need to be using a mock here?"
|
|
260
|
+
|
|
261
|
+
**Consider:** Integration tests with real components often simpler than complex mocks
|
|
262
|
+
|
|
263
|
+
## TDD Prevents These Anti-Patterns
|
|
264
|
+
|
|
265
|
+
**Why TDD helps:**
|
|
266
|
+
1. **Write test first** → Forces you to think about what you're actually testing
|
|
267
|
+
2. **Watch it fail** → Confirms test tests real behavior, not mocks
|
|
268
|
+
3. **Minimal implementation** → No test-only methods creep in
|
|
269
|
+
4. **Real dependencies** → You see what the test actually needs before mocking
|
|
270
|
+
|
|
271
|
+
**If you're testing mock behavior, you violated TDD** - you added mocks without watching test fail against real code first.
|
|
272
|
+
|
|
273
|
+
## Quick Reference
|
|
274
|
+
|
|
275
|
+
| Anti-Pattern | Fix |
|
|
276
|
+
|--------------|-----|
|
|
277
|
+
| Assert on mock elements | Test real component or unmock it |
|
|
278
|
+
| Test-only methods in production | Move to test utilities |
|
|
279
|
+
| Mock without understanding | Understand dependencies first, mock minimally |
|
|
280
|
+
| Incomplete mocks | Mirror real API completely |
|
|
281
|
+
| Tests as afterthought | TDD - tests first |
|
|
282
|
+
| Over-complex mocks | Consider integration tests |
|
|
283
|
+
|
|
284
|
+
## Red Flags
|
|
285
|
+
|
|
286
|
+
- Assertion checks for `*-mock` test IDs
|
|
287
|
+
- Methods only called in test files
|
|
288
|
+
- Mock setup is >50% of test
|
|
289
|
+
- Test fails when you remove mock
|
|
290
|
+
- Can't explain why mock is needed
|
|
291
|
+
- Mocking "just to be safe"
|
|
292
|
+
|
|
293
|
+
## The Bottom Line
|
|
294
|
+
|
|
295
|
+
**Mocks are tools to isolate, not things to test.**
|
|
296
|
+
|
|
297
|
+
If TDD reveals you're testing mock behavior, you've gone wrong.
|
|
298
|
+
|
|
299
|
+
Fix: Test real behavior or question why you're mocking at all.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
## 0.2.0
|
|
2
|
+
|
|
3
|
+
- Refactored validator to a pluggable rule-based architecture.
|
|
4
|
+
- Added support for custom rules via `SkillRule`.
|
|
5
|
+
- Added runtime assertion for duplicate rule names.
|
|
6
|
+
- Added warning when a rule emits an error with severity different from its definition.
|
|
7
|
+
- Updated `README.md` with custom rules documentation.
|
|
8
|
+
- **Breaking Change**: Enabling a rule via CLI flag now sets its severity to `error` instead of `warning`.
|
|
9
|
+
|
|
10
|
+
## 0.1.0
|
|
11
|
+
|
|
12
|
+
- Initial version.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# How to Contribute
|
|
2
|
+
|
|
3
|
+
We'd love to accept your patches and contributions to this project. There are
|
|
4
|
+
just a few small guidelines you need to follow.
|
|
5
|
+
|
|
6
|
+
## Contributor License Agreement
|
|
7
|
+
|
|
8
|
+
Contributions to this project must be accompanied by a Contributor License
|
|
9
|
+
Agreement (CLA). You (or your employer) retain the copyright to your
|
|
10
|
+
contribution; this simply gives us permission to use and redistribute your
|
|
11
|
+
contributions as part of the project. Head over to
|
|
12
|
+
<https://cla.developers.google.com/> to see your current agreements on file or
|
|
13
|
+
to sign a new one.
|
|
14
|
+
|
|
15
|
+
You generally only need to submit a CLA once, so if you've already submitted one
|
|
16
|
+
(even if it was for a different project), you probably don't need to do it
|
|
17
|
+
again.
|
|
18
|
+
|
|
19
|
+
## Code Reviews
|
|
20
|
+
|
|
21
|
+
All submissions, including submissions by project members, require review. We
|
|
22
|
+
use GitHub pull requests for this purpose. Consult
|
|
23
|
+
[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
|
|
24
|
+
information on using pull requests.
|
|
25
|
+
|
|
26
|
+
## Coding style
|
|
27
|
+
|
|
28
|
+
The Dart source code in this repo follows the:
|
|
29
|
+
|
|
30
|
+
* [Dart style guide](https://dart.dev/guides/language/effective-dart/style)
|
|
31
|
+
|
|
32
|
+
You should familiarize yourself with those guidelines.
|
|
33
|
+
|
|
34
|
+
## File headers
|
|
35
|
+
|
|
36
|
+
All files in the Dart project must start with the following header; if you add a
|
|
37
|
+
new file please also add this. The year should be a single number stating the
|
|
38
|
+
year the file was created (don't use a range like "2011-2012"). Additionally, if
|
|
39
|
+
you edit an existing file, you shouldn't update the year.
|
|
40
|
+
|
|
41
|
+
// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file
|
|
42
|
+
// for details. All rights reserved. Use of this source code is governed by a
|
|
43
|
+
// BSD-style license that can be found in the LICENSE file.
|
|
44
|
+
|
|
45
|
+
## Community Guidelines
|
|
46
|
+
|
|
47
|
+
This project follows
|
|
48
|
+
[Google's Open Source Community Guidelines](https://opensource.google/conduct/).
|
|
49
|
+
|
|
50
|
+
We pledge to maintain an open and welcoming environment. For details, see our
|
|
51
|
+
[code of conduct](https://dart.dev/code-of-conduct).
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
Copyright 2026, the Dart project authors.
|
|
2
|
+
|
|
3
|
+
Redistribution and use in source and binary forms, with or without
|
|
4
|
+
modification, are permitted provided that the following conditions are
|
|
5
|
+
met:
|
|
6
|
+
|
|
7
|
+
* Redistributions of source code must retain the above copyright
|
|
8
|
+
notice, this list of conditions and the following disclaimer.
|
|
9
|
+
* Redistributions in binary form must reproduce the above
|
|
10
|
+
copyright notice, this list of conditions and the following
|
|
11
|
+
disclaimer in the documentation and/or other materials provided
|
|
12
|
+
with the distribution.
|
|
13
|
+
* Neither the name of Google LLC nor the names of its
|
|
14
|
+
contributors may be used to endorse or promote products derived
|
|
15
|
+
from this software without specific prior written permission.
|
|
16
|
+
|
|
17
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
18
|
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
19
|
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
20
|
+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
21
|
+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
22
|
+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
23
|
+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
24
|
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
25
|
+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
26
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
27
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# dart_skills_lint
|
|
2
|
+
|
|
3
|
+
A static analysis linter for Agent Skills to ensure they meet the specification in presubmit checks. This project is a Dart package and can be run as a CLI tool to validate your skills directory before committing.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
- [Overview](#overview)
|
|
7
|
+
- [Installation](#installation)
|
|
8
|
+
- [Usage](#usage)
|
|
9
|
+
- [Configuration](#configuration)
|
|
10
|
+
- [Specification Validation](#specification-validation)
|
|
11
|
+
- [Best Practices](#best-practices)
|
|
12
|
+
|
|
13
|
+
## Overview
|
|
14
|
+
|
|
15
|
+
An **Agent Skill** is a portable, self-contained directory that extends an AI agent's capabilities. Pre-submit linting ensures that your skill definitions are valid and ready for consumption by agent platforms.
|
|
16
|
+
|
|
17
|
+
`dart_skills_lint` validates:
|
|
18
|
+
- Presence of mandatory `SKILL.md` file.
|
|
19
|
+
- YAML frontmatter constraints (naming, length, etc.).
|
|
20
|
+
- Directory structure (flat, no deep nesting).
|
|
21
|
+
- Relative path integrity.
|
|
22
|
+
|
|
23
|
+
For a full definition of the skill standard, see the [Agent Skills Specification](documentation/knowledge/SPECIFICATION.md).
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
Add `dart_skills_lint` to your Dart project or activate it globally.
|
|
28
|
+
|
|
29
|
+
### 1. As a project dependency
|
|
30
|
+
Add it to your `pubspec.yaml` (once published on pub.dev):
|
|
31
|
+
```yaml
|
|
32
|
+
dev_dependencies:
|
|
33
|
+
dart_skills_lint: ^0.2.0
|
|
34
|
+
```
|
|
35
|
+
Then run:
|
|
36
|
+
```bash
|
|
37
|
+
dart pub get
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 2. Globally activated
|
|
41
|
+
If you want to use it across multiple projects without adding it to each `pubspec.yaml`:
|
|
42
|
+
```bash
|
|
43
|
+
dart pub global activate dart_skills_lint
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Usage
|
|
47
|
+
|
|
48
|
+
There are three ways to interact with `dart_skills_lint`:
|
|
49
|
+
|
|
50
|
+
### 1. As a Command Line Tool with Arguments
|
|
51
|
+
Run the linter against your skills or root skills directories by passing arguments.
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
dart run dart_skills_lint --skills-directory ./path/to/skills-root
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Multiple root directories can be specified:
|
|
58
|
+
```bash
|
|
59
|
+
dart run dart_skills_lint --skills-directory ./path/to/root-a --skills-directory ./path/to/root-b
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Validate Individual Skills directly using `--skill` or `-s`:
|
|
63
|
+
```bash
|
|
64
|
+
dart run dart_skills_lint --skill ./path/to/my-single-skill
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
If no directory is specified, it automatically checks `.claude/skills` and `.agents/skills` relative to your workspace root.
|
|
68
|
+
|
|
69
|
+
### Flags
|
|
70
|
+
- `-d`, `--skills-directory`: Specifies a root directory containing sub-folders of skills to validate. Can be passed multiple times. Can use home tilde expansion (ex: `~/.agents/skills`).
|
|
71
|
+
- `-s`, `--skill`: Specifies an individual skill directory to validate directly. Can be passed multiple times.
|
|
72
|
+
- `-q`, `--quiet`: Hide non-error validation output.
|
|
73
|
+
- `-w`, `--print-warnings`: Enable printing of warning messages.
|
|
74
|
+
- `--fast-fail`: Halt execution immediately on the error.
|
|
75
|
+
- `--ignore-config`: Ignore the YAML configuration file entirely.
|
|
76
|
+
- `--[no-]check-trailing-whitespace`: Enable/disable checking for trailing whitespace. (Disabled by default).
|
|
77
|
+
- `--fix`: Preview fixes for failing lints (dry run).
|
|
78
|
+
- `--fix-apply`: Apply fixes for failing lints.
|
|
79
|
+
|
|
80
|
+
### 2. As a Command Line Tool with a YAML Configuration File
|
|
81
|
+
You can configure the linter using a configuration file (defaulting to `dart_skills_lint.yaml` in the current directory).
|
|
82
|
+
|
|
83
|
+
Create `dart_skills_lint.yaml` in the root of your repository:
|
|
84
|
+
|
|
85
|
+
```yaml
|
|
86
|
+
# dart_skills_lint.yaml
|
|
87
|
+
dart_skills_lint:
|
|
88
|
+
rules:
|
|
89
|
+
check-relative-paths: error
|
|
90
|
+
check-absolute-paths: error
|
|
91
|
+
directories:
|
|
92
|
+
- path: "~/.agents/skills"
|
|
93
|
+
ignore_file: "~/.agents/skills/ignore.json"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Then you can simply run:
|
|
97
|
+
```bash
|
|
98
|
+
dart run dart_skills_lint
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
### 3. As Dart Test Code
|
|
102
|
+
You can integrate the linter into your automated tests by importing the package and calling `validateSkills`. This allows you to enforce skill validity as part of your standard test suite.
|
|
103
|
+
|
|
104
|
+
Example `test/lint_skills_test.dart`:
|
|
105
|
+
```dart
|
|
106
|
+
import 'package:dart_skills_lint/dart_skills_lint.dart';
|
|
107
|
+
import 'package:test/test.dart';
|
|
108
|
+
|
|
109
|
+
void main() {
|
|
110
|
+
test('Run skills linter', () async {
|
|
111
|
+
final config = Configuration(
|
|
112
|
+
directoryConfigs: [
|
|
113
|
+
DirectoryConfig(
|
|
114
|
+
path: '../../skills',
|
|
115
|
+
rules: {},
|
|
116
|
+
ignoreFile: '.agents/skills/flutter_skills_ignore.json',
|
|
117
|
+
),
|
|
118
|
+
],
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
await validateSkills(
|
|
122
|
+
skillDirPaths: ['../../skills'],
|
|
123
|
+
resolvedRules: {
|
|
124
|
+
'check-relative-paths': AnalysisSeverity.error,
|
|
125
|
+
'check-absolute-paths': AnalysisSeverity.error,
|
|
126
|
+
},
|
|
127
|
+
config: config,
|
|
128
|
+
);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
You can also use `Validator` and `ValidationResult` directly if you need to inspect the errors programmatically.
|
|
134
|
+
|
|
135
|
+
### Custom Rules
|
|
136
|
+
|
|
137
|
+
You can author custom rules by extending the `SkillRule` class and passing them to `validateSkills` or the `Validator` constructor.
|
|
138
|
+
|
|
139
|
+
Example custom rule:
|
|
140
|
+
```dart
|
|
141
|
+
import 'package:dart_skills_lint/dart_skills_lint.dart';
|
|
142
|
+
|
|
143
|
+
class MyCustomRule extends SkillRule {
|
|
144
|
+
@override
|
|
145
|
+
final String name = 'my-custom-rule';
|
|
146
|
+
|
|
147
|
+
@override
|
|
148
|
+
final AnalysisSeverity severity = AnalysisSeverity.warning;
|
|
149
|
+
|
|
150
|
+
@override
|
|
151
|
+
Future<List<ValidationError>> validate(SkillContext context) async {
|
|
152
|
+
final errors = <ValidationError>[];
|
|
153
|
+
final yaml = context.parsedYaml;
|
|
154
|
+
if (yaml == null) return errors;
|
|
155
|
+
|
|
156
|
+
if (yaml['metadata']?['deprecated'] == true) {
|
|
157
|
+
errors.add(ValidationError(
|
|
158
|
+
ruleId: name,
|
|
159
|
+
severity: severity,
|
|
160
|
+
file: 'SKILL.md',
|
|
161
|
+
message: 'This skill is marked as deprecated.',
|
|
162
|
+
));
|
|
163
|
+
}
|
|
164
|
+
return errors;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Then use it in your test:
|
|
170
|
+
```dart
|
|
171
|
+
await validateSkills(
|
|
172
|
+
skillDirPaths: ['../../skills'],
|
|
173
|
+
customRules: [MyCustomRule()],
|
|
174
|
+
);
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## Specification Validation
|
|
178
|
+
|
|
179
|
+
The linter checks against the criteria defined in `documentation/knowledge/SPECIFICATION.md` (Section 5.1). Key checks include:
|
|
180
|
+
|
|
181
|
+
### 1. Directory and File Structure
|
|
182
|
+
- Path existence and directory verification.
|
|
183
|
+
- Mandatory `SKILL.md` file at the root.
|
|
184
|
+
- Directories starting with a dot `.` (e.g., `.dart_tool`) are ignored when scanning for skills.
|
|
185
|
+
|
|
186
|
+
### 2. Metadata (YAML Frontmatter)
|
|
187
|
+
- Valid YAML syntax.
|
|
188
|
+
- Allowed fields: `name`, `description`, `license`, `allowed-tools`, `metadata`, `compatibility`, `category`, `tags`, `version`, `eval_task`.
|
|
189
|
+
- Required fields: `name` and `description`.
|
|
190
|
+
|
|
191
|
+
### 3. Field Specific Constraints
|
|
192
|
+
- **Skill Name (`name`)**: Max 64 characters, lowercase alphanumeric and hyphens only, no leading/trailing/consecutive hyphens. **Must match the parent directory name.**
|
|
193
|
+
- **Description (`description`)**: Max 1024 characters.
|
|
194
|
+
- **Compatibility (`compatibility`)**: Max 500 characters.
|
|
195
|
+
|
|
196
|
+
### 4. Content Constraints
|
|
197
|
+
- **Trailing Whitespace**: Lines in `SKILL.md` should not have trailing whitespace. Exactly 2 spaces at the end of a line are allowed to support Markdown hard line breaks, per the [CommonMark Spec](https://spec.commonmark.org/0.31.2/#hard-line-breaks).
|
|
198
|
+
- **Path Constraints**: Checks that **inline** Markdown links do not use absolute paths to enforce portability. Can optionally be configured to check that relative paths point to valid, existing files (disabled by default). *Note: This rule only supports inline Markdown links and does not detect HTML or reference-style links.*
|
|
199
|
+
|
|
200
|
+
## Contributing
|
|
201
|
+
|
|
202
|
+
Contributions are welcome! Please ensure that any PRs pass the linter themselves and align with the `documentation/knowledge/SPECIFICATION.md`.
|
|
203
|
+
|