From a15d14d4a28ae0fb1c9ec271440dd1d09d420219 Mon Sep 17 00:00:00 2001 From: Reid Baker Date: Thu, 16 Jul 2026 16:29:34 -0400 Subject: [PATCH 1/4] feat: export RuleConfig and RuleConfigPatch in dart_skills_lint.dart --- tool/dart_skills_lint/lib/dart_skills_lint.dart | 1 + tool/dart_skills_lint/test/api_defaults_test.dart | 1 - tool/dart_skills_lint/test/rule_config_test.dart | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/tool/dart_skills_lint/lib/dart_skills_lint.dart b/tool/dart_skills_lint/lib/dart_skills_lint.dart index 90ea086..d49ad6a 100644 --- a/tool/dart_skills_lint/lib/dart_skills_lint.dart +++ b/tool/dart_skills_lint/lib/dart_skills_lint.dart @@ -6,6 +6,7 @@ export 'src/config_parser.dart'; export 'src/entry_point.dart'; export 'src/models/analysis_severity.dart'; export 'src/models/custom_rule_parameters.dart'; +export 'src/models/rule_config.dart'; export 'src/models/skill_context.dart'; export 'src/models/skill_rule.dart'; export 'src/models/validation_error.dart'; diff --git a/tool/dart_skills_lint/test/api_defaults_test.dart b/tool/dart_skills_lint/test/api_defaults_test.dart index e7ba2bf..3401ac1 100644 --- a/tool/dart_skills_lint/test/api_defaults_test.dart +++ b/tool/dart_skills_lint/test/api_defaults_test.dart @@ -4,7 +4,6 @@ import 'dart:io'; import 'package:dart_skills_lint/dart_skills_lint.dart'; -import 'package:dart_skills_lint/src/models/rule_config.dart'; import 'package:dart_skills_lint/src/rules/description_length_rule.dart'; import 'package:dart_skills_lint/src/rules/trailing_whitespace_rule.dart'; import 'package:dart_skills_lint/src/rules/valid_yaml_metadata_rule.dart'; diff --git a/tool/dart_skills_lint/test/rule_config_test.dart b/tool/dart_skills_lint/test/rule_config_test.dart index 808058d..362de9c 100644 --- a/tool/dart_skills_lint/test/rule_config_test.dart +++ b/tool/dart_skills_lint/test/rule_config_test.dart @@ -5,7 +5,6 @@ import 'dart:io'; import 'package:dart_skills_lint/dart_skills_lint.dart'; -import 'package:dart_skills_lint/src/models/rule_config.dart'; import 'package:test/test.dart'; import 'test_utils.dart'; From f9bda2072369a3b4781b4571a473248d0ab9dda1 Mon Sep 17 00:00:00 2001 From: Reid Baker Date: Thu, 16 Jul 2026 17:31:51 -0400 Subject: [PATCH 2/4] docs: enforce diagnostic consumer migration step right inside check-downstream-consumers skill --- .../skills/check-downstream-consumers/SKILL.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tool/dart_skills_lint/.agents/skills/check-downstream-consumers/SKILL.md b/tool/dart_skills_lint/.agents/skills/check-downstream-consumers/SKILL.md index 752c522..aaa359b 100644 --- a/tool/dart_skills_lint/.agents/skills/check-downstream-consumers/SKILL.md +++ b/tool/dart_skills_lint/.agents/skills/check-downstream-consumers/SKILL.md @@ -46,9 +46,18 @@ For each downstream consumer under evaluation: ref: ``` -2. **Resolve Dependencies & Run Verification Tests** +2. **Resolve Dependencies & Run Verification Tests (Legacy Check)** - Execute dependency resolution according to the consumer environment (Flutter workspaces require `flutter pub get`; standard pure Dart repositories require `dart pub get`). - - Run the consumer's verification tests (typically targeting tests like `test/validate_skills_test.dart` or running `flutter test` / `dart test`). + - Run the consumer's verification tests right against their existing code (typically targeting tests like `test/validate_skills_test.dart` or running `flutter test` / `dart test`). + - Confirm that all existing tests and static analyses compile and pass without disruptions right when using their established calling syntax. This ensures backward-compatibility deprecation shims work correctly right as they did before. + +3. **Perform Diagnostic API Migration & Boundary Verification** + After verifying right across each target consumer that deprecated legacy calls function properly without regressions right across Step 2: + - **Migrate Consumer Calling Syntax**: For every repository across the set of downstream targets right under evaluation (whether a single target, a requested subset, or all known consumers), update its codebase right right off any deprecated getters, parameters, or constructors directly right onto the new replacement API surface introduced right across `dart_skills_lint` (e.g., transitioning `resolvedRules` arguments across to `resolvedRuleConfigs`). + - **Verify Public Boundary Resolution**: Execute strict static analysis (`dart analyze --fatal-infos `) directly across the consumer's package directory right right right right after completing the migration. + - Confirm all new classes and parameters resolve cleanly completely through the public library barrier (`import 'package:dart_skills_lint/dart_skills_lint.dart';`). + - Any syntax reporting `Undefined class` right or requiring implementation imports (`import 'package:dart_skills_lint/src/...';`) to resolve indicates an explicit **public export deficit** inside `lib/dart_skills_lint.dart`. + - **Run Migrated Test Suite**: Re-run the full downstream consumer test harness across the newly migrated calling structure (`flutter test` / `dart test`) to guarantee behavioral correctness right before accepting the upstream change. --- From b134690bd580e20c7644f898a5dd5d7aa2807617 Mon Sep 17 00:00:00 2001 From: Reid Baker Date: Thu, 16 Jul 2026 18:20:11 -0400 Subject: [PATCH 3/4] refactor: relocate examples, add API boundary runner and GHA validation config --- .../workflows/dart_skills_lint_workflow.yaml | 8 +++ .../check-downstream-consumers/SKILL.md | 16 +++--- tool/dart_skills_lint/dart_skills_lint.yaml | 4 +- tool/dart_skills_lint/example/README.md | 14 ++--- .../example/api_boundary_runner/bin/main.dart | 56 +++++++++++++++++++ .../example/api_boundary_runner/pubspec.yaml | 9 +++ .../example/{ => skills}/invalid/SKILL.md | 4 +- .../example/{ => skills}/valid/SKILL.md | 2 +- .../test/example_fixtures_test.dart | 4 +- .../test/recipe_drift_test.dart | 4 +- 10 files changed, 97 insertions(+), 24 deletions(-) create mode 100644 tool/dart_skills_lint/example/api_boundary_runner/bin/main.dart create mode 100644 tool/dart_skills_lint/example/api_boundary_runner/pubspec.yaml rename tool/dart_skills_lint/example/{ => skills}/invalid/SKILL.md (91%) rename tool/dart_skills_lint/example/{ => skills}/valid/SKILL.md (90%) diff --git a/.github/workflows/dart_skills_lint_workflow.yaml b/.github/workflows/dart_skills_lint_workflow.yaml index 63642f9..5cd74d0 100644 --- a/.github/workflows/dart_skills_lint_workflow.yaml +++ b/.github/workflows/dart_skills_lint_workflow.yaml @@ -42,6 +42,14 @@ jobs: - run: dart test + - name: Verify API boundary runner example + run: | + cd example/api_boundary_runner + dart pub get + dart format --output=none --set-exit-if-changed . + dart analyze --fatal-infos . + dart run bin/main.dart + coverage: runs-on: ubuntu-latest steps: diff --git a/tool/dart_skills_lint/.agents/skills/check-downstream-consumers/SKILL.md b/tool/dart_skills_lint/.agents/skills/check-downstream-consumers/SKILL.md index aaa359b..78584da 100644 --- a/tool/dart_skills_lint/.agents/skills/check-downstream-consumers/SKILL.md +++ b/tool/dart_skills_lint/.agents/skills/check-downstream-consumers/SKILL.md @@ -48,16 +48,16 @@ For each downstream consumer under evaluation: 2. **Resolve Dependencies & Run Verification Tests (Legacy Check)** - Execute dependency resolution according to the consumer environment (Flutter workspaces require `flutter pub get`; standard pure Dart repositories require `dart pub get`). - - Run the consumer's verification tests right against their existing code (typically targeting tests like `test/validate_skills_test.dart` or running `flutter test` / `dart test`). - - Confirm that all existing tests and static analyses compile and pass without disruptions right when using their established calling syntax. This ensures backward-compatibility deprecation shims work correctly right as they did before. + - Run the consumer's verification tests against their existing code (typically targeting tests like `test/validate_skills_test.dart` or running `flutter test` / `dart test`). + - Confirm that all existing tests and static analyses compile and pass cleanly when using their established calling syntax. This ensures backward-compatibility deprecation shims function properly right alongside legacy calling conventions. 3. **Perform Diagnostic API Migration & Boundary Verification** - After verifying right across each target consumer that deprecated legacy calls function properly without regressions right across Step 2: - - **Migrate Consumer Calling Syntax**: For every repository across the set of downstream targets right under evaluation (whether a single target, a requested subset, or all known consumers), update its codebase right right off any deprecated getters, parameters, or constructors directly right onto the new replacement API surface introduced right across `dart_skills_lint` (e.g., transitioning `resolvedRules` arguments across to `resolvedRuleConfigs`). - - **Verify Public Boundary Resolution**: Execute strict static analysis (`dart analyze --fatal-infos `) directly across the consumer's package directory right right right right after completing the migration. - - Confirm all new classes and parameters resolve cleanly completely through the public library barrier (`import 'package:dart_skills_lint/dart_skills_lint.dart';`). - - Any syntax reporting `Undefined class` right or requiring implementation imports (`import 'package:dart_skills_lint/src/...';`) to resolve indicates an explicit **public export deficit** inside `lib/dart_skills_lint.dart`. - - **Run Migrated Test Suite**: Re-run the full downstream consumer test harness across the newly migrated calling structure (`flutter test` / `dart test`) to guarantee behavioral correctness right before accepting the upstream change. + After verifying across each target consumer that legacy calls function properly without regressions in Step 2: + - **Migrate Consumer Calling Syntax**: For every repository in the set of downstream targets under evaluation (whether a single target, a requested subset, or all known consumers), update its codebase to remove any usage of deprecated getters, parameters, or constructors directly, replacing them with the new API surface introduced in `dart_skills_lint` (for example, transitioning `resolvedRules` arguments to `resolvedRuleConfigs`). + - **Verify Public Boundary Resolution**: Execute strict static analysis (`dart analyze --fatal-infos `) within the consumer's package directory after completing the migration. + - Confirm that all newly exposed classes and parameters resolve cleanly through the public library barrier (`import 'package:dart_skills_lint/dart_skills_lint.dart';`). + - Any syntax check reporting `Undefined class` or requiring internal implementation imports (`import 'package:dart_skills_lint/src/...';`) to compile indicates an explicit **public export deficit** inside `lib/dart_skills_lint.dart`. + - **Run Migrated Test Suite**: Re-run the complete downstream consumer test harness against the migrated code (`flutter test` / `dart test`) to guarantee exact behavioral alignment before accepting the upstream change. --- diff --git a/tool/dart_skills_lint/dart_skills_lint.yaml b/tool/dart_skills_lint/dart_skills_lint.yaml index eeddd51..cd6926c 100644 --- a/tool/dart_skills_lint/dart_skills_lint.yaml +++ b/tool/dart_skills_lint/dart_skills_lint.yaml @@ -16,10 +16,10 @@ dart_skills_lint: rules: check-trailing-whitespace: error prevent-skills-sh-publishing: error - - path: "example/invalid" + - path: "example/skills/invalid" rules: prevent-skills-sh-publishing: error - - path: "example/valid" + - path: "example/skills/valid" rules: prevent-skills-sh-publishing: error individual_skills: diff --git a/tool/dart_skills_lint/example/README.md b/tool/dart_skills_lint/example/README.md index 9e45ce5..13a2260 100644 --- a/tool/dart_skills_lint/example/README.md +++ b/tool/dart_skills_lint/example/README.md @@ -4,8 +4,8 @@ Two reference fixtures live in this directory: | Fixture | Expected outcome | | --- | --- | -| [`valid/`](valid/SKILL.md) | All rules pass; the CLI exits 0. | -| [`invalid/`](invalid/SKILL.md) | Multiple rules fail; the CLI exits 1. | +| [`valid/`](skills/valid/SKILL.md) | All rules pass; the CLI exits 0. | +| [`invalid/`](skills/invalid/SKILL.md) | Multiple rules fail; the CLI exits 1. | Use them to take the linter for a spin without writing your own skill first, and to see exactly what real diagnostic output looks like. @@ -13,13 +13,13 @@ first, and to see exactly what real diagnostic output looks like. ## Run the valid fixture ```bash -dart run dart_skills_lint --skill ./example/valid +dart run dart_skills_lint --skill ./example/skills/valid ``` You should see: ``` -Evaluating directory: example/valid +Evaluating directory: example/skills/valid --- Validating skill: valid --- Skill is valid. ``` @@ -32,14 +32,14 @@ With default rule severities, only `invalid-skill-name` fires (the other two violations are below their default threshold): ```bash -dart run dart_skills_lint --skill ./example/invalid +dart run dart_skills_lint --skill ./example/skills/invalid ``` Exit code: `1`. To see every violation surface as an error, escalate the other two rules with explicit flags: ```bash -dart run dart_skills_lint --skill ./example/invalid \ +dart run dart_skills_lint --skill ./example/skills/invalid \ --disallowed-field --check-absolute-paths ``` @@ -63,7 +63,7 @@ when the target file exists. To experiment, point it at a real local file: ```bash -dart run dart_skills_lint --skill ./example/invalid --fix --dry-run +dart run dart_skills_lint --skill ./example/skills/invalid --fix --dry-run ``` `--dry-run` shows the proposed diff without writing; drop it to apply diff --git a/tool/dart_skills_lint/example/api_boundary_runner/bin/main.dart b/tool/dart_skills_lint/example/api_boundary_runner/bin/main.dart new file mode 100644 index 0000000..6ec010c --- /dev/null +++ b/tool/dart_skills_lint/example/api_boundary_runner/bin/main.dart @@ -0,0 +1,56 @@ +// Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// ignore_for_file: avoid_print + +import 'dart:io'; + +import 'package:dart_skills_lint/dart_skills_lint.dart'; +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as p; + +Future main(List args) async { + Logger.root.level = Level.ALL; + Logger.root.onRecord.listen((record) => print(record.message)); + + print('Running API boundary validation runner...'); + + final String scriptDir = p.dirname(Platform.script.toFilePath()); + final String validSkillPath = p.normalize( + p.absolute(p.join(scriptDir, '..', '..', 'skills', 'valid')), + ); + final String invalidSkillPath = p.normalize( + p.absolute(p.join(scriptDir, '..', '..', 'skills', 'invalid')), + ); + + print('Validating valid skill at: $validSkillPath'); + final bool validResult = await validateSkills( + individualSkillPaths: [validSkillPath], + resolvedRuleConfigs: { + 'check-absolute-paths': const RuleConfigPatch(severity: AnalysisSeverity.disabled), + }, + ); + + if (!validResult) { + print('Error: Valid skill fixture failed validation!'); + exit(1); + } + print('Success: Valid skill fixture validated cleanly.'); + + print('Validating invalid skill at: $invalidSkillPath'); + // Since this is invalid, we expect it to fail under standard rules. + final bool invalidResult = await validateSkills( + individualSkillPaths: [invalidSkillPath], + printWarnings: false, + quiet: true, + ); + + if (invalidResult) { + print('Error: Invalid skill fixture unexpectedly passed validation!'); + exit(1); + } + print('Success: Invalid skill fixture failed validation as expected.'); + + print('API boundary verification completed successfully.'); +} diff --git a/tool/dart_skills_lint/example/api_boundary_runner/pubspec.yaml b/tool/dart_skills_lint/example/api_boundary_runner/pubspec.yaml new file mode 100644 index 0000000..601d1dc --- /dev/null +++ b/tool/dart_skills_lint/example/api_boundary_runner/pubspec.yaml @@ -0,0 +1,9 @@ +name: api_boundary_runner +description: An example code runner that validates the public API boundary. +environment: + sdk: ^3.11.0-0 +dependencies: + path: ^1.9.0 + logging: ^1.2.0 + dart_skills_lint: + path: ../../ diff --git a/tool/dart_skills_lint/example/invalid/SKILL.md b/tool/dart_skills_lint/example/skills/invalid/SKILL.md similarity index 91% rename from tool/dart_skills_lint/example/invalid/SKILL.md rename to tool/dart_skills_lint/example/skills/invalid/SKILL.md index f71b2af..583366b 100644 --- a/tool/dart_skills_lint/example/invalid/SKILL.md +++ b/tool/dart_skills_lint/example/skills/invalid/SKILL.md @@ -27,13 +27,13 @@ The broken link: [absolute link](/tmp/this/does/not/exist.md) Run it with default rules: ```bash -dart run dart_skills_lint --skill ./example/invalid +dart run dart_skills_lint --skill ./example/skills/invalid ``` …and again with every rule turned up to error: ```bash -dart run dart_skills_lint --skill ./example/invalid \ +dart run dart_skills_lint --skill ./example/skills/invalid \ --disallowed-field --check-absolute-paths ``` diff --git a/tool/dart_skills_lint/example/valid/SKILL.md b/tool/dart_skills_lint/example/skills/valid/SKILL.md similarity index 90% rename from tool/dart_skills_lint/example/valid/SKILL.md rename to tool/dart_skills_lint/example/skills/valid/SKILL.md index 2997f9d..5846630 100644 --- a/tool/dart_skills_lint/example/valid/SKILL.md +++ b/tool/dart_skills_lint/example/skills/valid/SKILL.md @@ -17,7 +17,7 @@ against. It deliberately does nothing useful — it's documentation. Run it with: ```bash -dart run dart_skills_lint --skill ./example/valid +dart run dart_skills_lint --skill ./example/skills/valid ``` Expected output: `Skill is valid.` and exit code 0. diff --git a/tool/dart_skills_lint/test/example_fixtures_test.dart b/tool/dart_skills_lint/test/example_fixtures_test.dart index 43a4af6..dd91fe2 100644 --- a/tool/dart_skills_lint/test/example_fixtures_test.dart +++ b/tool/dart_skills_lint/test/example_fixtures_test.dart @@ -22,8 +22,8 @@ import 'package:test_process/test_process.dart'; void main() { group('example fixtures', () { final String cliPath = p.normalize(p.absolute('bin/cli.dart')); - final String validPath = p.normalize(p.absolute('example/valid')); - final String invalidPath = p.normalize(p.absolute('example/invalid')); + final String validPath = p.normalize(p.absolute('example/skills/valid')); + final String invalidPath = p.normalize(p.absolute('example/skills/invalid')); test('example/valid passes with default rules', () async { final TestProcess process = await TestProcess.start('dart', [cliPath, '--skill', validPath]); diff --git a/tool/dart_skills_lint/test/recipe_drift_test.dart b/tool/dart_skills_lint/test/recipe_drift_test.dart index 81f4a9c..098492b 100644 --- a/tool/dart_skills_lint/test/recipe_drift_test.dart +++ b/tool/dart_skills_lint/test/recipe_drift_test.dart @@ -30,8 +30,8 @@ void main() { group('README Recipes drift', () { late _RecipeReader reader; final String cliPath = p.normalize(p.absolute('bin/cli.dart')); - final String validFixture = p.normalize(p.absolute('example/valid')); - final String invalidFixture = p.normalize(p.absolute('example/invalid')); + final String validFixture = p.normalize(p.absolute('example/skills/valid')); + final String invalidFixture = p.normalize(p.absolute('example/skills/invalid')); setUpAll(() { reader = _RecipeReader.fromFile(p.normalize(p.absolute('README.md'))); From 3d375fed1b5f6bfe2881a601a4b6d91588594ff6 Mon Sep 17 00:00:00 2001 From: Reid Baker Date: Fri, 17 Jul 2026 11:08:16 -0400 Subject: [PATCH 4/4] refactor: apply PR code review suggestions on API boundary path resolution and VM clean exit rules --- .../example/api_boundary_runner/bin/main.dart | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/tool/dart_skills_lint/example/api_boundary_runner/bin/main.dart b/tool/dart_skills_lint/example/api_boundary_runner/bin/main.dart index 6ec010c..6c54d40 100644 --- a/tool/dart_skills_lint/example/api_boundary_runner/bin/main.dart +++ b/tool/dart_skills_lint/example/api_boundary_runner/bin/main.dart @@ -16,13 +16,24 @@ Future main(List args) async { print('Running API boundary validation runner...'); - final String scriptDir = p.dirname(Platform.script.toFilePath()); - final String validSkillPath = p.normalize( - p.absolute(p.join(scriptDir, '..', '..', 'skills', 'valid')), - ); - final String invalidSkillPath = p.normalize( - p.absolute(p.join(scriptDir, '..', '..', 'skills', 'invalid')), - ); + String findPath(String relativeSuffix) { + final pathsToTry = [ + p.join('example', 'skills', relativeSuffix), + p.join('..', 'skills', relativeSuffix), + if (Platform.script.scheme == 'file') + p.join(p.dirname(Platform.script.toFilePath()), '..', '..', 'skills', relativeSuffix), + ]; + for (final path in pathsToTry) { + final String absolutePath = p.absolute(path); + if (Directory(absolutePath).existsSync()) { + return p.normalize(absolutePath); + } + } + throw StateError('Could not locate skills/$relativeSuffix directory.'); + } + + final String validSkillPath = findPath('valid'); + final String invalidSkillPath = findPath('invalid'); print('Validating valid skill at: $validSkillPath'); final bool validResult = await validateSkills( @@ -34,7 +45,8 @@ Future main(List args) async { if (!validResult) { print('Error: Valid skill fixture failed validation!'); - exit(1); + exitCode = 1; + return; } print('Success: Valid skill fixture validated cleanly.'); @@ -48,7 +60,8 @@ Future main(List args) async { if (invalidResult) { print('Error: Invalid skill fixture unexpectedly passed validation!'); - exit(1); + exitCode = 1; + return; } print('Success: Invalid skill fixture failed validation as expected.');