Document why some lints aren't enabled and fix some minor issues. (#91527)

diff --git a/analysis_options.yaml b/analysis_options.yaml
index f34fbe3..cd428eb 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -27,13 +27,12 @@
     missing_required_param: warning
     # treat missing returns as a warning (not a hint)
     missing_return: warning
-    # allow having TODO/HACK comments in the code
+    # allow having TODO comments in the code
     todo: ignore
-    hack: ignore
     # allow self-reference to deprecated members (we do this because otherwise we have
     # to annotate every member in every test, assert, etc, when we deprecate something)
     deprecated_member_use_from_same_package: ignore
-    # TODO(https://github.com/flutter/flutter/issues/74381):
+    # TODO(ianh): https://github.com/flutter/flutter/issues/74381
     # Clean up existing unnecessary imports, and remove line to ignore.
     unnecessary_import: ignore
     # Turned off until null-safe rollout is complete.
@@ -92,12 +91,12 @@
     - avoid_unnecessary_containers
     - avoid_unused_constructor_parameters
     - avoid_void_async
-    # - avoid_web_libraries_in_flutter # not yet tested
+    # - avoid_web_libraries_in_flutter # we use web libraries in web-specific code, and our tests prevent us from using them elsewhere
     - await_only_futures
     - camel_case_extensions
     - camel_case_types
     - cancel_subscriptions
-    # - cascade_invocations # not yet tested
+    # - cascade_invocations # doesn't match the typical style of this repo
     - cast_nullable_to_non_nullable
     # - close_sinks # not reliable enough
     # - comment_references # blocked on https://github.com/dart-lang/linter/issues/1142
@@ -105,9 +104,9 @@
     - control_flow_in_finally
     # - curly_braces_in_flow_control_structures # not required by flutter style
     - deprecated_consistency
-    # - diagnostic_describe_all_properties # not yet tested
+    # - diagnostic_describe_all_properties # enabled only at the framework level (packages/flutter/lib)
     - directives_ordering
-    # - do_not_use_environment # we do this commonly
+    # - do_not_use_environment # there are appropriate times to use the environment, especially in our tests and build logic
     - empty_catches
     - empty_constructor_bodies
     - empty_statements
@@ -125,7 +124,7 @@
     - library_private_types_in_public_api
     # - lines_longer_than_80_chars # not required by flutter style
     - list_remove_unrelated_type
-    # - literal_only_boolean_expressions # too many false positives: https://github.com/dart-lang/sdk/issues/34181
+    # - literal_only_boolean_expressions # too many false positives: https://github.com/dart-lang/linter/issues/453
     - missing_whitespace_between_adjacent_strings
     - no_adjacent_strings_in_list
     # - no_default_cases # too many false positives
diff --git a/dev/devicelab/lib/framework/utils.dart b/dev/devicelab/lib/framework/utils.dart
index ea2856d..a38a740 100644
--- a/dev/devicelab/lib/framework/utils.dart
+++ b/dev/devicelab/lib/framework/utils.dart
@@ -21,22 +21,14 @@
 
 /// The local engine to use for [flutter] and [evalFlutter], if any.
 String? get localEngine {
-  // Use two distinct `defaultValue`s to determine whether a 'localEngine'
-  // declaration exists in the environment.
-  const bool isDefined =
-      String.fromEnvironment('localEngine', defaultValue: 'a') ==
-          String.fromEnvironment('localEngine', defaultValue: 'b');
+  const bool isDefined = bool.hasEnvironment('localEngine');
   return isDefined ? const String.fromEnvironment('localEngine') : null;
 }
 
 /// The local engine source path to use if a local engine is used for [flutter]
 /// and [evalFlutter].
 String? get localEngineSrcPath {
-  // Use two distinct `defaultValue`s to determine whether a
-  // 'localEngineSrcPath' declaration exists in the environment.
-  const bool isDefined =
-      String.fromEnvironment('localEngineSrcPath', defaultValue: 'a') ==
-          String.fromEnvironment('localEngineSrcPath', defaultValue: 'b');
+  const bool isDefined = bool.hasEnvironment('localEngineSrcPath');
   return isDefined ? const String.fromEnvironment('localEngineSrcPath') : null;
 }
 
diff --git a/packages/flutter/lib/analysis_options.yaml b/packages/flutter/lib/analysis_options.yaml
new file mode 100644
index 0000000..dbb6737
--- /dev/null
+++ b/packages/flutter/lib/analysis_options.yaml
@@ -0,0 +1,5 @@
+include: ../analysis_options.yaml
+
+linter:
+  rules:
+    # - diagnostic_describe_all_properties # blocked on https://github.com/dart-lang/sdk/issues/47418
diff --git a/packages/flutter_driver/lib/src/driver/driver.dart b/packages/flutter_driver/lib/src/driver/driver.dart
index 3f746b9..3f87dd9 100644
--- a/packages/flutter_driver/lib/src/driver/driver.dart
+++ b/packages/flutter_driver/lib/src/driver/driver.dart
@@ -556,53 +556,55 @@
   ///
   /// The image will be returned as a PNG.
   ///
-  ///  HACK: There will be a 2-second artificial delay before screenshotting,
-  ///        the delay here is to deal with a race between the driver script and
-  ///        the raster thread (formerly known as the GPU thread). The issue is
-  ///        that driver API synchronizes with the framework based on transient
-  ///        callbacks, which are out of sync with the raster thread.
-  ///        Here's the timeline of events in ASCII art:
+  /// **Warning:** This is not reliable.
   ///
