element.py: Lower expectations when discarding failed builds in non-strict mode

We were trying to compare the potential strong key against the artifact
strong key, but the potential strong key is not guaranteed to be known when
loading the artifact.

For now, we just retry failed builds in non-strict mode as long as the failed
build artifact key is not equal to the element's strict key, as we know that
dependencies *might* have changed since the failed build occurred.
diff --git a/src/buildstream/element.py b/src/buildstream/element.py
index 1d6ea80..ba73b2c 100644
--- a/src/buildstream/element.py
+++ b/src/buildstream/element.py
@@ -1878,9 +1878,9 @@
         # Attempt to pull artifact with the weak cache key
         pulled = pull and artifact.pull(pull_buildtrees=pull_buildtrees)
 
-        # When building in non-strict mode, we ignore a failed artifact unless it has the
-        # expected strong key, this ensures that failed builds will be retried whenever
-        # dependencies have changed.
+        # Automatically retry building failed builds in non-strict mode, because
+        # dependencies may have changed since the last build which might cause this
+        # failed build to succeed.
         #
         # When not building (e.g. `bst show`, `bst artifact push` etc), we do not drop
         # the failed artifact, the retry only occurs at build time.
@@ -1888,12 +1888,19 @@
         if context.build and artifact.cached():
             success, _, _ = artifact.load_build_result()
             if not success:
-
-                # Calculate what the cache key would be for this artifact, if we were going to build it
-                cache_key = self.__calculate_strong_cache_key()
-                assert cache_key is not None
-
-                if artifact.strong_key != cache_key:
+                #
+                # If we could resolve the stong cache key for this element at this time,
+                # we could compare the artifact key against the resolved strong key.
+                #
+                # If we could assert that artifact state is never consulted in advance
+                # of resolving the strong key, then we could discard the loaded artifact
+                # at that time instead.
+                #
+                # Since neither of these are true, we settle for always retrying a failed
+                # build in non-strict mode unless the failed artifact's strong key is
+                # equal to the resolved strict key.
+                #
+                if artifact.strong_key != self.__strict_cache_key:
                     artifact = Artifact(
                         self,
                         context,
@@ -3155,19 +3162,6 @@
 
         self.__build_result = self.__artifact.load_build_result()
 
-    # __calculate_strong_cache_key():
-    #
-    # Convenience function for calculating the strong cache key
-    #
-    # This will return the strong cache key if all of the dependencies have cache
-    # keys available, otherwise it will return None.
-    #
-    def __calculate_strong_cache_key(self):
-        assert self.__weak_cache_key is not None
-
-        dependencies = [[e.project_name, e.name, e._get_cache_key()] for e in self._dependencies(_Scope.BUILD)]
-        return self._calculate_cache_key(dependencies, self.__weak_cache_key)
-
     # __update_cache_keys()
     #
     # Updates weak and strict cache keys
@@ -3276,7 +3270,10 @@
                 self.__cache_key = strong_key
             elif self.__assemble_scheduled or self.__assemble_done:
                 # Artifact will or has been built, not downloaded
-                self.__cache_key = self.__calculate_strong_cache_key()
+                assert self.__weak_cache_key is not None
+
+                dependencies = [[e.project_name, e.name, e._get_cache_key()] for e in self._dependencies(_Scope.BUILD)]
+                self.__cache_key = self._calculate_cache_key(dependencies, self.__weak_cache_key)
 
             if self.__cache_key is None:
                 # Strong cache key could not be calculated yet