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 752c522..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 @@ -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 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 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..6c54d40 --- /dev/null +++ b/tool/dart_skills_lint/example/api_boundary_runner/bin/main.dart @@ -0,0 +1,69 @@ +// 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...'); + + 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( + individualSkillPaths: [validSkillPath], + resolvedRuleConfigs: { + 'check-absolute-paths': const RuleConfigPatch(severity: AnalysisSeverity.disabled), + }, + ); + + if (!validResult) { + print('Error: Valid skill fixture failed validation!'); + exitCode = 1; + return; + } + 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!'); + exitCode = 1; + return; + } + 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')));