-  ///        -------------------------------------------------------------------
-  ///        Without this delay:
-  ///        -------------------------------------------------------------------
-  ///        UI    : <-- build -->
-  ///        Raster:               <-- rasterize -->
-  ///        Gap   :              | random |
-  ///        Driver:                        <-- screenshot -->
+  /// There is a two-second artificial delay before screenshotting. The delay
+  /// here is to deal with a race between the driver script and the raster
+  /// thread (formerly known as the GPU thread). The issue is that the driver
+  /// API synchronizes with the framework based on transient callbacks, which
+  /// are out of sync with the raster thread.
   ///
-  ///        In the diagram above, the gap is the time between the last driver
-  ///        action taken, such as a `tap()`, and the subsequent call to
-  ///        `screenshot()`. The gap is random because it is determined by the
-  ///        unpredictable network communication between the driver process and
-  ///        the application. If this gap is too short, which it typically will
-  ///        be, the screenshot is taken before the raster thread is done
-  ///        rasterizing the frame, so the screenshot of the previous frame is
-  ///        taken, which is wrong.
+  /// Here's the timeline of events in ASCII art:
   ///
-  ///        -------------------------------------------------------------------
-  ///        With this delay, if we're lucky:
-  ///        -------------------------------------------------------------------
-  ///        UI    : <-- build -->
-  ///        Raster:               <-- rasterize -->
-  ///        Gap   :              |    2 seconds or more   |
-  ///        Driver:                                        <-- screenshot -->
+  ///     ---------------------------------------------------------------
+  ///     Without this delay:
+  ///     ---------------------------------------------------------------
+  ///     UI    : <-- build -->
+  ///     Raster:               <-- rasterize -->
+  ///     Gap   :              | random |
+  ///     Driver:                        <-- screenshot -->
   ///
-  ///        The two-second gap should be long enough for the raster thread to
-  ///        finish rasterizing the frame, but not longer than necessary to keep
-  ///        driver tests as fast a possible.
+  /// In the diagram above, the gap is the time between the last driver action
+  /// taken, such as a `tap()`, and the subsequent call to `screenshot()`. The
+  /// gap is random because it is determined by the unpredictable communication
+  /// channel between the driver process and the application. If this gap is too
+  /// short, which it typically will be, the screenshot is taken before the
+  /// raster thread is done rasterizing the frame, so the screenshot of the
+  /// previous frame is taken, which is not what is intended.
   ///
-  ///        -------------------------------------------------------------------
-  ///        With this delay, if we're not lucky:
-  ///        -------------------------------------------------------------------
-  ///        UI    : <-- build -->
-  ///        Raster:               <-- rasterize randomly slow today -->
-  ///        Gap   :              |    2 seconds or more   |
-  ///        Driver:                                        <-- screenshot -->
+  ///     ---------------------------------------------------------------
+  ///     With this delay, if we're lucky:
+  ///     ---------------------------------------------------------------
+  ///     UI    : <-- build -->
+  ///     Raster:               <-- rasterize -->
+  ///     Gap   :              |    2 seconds or more   |
+  ///     Driver:                                        <-- screenshot -->
   ///
-  ///        In practice, sometimes the device gets really busy for a while and
-  ///        even two seconds isn't enough, which means that this is still racy
-  ///        and a source of flakes.
+  /// The two-second gap should be long enough for the raster thread to finish
+  /// rasterizing the frame, but not longer than necessary to keep driver tests
+  /// as fast a possible.
+  ///
+  ///     ---------------------------------------------------------------
+  ///     With this delay, if we're not lucky:
+  ///     ---------------------------------------------------------------
+  ///     UI    : <-- build -->
+  ///     Raster:               <-- rasterize randomly slow today -->
+  ///     Gap   :              |    2 seconds or more   |
+  ///     Driver:                                        <-- screenshot -->
+  ///
+  /// In practice, sometimes the device gets really busy for a while and even
+  /// two seconds isn't enough, which means that this is still racy and a source
+  /// of flakes.
   Future<List<int>> screenshot() async {
     throw UnimplementedError();
   }
diff --git a/packages/flutter_tools/lib/src/commands/packages.dart b/packages/flutter_tools/lib/src/commands/packages.dart
index bb50782..6258817 100644
--- a/packages/flutter_tools/lib/src/commands/packages.dart
+++ b/packages/flutter_tools/lib/src/commands/packages.dart
@@ -305,10 +305,11 @@
     List<String> rest = argResults.rest;
     final bool isHelp = rest.contains('-h') || rest.contains('--help');
     String target;
-    if (rest.length == 1 && (rest[0].contains('/') || rest[0].contains(r'\'))) {
-      // HACK: Supporting flutter specific behavior where you can pass a
-      //       folder to the command.
-      target = findProjectRoot(globals.fs, rest[0]);
+    if (rest.length == 1 && (rest.single.contains('/') || rest.single.contains(r'\'))) {
+      // For historical reasons, if there is one argument to the command and it contains
+      // a multiple-component path (i.e. contains a slash) then we use that to determine
+      // to which project we're applying the command.
+      target = findProjectRoot(globals.fs, rest.single);
       rest = <String>[];
     } else {
       target = findProjectRoot(globals.fs);