Merge "Add framework-connectivity-tiramisu to PRODUCT_APEX_BOOT_JARS"
diff --git a/core/Makefile b/core/Makefile
index a580ac8..c45fc15 100644
--- a/core/Makefile
+++ b/core/Makefile
@@ -527,16 +527,6 @@
     $(eval ALL_DEFAULT_INSTALLED_MODULES += $(call build-image-kernel-modules-dir,GENERIC_RAMDISK,$(TARGET_RAMDISK_OUT),,modules.load,,$(kmd)))))
 
 # -----------------------------------------------------------------
-# FSVerity metadata generation
-ifeq ($(PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA),true)
-
-FSVERITY_APK_KEY_PATH := $(DEFAULT_SYSTEM_DEV_CERTIFICATE)
-FSVERITY_APK_OUT := system/etc/security/fsverity/BuildManifest.apk
-FSVERITY_APK_MANIFEST_PATH := system/security/fsverity/AndroidManifest.xml
-
-endif # PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA
-
-# -----------------------------------------------------------------
 # Cert-to-package mapping.  Used by the post-build signing tools.
 # Use a macro to add newline to each echo command
 # $1 stem name of the package
@@ -784,6 +774,7 @@
 $(INSTALLED_FILES_FILE_ROOT): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_ROOT)
 $(INSTALLED_FILES_FILE_ROOT) : $(INTERNAL_ROOT_FILES) $(FILESLIST) $(FILESLIST_UTIL)
 	@echo Installed file list: $@
+	mkdir -p $(TARGET_ROOT_OUT)
 	mkdir -p $(dir $@)
 	rm -f $@
 	$(FILESLIST) $(TARGET_ROOT_OUT) > $(@:.txt=.json)
@@ -822,7 +813,7 @@
 	$(FILESLIST_UTIL) -c $(@:.txt=.json) > $@
 
 ifeq ($(HOST_OS),linux)
-$(call dist-for-goals, sdk win_sdk sdk_addon, $(INSTALLED_FILES_FILE_RAMDISK))
+$(call dist-for-goals, sdk sdk_addon, $(INSTALLED_FILES_FILE_RAMDISK))
 endif
 BUILT_RAMDISK_TARGET := $(PRODUCT_OUT)/ramdisk.img
 
@@ -908,8 +899,16 @@
 INTERNAL_BOOTIMAGE_ARGS := \
 	$(addprefix --second ,$(INSTALLED_2NDBOOTLOADER_TARGET))
 
+INTERNAL_INIT_BOOT_IMAGE_ARGS :=
+
+INTERNAL_BOOT_HAS_RAMDISK :=
 ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true)
-INTERNAL_BOOTIMAGE_ARGS += --ramdisk $(INSTALLED_RAMDISK_TARGET)
+  ifneq ($(BUILDING_INIT_BOOT_IMAGE),true)
+    INTERNAL_BOOTIMAGE_ARGS += --ramdisk $(INSTALLED_RAMDISK_TARGET)
+    INTERNAL_BOOT_HAS_RAMDISK := true
+  else
+    INTERNAL_INIT_BOOT_IMAGE_ARGS += --ramdisk $(INSTALLED_RAMDISK_TARGET)
+  endif
 endif
 
 ifndef BUILDING_VENDOR_BOOT_IMAGE
@@ -955,21 +954,48 @@
     --os_version $(PLATFORM_VERSION_LAST_STABLE) \
     --os_patch_level $(PLATFORM_SECURITY_PATCH)
 
-ifdef BOARD_GKI_SIGNING_KEY_PATH
-ifndef BOARD_GKI_SIGNING_ALGORITHM
-$(error BOARD_GKI_SIGNING_ALGORITHM should be defined with BOARD_GKI_SIGNING_KEY_PATH)
-endif
-INTERNAL_MKBOOTIMG_GKI_SINGING_ARGS := \
-    --gki_signing_key $(BOARD_GKI_SIGNING_KEY_PATH) \
-    --gki_signing_algorithm $(BOARD_GKI_SIGNING_ALGORITHM) \
-    --gki_signing_avbtool_path $(AVBTOOL)
-endif
+# $(1): image target to certify
+# $(2): out certificate target
+# $(3): image name
+# $(4): additional AVB arguments
+define generate_generic_boot_image_certificate
+  rm -rf "$(2)"
+  mkdir -p "$(dir $(2))"
+  $(GENERATE_GKI_CERTIFICATE) $(INTERNAL_GKI_CERTIFICATE_ARGS) \
+    --additional_avb_args "$(4)" \
+    --name "$(3)" --output "$(2)" "$(1)"
+endef
 
-# Using double quote to pass BOARD_GKI_SIGNING_SIGNATURE_ARGS as a single string
-# to MKBOOTIMG, although it may contain multiple args.
-ifdef BOARD_GKI_SIGNING_SIGNATURE_ARGS
-INTERNAL_MKBOOTIMG_GKI_SINGING_ARGS += \
-    --gki_signing_signature_args "$(BOARD_GKI_SIGNING_SIGNATURE_ARGS)"
+INTERNAL_GKI_CERTIFICATE_ARGS :=
+INTERNAL_GKI_CERTIFICATE_DEPS :=
+INTERNAL_GENERIC_RAMDISK_BOOT_SIGNATURE :=
+ifdef BOARD_GKI_SIGNING_KEY_PATH
+  ifndef BOARD_GKI_SIGNING_ALGORITHM
+    $(error BOARD_GKI_SIGNING_ALGORITHM should be defined with BOARD_GKI_SIGNING_KEY_PATH)
+  endif
+
+  INTERNAL_GKI_CERTIFICATE_ARGS := \
+    --key "$(BOARD_GKI_SIGNING_KEY_PATH)" \
+    --algorithm "$(BOARD_GKI_SIGNING_ALGORITHM)" \
+    --avbtool "$(AVBTOOL)"
+
+  # Quote and pass BOARD_GKI_SIGNING_SIGNATURE_ARGS as a single string argument.
+  ifdef BOARD_GKI_SIGNING_SIGNATURE_ARGS
+    INTERNAL_GKI_CERTIFICATE_ARGS += --additional_avb_args "$(BOARD_GKI_SIGNING_SIGNATURE_ARGS)"
+  endif
+
+  INTERNAL_GKI_CERTIFICATE_DEPS := \
+    $(GENERATE_GKI_CERTIFICATE) \
+    $(BOARD_GKI_SIGNING_KEY_PATH) \
+    $(AVBTOOL)
+
+  ifdef INSTALLED_RAMDISK_TARGET
+    INTERNAL_GENERIC_RAMDISK_BOOT_SIGNATURE := \
+      $(call intermediates-dir-for,PACKAGING,generic_ramdisk)/boot_signature
+
+    $(INTERNAL_GENERIC_RAMDISK_BOOT_SIGNATURE): $(INSTALLED_RAMDISK_TARGET) $(INTERNAL_GKI_CERTIFICATE_DEPS)
+	$(call generate_generic_boot_image_certificate,$(INSTALLED_RAMDISK_TARGET),$@,generic_ramdisk,$(BOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS))
+  endif
 endif
 
 # Define these only if we are building boot
@@ -986,8 +1012,15 @@
 
 # $1: boot image target
 define build_boot_board_avb_enabled
-  $(MKBOOTIMG) --kernel $(call bootimage-to-kernel,$(1)) $(INTERNAL_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) \
-               $(INTERNAL_MKBOOTIMG_GKI_SINGING_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $(1)
+  $(eval kernel := $(call bootimage-to-kernel,$(1)))
+  $(if $(BOARD_GKI_SIGNING_KEY_PATH), \
+    $(eval kernel_signature := $(call intermediates-dir-for,PACKAGING,generic_kernel)/$(notdir $(kernel)).boot_signature) \
+    $(call generate_generic_boot_image_certificate,$(kernel),$(kernel_signature),generic_kernel,$(BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS)) $(newline) \
+    $(if $(INTERNAL_BOOT_HAS_RAMDISK), \
+      cat $(INTERNAL_GENERIC_RAMDISK_BOOT_SIGNATURE) >> $(kernel_signature) $(newline)))
+  $(MKBOOTIMG) --kernel $(kernel) $(INTERNAL_BOOTIMAGE_ARGS) \
+    $(if $(BOARD_GKI_SIGNING_KEY_PATH),--boot_signature "$(kernel_signature)",$(INTERNAL_MKBOOTIMG_VERSION_ARGS)) \
+    $(BOARD_MKBOOTIMG_ARGS) --output $(1)
   $(call assert-max-image-size,$(1),$(call get-hash-image-max-size,$(call get-bootimage-partition-size,$(1),boot)))
   $(AVBTOOL) add_hash_footer \
           --image $(1) \
@@ -996,12 +1029,15 @@
           $(BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS)
 endef
 
-$(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(AVBTOOL) $(INTERNAL_BOOTIMAGE_FILES) $(BOARD_AVB_BOOT_KEY_PATH) $(BOARD_GKI_SIGNING_KEY_PATH)
+ifdef INTERNAL_BOOT_HAS_RAMDISK
+$(INSTALLED_BOOTIMAGE_TARGET): $(INTERNAL_GENERIC_RAMDISK_BOOT_SIGNATURE)
+endif
+$(INSTALLED_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(AVBTOOL) $(INTERNAL_BOOTIMAGE_FILES) $(BOARD_AVB_BOOT_KEY_PATH) $(INTERNAL_GKI_CERTIFICATE_DEPS)
 	$(call pretty,"Target boot image: $@")
 	$(call build_boot_board_avb_enabled,$@)
 
 .PHONY: bootimage-nodeps
-bootimage-nodeps: $(MKBOOTIMG) $(AVBTOOL) $(BOARD_AVB_BOOT_KEY_PATH) $(BOARD_GKI_SIGNING_KEY_PATH)
+bootimage-nodeps: $(MKBOOTIMG) $(AVBTOOL) $(BOARD_AVB_BOOT_KEY_PATH) $(INTERNAL_GKI_CERTIFICATE_DEPS)
 	@echo "make $@: ignoring dependencies"
 	$(foreach b,$(INSTALLED_BOOTIMAGE_TARGET),$(call build_boot_board_avb_enabled,$(b)))
 
@@ -1089,6 +1125,61 @@
 my_installed_prebuilt_gki_apex :=
 
 # -----------------------------------------------------------------
+#  init boot image
+ifeq ($(BUILDING_INIT_BOOT_IMAGE),true)
+
+INSTALLED_INIT_BOOT_IMAGE_TARGET := $(PRODUCT_OUT)/init_boot.img
+$(INSTALLED_INIT_BOOT_IMAGE_TARGET): $(MKBOOTIMG) $(INSTALLED_RAMDISK_TARGET)
+
+ifdef BOARD_KERNEL_PAGESIZE
+  INTERNAL_INIT_BOOT_IMAGE_ARGS += --pagesize $(BOARD_KERNEL_PAGESIZE)
+endif
+
+ifeq ($(BOARD_AVB_ENABLE),true)
+$(INSTALLED_INIT_BOOT_IMAGE_TARGET): $(INTERNAL_GENERIC_RAMDISK_BOOT_SIGNATURE)
+$(INSTALLED_INIT_BOOT_IMAGE_TARGET): $(AVBTOOL) $(BOARD_AVB_INIT_BOOT_KEY_PATH)
+	$(call pretty,"Target init_boot image: $@")
+	$(MKBOOTIMG) $(INTERNAL_INIT_BOOT_IMAGE_ARGS) \
+	  $(if $(BOARD_GKI_SIGNING_KEY_PATH),--boot_signature "$(INTERNAL_GENERIC_RAMDISK_BOOT_SIGNATURE)",$(INTERNAL_MKBOOTIMG_VERSION_ARGS)) \
+	  $(BOARD_MKBOOTIMG_INIT_ARGS) --output "$@"
+	$(call assert-max-image-size,$@,$(BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE))
+	$(AVBTOOL) add_hash_footer \
+           --image $@ \
+	   --partition_size $(BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE) \
+	   --partition_name init_boot $(INTERNAL_AVB_INIT_BOOT_SIGNING_ARGS) \
+	   $(BOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS)
+else
+$(INSTALLED_INIT_BOOT_IMAGE_TARGET):
+	$(call pretty,"Target init_boot image: $@")
+	$(MKBOOTIMG) $(INTERNAL_INIT_BOOT_IMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_MKBOOTIMG_INIT_ARGS) --output $@
+	$(call assert-max-image-size,$@,$(BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE))
+endif
+
+else # BUILDING_INIT_BOOT_IMAGE is not true
+
+ifdef BOARD_PREBUILT_INIT_BOOT_IMAGE
+INTERNAL_PREBUILT_INIT_BOOT_IMAGE := $(BOARD_PREBUILT_INIT_BOOT_IMAGE)
+INSTALLED_INIT_BOOT_IMAGE_TARGET := $(PRODUCT_OUT)/init_boot.img
+
+ifeq ($(BOARD_AVB_ENABLE),true)
+$(INSTALLED_INIT_BOOT_IMAGE_TARGET): $(INTERNAL_PREBUILT_INIT_BOOT_IMAGE) $(AVBTOOL) $(BOARD_AVB_INIT_BOOT_KEY_PATH)
+	cp $(INTERNAL_PREBUILT_INIT_BOOT_IMAGE) $@
+	$(AVBTOOL) add_hash_footer \
+	    --image $@ \
+	    --partition_size $(BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE) \
+	    --partition_name boot $(INTERNAL_AVB_INIT_BOOT_SIGNING_ARGS) \
+	    $(BOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS)
+else
+$(INSTALLED_INIT_BOOT_IMAGE_TARGET): $(INTERNAL_PREBUILT_INIT_BOOT_IMAGE)
+	cp $(INTERNAL_PREBUILT_INIT_BOOT_IMAGE) $@
+endif # BOARD_AVB_ENABLE
+
+else # BOARD_PREBUILT_INIT_BOOT_IMAGE not defined
+INSTALLED_INIT_BOOT_IMAGE_TARGET :=
+endif # BOARD_PREBUILT_INIT_BOOT_IMAGE
+
+endif # BUILDING_INIT_BOOT_IMAGE is not true
+# -----------------------------------------------------------------
 # vendor boot image
 ifeq ($(BUILDING_VENDOR_BOOT_IMAGE),true)
 
@@ -1685,11 +1776,6 @@
 $(if $(filter $(2),system),\
     $(if $(INTERNAL_SYSTEM_OTHER_PARTITION_SIZE),$(hide) echo "system_other_size=$(INTERNAL_SYSTEM_OTHER_PARTITION_SIZE)" >> $(1))
     $(if $(PRODUCT_SYSTEM_HEADROOM),$(hide) echo "system_headroom=$(PRODUCT_SYSTEM_HEADROOM)" >> $(1))
-    $(if $(filter true,$(PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA)),$(hide) echo "fsverity=$(HOST_OUT_EXECUTABLES)/fsverity" >> $(1))
-    $(if $(filter true,$(PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA)),$(hide) echo "fsverity_generate_metadata=true" >> $(1))
-    $(if $(filter true,$(PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA)),$(hide) echo "fsverity_apk_key=$(FSVERITY_APK_KEY_PATH)" >> $(1))
-    $(if $(filter true,$(PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA)),$(hide) echo "fsverity_apk_manifest=$(FSVERITY_APK_MANIFEST_PATH)" >> $(1))
-    $(if $(filter true,$(PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA)),$(hide) echo "fsverity_apk_out=$(FSVERITY_APK_OUT)" >> $(1))
     $(call add-common-ro-flags-to-image-props,system,$(1))
 )
 $(if $(filter $(2),system_other),\
@@ -2073,6 +2159,9 @@
 
 ifdef TARGET_RECOVERY_FSTAB
 recovery_fstab := $(TARGET_RECOVERY_FSTAB)
+else ifdef TARGET_RECOVERY_FSTAB_GENRULE
+# Specifies a soong genrule module that generates an fstab.
+recovery_fstab := $(call intermediates-dir-for,ETC,$(TARGET_RECOVERY_FSTAB_GENRULE))/$(TARGET_RECOVERY_FSTAB_GENRULE)
 else
 recovery_fstab := $(strip $(wildcard $(TARGET_DEVICE_DIR)/recovery.fstab))
 endif
@@ -2251,7 +2340,7 @@
                  $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(BOARD_RECOVERY_MKBOOTIMG_ARGS) \
                  --output $(1).unsigned, \
     $(MKBOOTIMG) $(if $(strip $(2)),--kernel $(strip $(2))) $(INTERNAL_RECOVERYIMAGE_ARGS) \
-                 $(INTERNAL_MKBOOTIMG_VERSION_ARGS) $(INTERNAL_MKBOOTIMG_GKI_SINGING_ARGS) \
+                 $(INTERNAL_MKBOOTIMG_VERSION_ARGS) \
                  $(BOARD_RECOVERY_MKBOOTIMG_ARGS) --output $(1))
   $(if $(filter true,$(PRODUCT_SUPPORTS_BOOT_SIGNER)),\
     $(if $(filter true,$(BOARD_USES_RECOVERY_AS_BOOT)),\
@@ -2280,9 +2369,6 @@
 ifeq (true,$(BOARD_AVB_ENABLE))
   recoveryimage-deps += $(AVBTOOL) $(BOARD_AVB_BOOT_KEY_PATH)
 endif
-ifdef BOARD_GKI_SIGNING_KEY_PATH
-  recoveryimage-deps += $(BOARD_GKI_SIGNING_KEY_PATH) $(AVBTOOL)
-endif
 ifdef BOARD_INCLUDE_RECOVERY_DTBO
   ifdef BOARD_PREBUILT_RECOVERY_DTBOIMAGE
     recoveryimage-deps += $(BOARD_PREBUILT_RECOVERY_DTBOIMAGE)
@@ -2446,17 +2532,17 @@
 define build-debug-bootimage-target
   $(MKBOOTIMG) --kernel $(PRODUCT_OUT)/$(subst .img,,$(subst boot-debug,kernel,$(notdir $(1)))) \
     $(INTERNAL_DEBUG_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) \
-    $(INTERNAL_MKBOOTIMG_GKI_SINGING_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $1
+    $(BOARD_MKBOOTIMG_ARGS) --output $1
   $(if $(BOARD_AVB_BOOT_KEY_PATH),$(call test-key-sign-bootimage,$1,boot-debug))
 endef
 
 # Depends on original boot.img and ramdisk-debug.img, to build the new boot-debug.img
-$(INSTALLED_DEBUG_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INSTALLED_BOOTIMAGE_TARGET) $(BOARD_GKI_SIGNING_KEY_PATH) $(AVBTOOL)
+$(INSTALLED_DEBUG_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INSTALLED_BOOTIMAGE_TARGET) $(AVBTOOL)
 	$(call pretty,"Target boot debug image: $@")
 	$(call build-debug-bootimage-target, $@)
 
 .PHONY: bootimage_debug-nodeps
-bootimage_debug-nodeps: $(MKBOOTIMG) $(BOARD_GKI_SIGNING_KEY_PATH) $(AVBTOOL)
+bootimage_debug-nodeps: $(MKBOOTIMG) $(AVBTOOL)
 	echo "make $@: ignoring dependencies"
 	$(foreach b,$(INSTALLED_DEBUG_BOOTIMAGE_TARGET),$(call build-debug-bootimage-target,$b))
 
@@ -2623,17 +2709,17 @@
 define build-boot-test-harness-target
   $(MKBOOTIMG) --kernel $(PRODUCT_OUT)/$(subst .img,,$(subst boot-test-harness,kernel,$(notdir $(1)))) \
     $(INTERNAL_TEST_HARNESS_BOOTIMAGE_ARGS) $(INTERNAL_MKBOOTIMG_VERSION_ARGS) \
-    $(INTERNAL_MKBOOTIMG_GKI_SINGING_ARGS) $(BOARD_MKBOOTIMG_ARGS) --output $@
+    $(BOARD_MKBOOTIMG_ARGS) --output $@
   $(if $(BOARD_AVB_BOOT_KEY_PATH),$(call test-key-sign-bootimage,$@,boot-test-harness))
 endef
 
 # Build the new boot-test-harness.img, based on boot-debug.img and ramdisk-test-harness.img.
-$(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INSTALLED_DEBUG_BOOTIMAGE_TARGET) $(BOARD_GKI_SIGNING_KEY_PATH) $(AVBTOOL)
+$(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET): $(MKBOOTIMG) $(INSTALLED_DEBUG_BOOTIMAGE_TARGET) $(AVBTOOL)
 	$(call pretty,"Target boot test harness image: $@")
 	$(call build-boot-test-harness-target,$@)
 
 .PHONY: bootimage_test_harness-nodeps
-bootimage_test_harness-nodeps: $(MKBOOTIMG) $(BOARD_GKI_SIGNING_KEY_PATH) $(AVBTOOL)
+bootimage_test_harness-nodeps: $(MKBOOTIMG) $(AVBTOOL)
 	echo "make $@: ignoring dependencies"
 	$(foreach b,$(INSTALLED_TEST_HARNESS_BOOTIMAGE_TARGET),$(call build-boot-test-harness-target,$b))
 
@@ -2709,6 +2795,59 @@
 # -----------------------------------------------------------------
 # system image
 
+# FSVerity metadata generation
+# Generate fsverity metadata files (.fsv_meta) and build manifest
+# (system/etc/security/fsverity/BuildManifest.apk) BEFORE filtering systemimage files below
+ifeq ($(PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA),true)
+
+# Generate fsv_meta
+fsverity-metadata-targets := $(sort $(filter \
+  $(TARGET_OUT)/framework/% \
+  $(TARGET_OUT)/etc/boot-image.prof \
+  $(TARGET_OUT)/etc/dirty-image-objects \
+  $(TARGET_OUT)/etc/classpaths/%.pb, \
+  $(ALL_GENERATED_SOURCES) $(ALL_DEFAULT_INSTALLED_MODULES)))
+
+define fsverity-generate-metadata
+$(1).fsv_meta: PRIVATE_SRC := $(1)
+$(1).fsv_meta: PRIVATE_FSVERITY := $(HOST_OUT_EXECUTABLES)/fsverity
+$(1).fsv_meta: $(HOST_OUT_EXECUTABLES)/fsverity_metadata_generator $(HOST_OUT_EXECUTABLES)/fsverity $(1)
+	$$< --fsverity-path $$(PRIVATE_FSVERITY) --signature none \
+	    --hash-alg sha256 --output $$@ $$(PRIVATE_SRC)
+endef
+
+$(foreach f,$(fsverity-metadata-targets),$(eval $(call fsverity-generate-metadata,$(f))))
+ALL_DEFAULT_INSTALLED_MODULES += $(addsuffix .fsv_meta,$(fsverity-metadata-targets))
+
+# Generate BuildManifest.apk
+FSVERITY_APK_KEY_PATH := $(DEFAULT_SYSTEM_DEV_CERTIFICATE)
+FSVERITY_APK_OUT := $(TARGET_OUT)/etc/security/fsverity/BuildManifest.apk
+FSVERITY_APK_MANIFEST_PATH := system/security/fsverity/AndroidManifest.xml
+$(FSVERITY_APK_OUT): PRIVATE_FSVERITY := $(HOST_OUT_EXECUTABLES)/fsverity
+$(FSVERITY_APK_OUT): PRIVATE_AAPT2 := $(HOST_OUT_EXECUTABLES)/aapt2
+$(FSVERITY_APK_OUT): PRIVATE_MIN_SDK_VERSION := $(DEFAULT_APP_TARGET_SDK)
+$(FSVERITY_APK_OUT): PRIVATE_APKSIGNER := $(HOST_OUT_EXECUTABLES)/apksigner
+$(FSVERITY_APK_OUT): PRIVATE_MANIFEST := $(FSVERITY_APK_MANIFEST_PATH)
+$(FSVERITY_APK_OUT): PRIVATE_FRAMEWORK_RES := $(call intermediates-dir-for,APPS,framework-res,,COMMON)/package-export.apk
+$(FSVERITY_APK_OUT): PRIVATE_KEY := $(FSVERITY_APK_KEY_PATH)
+$(FSVERITY_APK_OUT): PRIVATE_INPUTS := $(fsverity-metadata-targets)
+$(FSVERITY_APK_OUT): $(HOST_OUT_EXECUTABLES)/fsverity_manifest_generator \
+    $(HOST_OUT_EXECUTABLES)/fsverity $(HOST_OUT_EXECUTABLES)/aapt2 \
+    $(HOST_OUT_EXECUTABLES)/apksigner $(FSVERITY_APK_MANIFEST_PATH) \
+    $(FSVERITY_APK_KEY_PATH).x509.pem $(FSVERITY_APK_KEY_PATH).pk8 \
+    $(call intermediates-dir-for,APPS,framework-res,,COMMON)/package-export.apk \
+    $(fsverity-metadata-targets)
+	$< --fsverity-path $(PRIVATE_FSVERITY) --aapt2-path $(PRIVATE_AAPT2) \
+	    --min-sdk-version $(PRIVATE_MIN_SDK_VERSION) \
+	    --apksigner-path $(PRIVATE_APKSIGNER) --apk-key-path $(PRIVATE_KEY) \
+	    --apk-manifest-path $(PRIVATE_MANIFEST) --framework-res $(PRIVATE_FRAMEWORK_RES) \
+	    --output $@ \
+	    --base-dir $(PRODUCT_OUT) $(PRIVATE_INPUTS)
+
+ALL_DEFAULT_INSTALLED_MODULES += $(FSVERITY_APK_OUT)
+
+endif  # PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA
+
 INTERNAL_SYSTEMIMAGE_FILES := $(sort $(filter $(TARGET_OUT)/%, \
     $(ALL_GENERATED_SOURCES) \
     $(ALL_DEFAULT_INSTALLED_MODULES)))
@@ -2728,6 +2867,19 @@
   INTERNAL_SYSTEMIMAGE_FILES += $(call create-partition-compat-symlink,$(TARGET_OUT)/system_ext,/system_ext,system_ext.img)
 endif
 
+# -----------------------------------------------------------------
+# system_dlkm partition image
+
+# Create symlinks for system_dlkm on devices with a system_dlkm partition:
+# /system/lib/modules -> /system_dlkm/lib/modules
+#
+# On devices with a system_dlkm partition,
+# - /system/lib/modules is a symlink to a directory that stores system DLKMs.
+# - The system_dlkm partition is mounted at /system_dlkm at runtime.
+ifdef BOARD_USES_SYSTEM_DLKM_PARTITION
+  INTERNAL_SYSTEMIMAGE_FILES += $(call create-partition-compat-symlink,$(TARGET_OUT)/lib/modules,/system_dlkm/lib/modules,system_dlkm.img)
+endif
+
 FULL_SYSTEMIMAGE_DEPS := $(INTERNAL_SYSTEMIMAGE_FILES) $(INTERNAL_USERIMAGES_DEPS)
 
 # ASAN libraries in the system image - add dependency.
@@ -2795,10 +2947,6 @@
 ifeq ($(BOARD_AVB_ENABLE),true)
 $(BUILT_SYSTEMIMAGE): $(BOARD_AVB_SYSTEM_KEY_PATH)
 endif
-ifeq ($(PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA),true)
-$(BUILT_SYSTEMIMAGE): $(HOST_OUT_EXECUTABLES)/fsverity $(HOST_OUT_EXECUTABLES)/aapt2 $(HOST_OUT_EXECUTABLES)/apksigner \
-    $(FSVERITY_APK_MANIFEST_PATH) $(FSVERITY_APK_KEY_PATH).x509.pem $(FSVERITY_APK_KEY_PATH).pk8
-endif
 $(BUILT_SYSTEMIMAGE): $(FULL_SYSTEMIMAGE_DEPS) $(INSTALLED_FILES_FILE)
 	$(call build-systemimage-target,$@)
 
@@ -3475,7 +3623,10 @@
 # $(INSTALLED_VENDORIMAGE_TARGET)" for "system vendor".
 # (1): list of partitions like "system", "vendor" or "system product system_ext".
 define images-for-partitions
-$(strip $(foreach item,$(1),$(if $(filter $(item),system_other),$(INSTALLED_SYSTEMOTHERIMAGE_TARGET),$(INSTALLED_$(call to-upper,$(item))IMAGE_TARGET))))
+$(strip $(foreach item,$(1),\
+  $(if $(filter $(item),system_other),$(INSTALLED_SYSTEMOTHERIMAGE_TARGET),\
+    $(if $(filter $(item),init_boot),$(INSTALLED_INIT_BOOT_IMAGE_TARGET),\
+      $(INSTALLED_$(call to-upper,$(item))IMAGE_TARGET)))))
 endef
 
 # -----------------------------------------------------------------
@@ -3592,6 +3743,10 @@
     --prop com.android.build.boot.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) \
     --prop com.android.build.boot.os_version:$(PLATFORM_VERSION_LAST_STABLE)
 
+BOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS += \
+    --prop com.android.build.init_boot.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) \
+    --prop com.android.build.init_boot.os_version:$(PLATFORM_VERSION_LAST_STABLE)
+
 BOARD_AVB_VENDOR_BOOT_ADD_HASH_FOOTER_ARGS += \
     --prop com.android.build.vendor_boot.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE) \
 
@@ -3621,11 +3776,21 @@
     --prop com.android.build.pvmfw.fingerprint:$(BUILD_FINGERPRINT_FROM_FILE)
 
 # The following vendor- and odm-specific images needs explicit SPL set per board.
+# TODO(b/210875415) Is this security_patch property used? Should it be removed from
+# boot.img when there is no platform ramdisk included in it?
 ifdef BOOT_SECURITY_PATCH
 BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS += \
     --prop com.android.build.boot.security_patch:$(BOOT_SECURITY_PATCH)
 endif
 
+ifdef INIT_BOOT_SECURITY_PATCH
+BOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS += \
+    --prop com.android.build.init_boot.security_patch:$(INIT_BOOT_SECURITY_PATCH)
+else ifdef BOOT_SECURITY_PATCH
+BOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS += \
+    --prop com.android.build.init_boot.security_patch:$(BOOT_SECURITY_PATCH)
+endif
+
 ifdef VENDOR_SECURITY_PATCH
 BOARD_AVB_VENDOR_ADD_HASHTREE_FOOTER_ARGS += \
     --prop com.android.build.vendor.security_patch:$(VENDOR_SECURITY_PATCH)
@@ -3651,7 +3816,15 @@
     --prop com.android.build.pvmfw.security_patch:$(PVMFW_SECURITY_PATCH)
 endif
 
+# For upgrading devices without a init_boot partition, the init_boot footer args
+# should fallback to boot partition footer.
+ifndef INSTALLED_INIT_BOOT_IMAGE_TARGET
+BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS += \
+    $(BOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS)
+endif
+
 BOOT_FOOTER_ARGS := BOARD_AVB_BOOT_ADD_HASH_FOOTER_ARGS
+INIT_BOOT_FOOTER_ARGS := BOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS
 VENDOR_BOOT_FOOTER_ARGS := BOARD_AVB_VENDOR_BOOT_ADD_HASH_FOOTER_ARGS
 DTBO_FOOTER_ARGS := BOARD_AVB_DTBO_ADD_HASH_FOOTER_ARGS
 PVMFW_FOOTER_ARGS := BOARD_AVB_PVMFW_ADD_HASH_FOOTER_ARGS
@@ -3735,11 +3908,17 @@
 $(eval $(call check-and-set-avb-args,boot))
 endif
 
+ifdef INSTALLED_INIT_BOOT_IMAGE_TARGET
+$(eval $(call check-and-set-avb-args,init_boot))
+endif
+
 ifdef INSTALLED_VENDOR_BOOTIMAGE_TARGET
 $(eval $(call check-and-set-avb-args,vendor_boot))
 endif
 
+ifdef INSTALLED_SYSTEMIMAGE_TARGET
 $(eval $(call check-and-set-avb-args,system))
+endif
 
 ifdef INSTALLED_VENDORIMAGE_TARGET
 $(eval $(call check-and-set-avb-args,vendor))
@@ -3831,6 +4010,9 @@
   $(if $(BOARD_AVB_BOOT_KEY_PATH),\
     $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_BOOT_KEY_PATH) \
       --output $(1)/boot.avbpubkey)
+  $(if $(BOARD_AVB_INIT_BOOT_KEY_PATH),\
+    $(hide) $(AVBTOOL) extract_public_key --key $(BOARD_AVB_INIT_BOOT_KEY_PATH) \
+      --output $(1)/init_boot.avbpubkey)
   $(if $(BOARD_AVB_VENDOR_BOOT_KEY_PATH),\
     $(AVBTOOL) extract_public_key --key $(BOARD_AVB_VENDOR_BOOT_KEY_PATH) \
       --output $(1)/vendor_boot.avbpubkey)
@@ -3934,6 +4116,7 @@
 $(INSTALLED_VBMETAIMAGE_TARGET): \
 	    $(AVBTOOL) \
 	    $(INSTALLED_BOOTIMAGE_TARGET) \
+	    $(INSTALLED_INIT_BOOT_IMAGE_TARGET) \
 	    $(INSTALLED_VENDOR_BOOTIMAGE_TARGET) \
 	    $(INSTALLED_SYSTEMIMAGE_TARGET) \
 	    $(INSTALLED_VENDORIMAGE_TARGET) \
@@ -4351,6 +4534,7 @@
   fec \
   fsck.f2fs \
   fs_config \
+  generate_gki_certificate \
   generate_verity_key \
   host_init_verifier \
   img2simg \
@@ -4505,6 +4689,10 @@
 else
 	echo "boot_images=$(foreach b,$(INSTALLED_BOOTIMAGE_TARGET),$(notdir $(b)))" >> $@
 endif
+ifneq ($(INSTALLED_INIT_BOOT_IMAGE_TARGET),)
+	$(hide) echo "init_boot=true" >> $@
+	$(hide) echo "init_boot_size=$(BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE)" >> $@
+endif
 ifeq ($(BOARD_RAMDISK_USE_LZ4),true)
 	echo "lz4_ramdisks=true" >> $@
 endif
@@ -4532,17 +4720,19 @@
 endif
 	$(hide) echo "tool_extensions=$(tool_extensions)" >> $@
 	$(hide) echo "default_system_dev_certificate=$(DEFAULT_SYSTEM_DEV_CERTIFICATE)" >> $@
+ifdef PRODUCT_EXTRA_OTA_KEYS
+	$(hide) echo "extra_ota_keys=$(PRODUCT_EXTRA_OTA_KEYS)" >> $@
+endif
 ifdef PRODUCT_EXTRA_RECOVERY_KEYS
 	$(hide) echo "extra_recovery_keys=$(PRODUCT_EXTRA_RECOVERY_KEYS)" >> $@
 endif
 	$(hide) echo 'mkbootimg_args=$(BOARD_MKBOOTIMG_ARGS)' >> $@
 	$(hide) echo 'recovery_mkbootimg_args=$(BOARD_RECOVERY_MKBOOTIMG_ARGS)' >> $@
 	$(hide) echo 'mkbootimg_version_args=$(INTERNAL_MKBOOTIMG_VERSION_ARGS)' >> $@
+	$(hide) echo 'mkbootimg_init_args=$(BOARD_MKBOOTIMG_INIT_ARGS)' >> $@
 ifdef BOARD_GKI_SIGNING_KEY_PATH
 	$(hide) echo 'gki_signing_key_path=$(BOARD_GKI_SIGNING_KEY_PATH)' >> $@
 	$(hide) echo 'gki_signing_algorithm=$(BOARD_GKI_SIGNING_ALGORITHM)' >> $@
-endif
-ifdef BOARD_GKI_SIGNING_SIGNATURE_ARGS
 	$(hide) echo 'gki_signing_signature_args=$(BOARD_GKI_SIGNING_SIGNATURE_ARGS)' >> $@
 endif
 	$(hide) echo "multistage_support=1" >> $@
@@ -4578,6 +4768,12 @@
 	$(hide) echo "avb_boot_algorithm=$(BOARD_AVB_BOOT_ALGORITHM)" >> $@
 	$(hide) echo "avb_boot_rollback_index_location=$(BOARD_AVB_BOOT_ROLLBACK_INDEX_LOCATION)" >> $@
 endif # BOARD_AVB_BOOT_KEY_PATH
+	$(hide) echo "avb_init_boot_add_hash_footer_args=$(BOARD_AVB_INIT_BOOT_ADD_HASH_FOOTER_ARGS)" >> $@
+ifdef BOARD_AVB_INIT_BOOT_KEY_PATH
+	$(hide) echo "avb_init_boot_key_path=$(BOARD_AVB_INIT_BOOT_KEY_PATH)" >> $@
+	$(hide) echo "avb_init_boot_algorithm=$(BOARD_AVB_INIT_BOOT_ALGORITHM)" >> $@
+	$(hide) echo "avb_init_boot_rollback_index_location=$(BOARD_AVB_INIT_BOOT_ROLLBACK_INDEX_LOCATION)" >> $@
+endif # BOARD_AVB_INIT_BOOT_KEY_PATH
 	echo "avb_vendor_boot_add_hash_footer_args=$(BOARD_AVB_VENDOR_BOOT_ADD_HASH_FOOTER_ARGS)" >> $@
 ifdef BOARD_AVB_VENDOR_BOOT_KEY_PATH
 	echo "avb_vendor_boot_key_path=$(BOARD_AVB_VENDOR_BOOT_KEY_PATH)" >> $@
@@ -4734,14 +4930,16 @@
 tool_extension := $(wildcard $(tool_extensions)/releasetools.py)
 $(BUILT_TARGET_FILES_PACKAGE): PRIVATE_TOOL_EXTENSION := $(tool_extension)
 
+updaer_dep :=
 ifeq ($(AB_OTA_UPDATER),true)
-updater_dep := system/update_engine/update_engine.conf
-updater_dep := external/zucchini/version_info.h
+updater_dep += system/update_engine/update_engine.conf
+updater_dep += external/zucchini/version_info.h
+updater_dep += $(HOST_OUT_SHARED_LIBRARIES)/liblz4.so
 endif
 
 # Build OTA tools if non-A/B is allowed
 ifeq ($(TARGET_OTA_ALLOW_NON_AB),true)
-updater_dep := $(built_ota_tools)
+updater_dep += $(built_ota_tools)
 endif
 
 $(BUILT_TARGET_FILES_PACKAGE): $(updater_dep)
@@ -4860,6 +5058,10 @@
 # image.
 ifdef BUILDING_SYSTEM_IMAGE
   $(BUILT_TARGET_FILES_PACKAGE): $(FULL_SYSTEMIMAGE_DEPS)
+else
+  # releasetools may need the system build.prop even when building a
+  # system-image-less product.
+  $(BUILT_TARGET_FILES_PACKAGE): $(INSTALLED_BUILD_PROP_TARGET)
 endif
 
 ifdef BUILDING_USERDATA_IMAGE
@@ -4915,9 +5117,10 @@
   $(BUILT_TARGET_FILES_PACKAGE): $(INSTALLED_SYSTEM_EXTIMAGE_TARGET)
 endif
 
-ifdef BUILDING_BOOT_IMAGE
+ifneq (,$(BUILDING_BOOT_IMAGE)$(BUILDING_INIT_BOOT_IMAGE))
   $(BUILT_TARGET_FILES_PACKAGE): $(INTERNAL_RAMDISK_FILES)
-endif
+endif  # BUILDING_BOOT_IMAGE != "" || BUILDING_INIT_BOOT_IMAGE != ""
+
 ifneq (,$(INTERNAL_PREBUILT_BOOTIMAGE) $(filter true,$(BOARD_COPY_BOOT_IMAGE_TO_TARGET_FILES)))
   $(BUILT_TARGET_FILES_PACKAGE): $(INSTALLED_BOOTIMAGE_TARGET)
 endif
@@ -5105,6 +5308,12 @@
 	@# Contents of the system image
 	$(hide) $(call package_files-copy-root, \
 	    $(SYSTEMIMAGE_SOURCE_DIR),$(zip_root)/SYSTEM)
+else ifdef INSTALLED_BUILD_PROP_TARGET
+	@# Copy the system build.prop even if not building a system image
+	@# because add_img_to_target_files may need it to build other partition
+	@# images.
+	$(hide) mkdir -p "$(zip_root)/SYSTEM"
+	$(hide) cp "$(INSTALLED_BUILD_PROP_TARGET)" "$(patsubst $(TARGET_OUT)/%,$(zip_root)/SYSTEM/%,$(INSTALLED_BUILD_PROP_TARGET))"
 endif
 ifdef BUILDING_USERDATA_IMAGE
 	@# Contents of the data image
@@ -5211,6 +5420,7 @@
 	@# When using the A/B updater, include the updater config files in the zip.
 	$(hide) cp $(TOPDIR)system/update_engine/update_engine.conf $(zip_root)/META/update_engine_config.txt
 	$(hide) cp $(TOPDIR)external/zucchini/version_info.h $(zip_root)/META/zucchini_config.txt
+	$(hide) cp $(HOST_OUT_SHARED_LIBRARIES)/liblz4.so $(zip_root)/META/liblz4.so
 	$(hide) for part in $(strip $(AB_OTA_PARTITIONS)); do \
 	  echo "$${part}" >> $(zip_root)/META/ab_partitions.txt; \
 	done
@@ -5239,8 +5449,12 @@
 	$(hide) mkdir -p $(zip_root)/IMAGES
 	$(hide) cp $(INSTALLED_SYSTEM_EXTIMAGE_TARGET) $(zip_root)/IMAGES/
 endif
+ifdef BOARD_PREBUILT_INIT_BOOT_IMAGE
+	$(hide) mkdir -p $(zip_root)/PREBUILT_IMAGES
+	$(hide) cp $(INSTALLED_INIT_BOOT_IMAGE_TARGET) $(zip_root)/PREBUILT_IMAGES/
+endif
 ifndef BOARD_PREBUILT_BOOTIMAGE
-ifneq (,$(INTERNAL_PREBUILT_BOOTIMAGE) $(filter true,$(BOARD_COPY_BOOT_IMAGE_TO_TARGET_FILES)))
+ifneq (,$(strip $(INTERNAL_PREBUILT_BOOTIMAGE) $(filter true,$(BOARD_COPY_BOOT_IMAGE_TO_TARGET_FILES))))
 ifdef INSTALLED_BOOTIMAGE_TARGET
 	$(hide) mkdir -p $(zip_root)/IMAGES
 	$(hide) cp $(INSTALLED_BOOTIMAGE_TARGET) $(zip_root)/IMAGES/
@@ -5316,6 +5530,13 @@
 	@# BOOT/RAMDISK exists and contains the ramdisk for recovery if using BOARD_USES_RECOVERY_AS_BOOT.
 	$(hide) $(call fs_config,$(zip_root)/BOOT/RAMDISK,) > $(zip_root)/META/boot_filesystem_config.txt
 endif
+ifdef BUILDING_INIT_BOOT_IMAGE
+	$(hide) $(call package_files-copy-root, $(TARGET_RAMDISK_OUT),$(zip_root)/INIT_BOOT/RAMDISK)
+	$(hide) $(call fs_config,$(zip_root)/INIT_BOOT/RAMDISK,) > $(zip_root)/META/init_boot_filesystem_config.txt
+ifdef BOARD_KERNEL_PAGESIZE
+	$(hide) echo "$(BOARD_KERNEL_PAGESIZE)" > $(zip_root)/INIT_BOOT/pagesize
+endif # BOARD_KERNEL_PAGESIZE
+endif # BUILDING_INIT_BOOT_IMAGE
 ifneq ($(INSTALLED_VENDOR_BOOTIMAGE_TARGET),)
 	$(call fs_config,$(zip_root)/VENDOR_BOOT/RAMDISK,) > $(zip_root)/META/vendor_boot_filesystem_config.txt
 endif
@@ -5629,6 +5850,7 @@
     $(INSTALLED_SYSTEMIMAGE_TARGET) \
     $(INSTALLED_RAMDISK_TARGET) \
     $(INSTALLED_BOOTIMAGE_TARGET) \
+    $(INSTALLED_INIT_BOOT_IMAGE_TARGET) \
     $(INSTALLED_USERDATAIMAGE_TARGET) \
     $(INSTALLED_VENDORIMAGE_TARGET) \
     $(INSTALLED_PRODUCTIMAGE_TARGET) \
@@ -6121,6 +6343,10 @@
 haiku: $(SOONG_FUZZ_PACKAGING_ARCH_MODULES) $(ALL_FUZZ_TARGETS)
 $(call dist-for-goals,haiku,$(SOONG_FUZZ_PACKAGING_ARCH_MODULES))
 
+.PHONY: haiku-java
+haiku-java: $(SOONG_JAVA_FUZZ_PACKAGING_ARCH_MODULES) $(ALL_JAVA_FUZZ_TARGETS)
+$(call dist-for-goals,haiku-java,$(SOONG_JAVA_FUZZ_PACKAGING_ARCH_MODULES))
+
 .PHONY: haiku-rust
 haiku-rust: $(SOONG_RUST_FUZZ_PACKAGING_ARCH_MODULES) $(ALL_RUST_FUZZ_TARGETS)
 $(call dist-for-goals,haiku-rust,$(SOONG_RUST_FUZZ_PACKAGING_ARCH_MODULES))
diff --git a/core/android_soong_config_vars.mk b/core/android_soong_config_vars.mk
index cd42e1f..0befbfa 100644
--- a/core/android_soong_config_vars.mk
+++ b/core/android_soong_config_vars.mk
@@ -27,6 +27,10 @@
 # Add variables to the namespace below:
 
 $(call add_soong_config_var,ANDROID,TARGET_ENABLE_MEDIADRM_64)
+$(call add_soong_config_var,ANDROID,IS_TARGET_MIXED_SEPOLICY)
+ifeq ($(IS_TARGET_MIXED_SEPOLICY),true)
+$(call add_soong_config_var_value,ANDROID,MIXED_SEPOLICY_VERSION,$(BOARD_SEPOLICY_VERS))
+endif
 $(call add_soong_config_var,ANDROID,BOARD_USES_ODMIMAGE)
 $(call add_soong_config_var,ANDROID,BOARD_USES_RECOVERY_AS_BOOT)
 $(call add_soong_config_var,ANDROID,BOARD_BUILD_SYSTEM_ROOT_IMAGE)
@@ -88,7 +92,7 @@
   # This sets the default for building ART APEXes from source rather than
   # prebuilts (in packages/modules/ArtPrebuilt and prebuilt/module_sdk/art) in
   # all other platform builds.
-  SOONG_CONFIG_art_module_source_build ?= false
+  SOONG_CONFIG_art_module_source_build ?= true
 endif
 
 # Apex build mode variables
@@ -100,6 +104,9 @@
 $(call add_soong_config_var_value,ANDROID,module_build_from_source,true)
 endif
 
+# TODO(b/203088572): Remove when Java optimizations enabled by default for
+# SystemUI.
+$(call add_soong_config_var,ANDROID,SYSTEMUI_OPTIMIZE_JAVA)
 # TODO(b/196084106): Remove when Java optimizations enabled by default for
 # system packages.
 $(call add_soong_config_var,ANDROID,SYSTEM_OPTIMIZE_JAVA)
diff --git a/core/base_rules.mk b/core/base_rules.mk
index 134cb8f..cec7792 100644
--- a/core/base_rules.mk
+++ b/core/base_rules.mk
@@ -981,6 +981,18 @@
 ALL_MODULES.$(my_register_name).SYSTEM_SHARED_LIBS := \
     $(ALL_MODULES.$(my_register_name).SYSTEM_SHARED_LIBS) $(LOCAL_SYSTEM_SHARED_LIBRARIES)
 
+ifdef LOCAL_TEST_DATA
+  # Export the list of targets that are handled as data inputs and required
+  # by tests at runtime. The LOCAL_TEST_DATA format is generated from below
+  # https://cs.android.com/android/platform/superproject/+/master:build/soong/android/androidmk.go;l=925-944;drc=master
+  # which format is like $(path):$(relative_file) but for module-info, only
+  # the string after ":" is needed.
+  ALL_MODULES.$(my_register_name).TEST_DATA := \
+    $(strip $(ALL_MODULES.$(my_register_name).TEST_DATA) \
+      $(foreach f, $(LOCAL_TEST_DATA),\
+        $(call word-colon,2,$(f))))
+endif
+
 ##########################################################################
 ## When compiling against the VNDK, add the .vendor or .product suffix to
 ## required modules.
@@ -1055,7 +1067,9 @@
 endif
 ALL_MODULES.$(my_register_name).FOR_HOST_CROSS := $(my_host_cross)
 ALL_MODULES.$(my_register_name).MODULE_NAME := $(LOCAL_MODULE)
-ALL_MODULES.$(my_register_name).COMPATIBILITY_SUITES := $(LOCAL_COMPATIBILITY_SUITE)
+ALL_MODULES.$(my_register_name).COMPATIBILITY_SUITES := \
+  $(ALL_MODULES.$(my_register_name).COMPATIBILITY_SUITES) \
+  $(filter-out $(ALL_MODULES.$(my_register_name).COMPATIBILITY_SUITES),$(LOCAL_COMPATIBILITY_SUITE))
 ALL_MODULES.$(my_register_name).TEST_CONFIG := $(test_config)
 ALL_MODULES.$(my_register_name).EXTRA_TEST_CONFIGS := $(LOCAL_EXTRA_FULL_TEST_CONFIGS)
 ALL_MODULES.$(my_register_name).TEST_MAINLINE_MODULES := $(LOCAL_TEST_MAINLINE_MODULES)
diff --git a/core/board_config.mk b/core/board_config.mk
index 6bbb3a0..95cbe3d 100644
--- a/core/board_config.mk
+++ b/core/board_config.mk
@@ -65,6 +65,7 @@
 # File system variables
 _board_strip_readonly_list += BOARD_FLASH_BLOCK_SIZE
 _board_strip_readonly_list += BOARD_BOOTIMAGE_PARTITION_SIZE
+_board_strip_readonly_list += BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE
 _board_strip_readonly_list += BOARD_RECOVERYIMAGE_PARTITION_SIZE
 _board_strip_readonly_list += BOARD_SYSTEMIMAGE_PARTITION_SIZE
 _board_strip_readonly_list += BOARD_SYSTEMIMAGE_FILE_SYSTEM_TYPE
@@ -122,6 +123,9 @@
 _board_strip_readonly_list += BOARD_MOVE_GSI_AVB_KEYS_TO_VENDOR_BOOT
 _board_strip_readonly_list += BOARD_COPY_BOOT_IMAGE_TO_TARGET_FILES
 
+# Prebuilt image variables
+_board_strip_readonly_list += BOARD_PREBUILT_INIT_BOOT_IMAGE
+
 # Defines the list of logical vendor ramdisk names to build or include in vendor_boot.
 _board_strip_readonly_list += BOARD_VENDOR_RAMDISK_FRAGMENTS
 
@@ -461,6 +465,25 @@
 endif
 .KATI_READONLY := BUILDING_BOOT_IMAGE
 
+# Are we building an init boot image
+BUILDING_INIT_BOOT_IMAGE :=
+ifeq ($(PRODUCT_BUILD_INIT_BOOT_IMAGE),)
+  ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
+    BUILDING_INIT_BOOT_IMAGE :=
+  else ifdef BOARD_PREBUILT_INIT_BOOT_IMAGE
+    BUILDING_INIT_BOOT_IMAGE :=
+  else ifdef BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE
+    BUILDING_INIT_BOOT_IMAGE := true
+  endif
+else ifeq ($(PRODUCT_BUILD_INIT_BOOT_IMAGE),true)
+  ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
+    $(error PRODUCT_BUILD_INIT_BOOT_IMAGE is true, but so is BOARD_USES_RECOVERY_AS_BOOT. Use only one option.)
+  else
+    BUILDING_INIT_BOOT_IMAGE := true
+  endif
+endif
+.KATI_READONLY := BUILDING_INIT_BOOT_IMAGE
+
 # Are we building a recovery image
 BUILDING_RECOVERY_IMAGE :=
 ifeq ($(PRODUCT_BUILD_RECOVERY_IMAGE),)
@@ -569,6 +592,11 @@
     $(warning PRODUCT_BUILD_DEBUG_BOOT_IMAGE is true, but we're not building a boot image. \
       Skip building the debug boot image.)
   endif
+else ifdef BUILDING_INIT_BOOT_IMAGE
+  ifeq ($(PRODUCT_BUILD_DEBUG_BOOT_IMAGE),true)
+    $(warning PRODUCT_BUILD_DEBUG_BOOT_IMAGE is true, but we don't have a ramdisk in the boot image. \
+      Skip building the debug boot image.)
+  endif
 else
   ifeq ($(PRODUCT_BUILD_DEBUG_BOOT_IMAGE),)
     BUILDING_DEBUG_BOOT_IMAGE := true
diff --git a/core/config.mk b/core/config.mk
index 4794816..72b6ec8 100644
--- a/core/config.mk
+++ b/core/config.mk
@@ -577,6 +577,7 @@
 MKBOOTFS := $(HOST_OUT_EXECUTABLES)/mkbootfs$(HOST_EXECUTABLE_SUFFIX)
 MINIGZIP := $(HOST_OUT_EXECUTABLES)/minigzip$(HOST_EXECUTABLE_SUFFIX)
 LZ4 := $(HOST_OUT_EXECUTABLES)/lz4$(HOST_EXECUTABLE_SUFFIX)
+GENERATE_GKI_CERTIFICATE := $(HOST_OUT_EXECUTABLES)/generate_gki_certificate$(HOST_EXECUTABLE_SUFFIX)
 ifeq (,$(strip $(BOARD_CUSTOM_MKBOOTIMG)))
 MKBOOTIMG := $(HOST_OUT_EXECUTABLES)/mkbootimg$(HOST_EXECUTABLE_SUFFIX)
 else
@@ -831,6 +832,23 @@
 sepolicy_major_vers :=
 sepolicy_minor_vers :=
 
+# BOARD_SEPOLICY_VERS must take the format "NN.m" and contain the sepolicy
+# version identifier corresponding to the sepolicy on which the non-platform
+# policy is to be based. If unspecified, this will build against the current
+# public platform policy in tree
+ifndef BOARD_SEPOLICY_VERS
+# The default platform policy version.
+BOARD_SEPOLICY_VERS := $(PLATFORM_SEPOLICY_VERSION)
+endif
+
+ifeq ($(BOARD_SEPOLICY_VERS),$(PLATFORM_SEPOLICY_VERSION))
+IS_TARGET_MIXED_SEPOLICY :=
+else
+IS_TARGET_MIXED_SEPOLICY := true
+endif
+
+.KATI_READONLY := IS_TARGET_MIXED_SEPOLICY
+
 # A list of SEPolicy versions, besides PLATFORM_SEPOLICY_VERSION, that the framework supports.
 PLATFORM_SEPOLICY_COMPAT_VERSIONS := \
     28.0 \
diff --git a/core/envsetup.mk b/core/envsetup.mk
index b673050..8232907 100644
--- a/core/envsetup.mk
+++ b/core/envsetup.mk
@@ -317,11 +317,17 @@
 # Dumps all variables that match [A-Z][A-Z0-9_]* (with a few exceptions)
 # to the file at $(1). It is used to print only the variables that are
 # likely to be relevant to the product or board configuration.
+# Soong config variables are dumped as $(call soong_config_set) calls
+# instead of the raw variable values, because mk2rbc can't read the
+# raw ones.
 define dump-variables-rbc
 $(file >$(OUT_DIR)/dump-variables-rbc-temp.txt,$(subst $(space),$(newline),$(.VARIABLES)))\
 $(file >$(1),\
 $(foreach v, $(shell grep -he "^[A-Z][A-Z0-9_]*$$" $(OUT_DIR)/dump-variables-rbc-temp.txt | grep -vhE "^(SOONG_.*|LOCAL_PATH|TOPDIR|PRODUCT_COPY_OUT_.*)$$"),\
-$(v) := $(strip $($(v)))$(newline)))
+$(v) := $(strip $($(v)))$(newline))\
+$(foreach ns,$(SOONG_CONFIG_NAMESPACES),\
+$(foreach v,$(SOONG_CONFIG_$(ns)),\
+$$(call soong_config_set,$(ns),$(v),$(SOONG_CONFIG_$(ns)_$(v)))$(newline))))
 endef
 
 # Read the product specs so we can get TARGET_DEVICE and other
diff --git a/core/force_aapt2.mk b/core/force_aapt2.mk
index 25b45e4..5f3182a 100644
--- a/core/force_aapt2.mk
+++ b/core/force_aapt2.mk
@@ -44,10 +44,3 @@
   LOCAL_SDK_RES_VERSION := current
 endif
 
-ifeq (,$(strip $(LOCAL_MANIFEST_FILE)$(LOCAL_FULL_MANIFEST_FILE)))
-  ifeq (,$(wildcard $(LOCAL_PATH)/AndroidManifest.xml))
-    # work around missing manifests by creating a default one
-    LOCAL_FULL_MANIFEST_FILE := $(call local-intermediates-dir,COMMON)/DefaultManifest.xml
-    $(call create-default-manifest-file,$(LOCAL_FULL_MANIFEST_FILE),$(call module-min-sdk-version))
-  endif
-endif
diff --git a/core/main.mk b/core/main.mk
index f7cf8de..56007e2 100644
--- a/core/main.mk
+++ b/core/main.mk
@@ -240,7 +240,7 @@
 ADDITIONAL_VENDOR_PROPERTIES += ro.bionic.2nd_arch=$(TARGET_2ND_ARCH)
 ADDITIONAL_VENDOR_PROPERTIES += ro.bionic.2nd_cpu_variant=$(TARGET_2ND_CPU_VARIANT_RUNTIME)
 
-ADDITIONAL_VENDOR_PROPERTIES += persist.sys.dalvik.vm.lib.2=libart.so
+ADDITIONAL_VENDOR_PROPERTIES += persist.sys.dalvik.vm.lib.2=
 ADDITIONAL_VENDOR_PROPERTIES += dalvik.vm.isa.$(TARGET_ARCH).variant=$(DEX2OAT_TARGET_CPU_VARIANT_RUNTIME)
 ifneq ($(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES),)
   ADDITIONAL_VENDOR_PROPERTIES += dalvik.vm.isa.$(TARGET_ARCH).features=$(DEX2OAT_TARGET_INSTRUCTION_SET_FEATURES)
@@ -1603,6 +1603,9 @@
 .PHONY: bootimage
 bootimage: $(INSTALLED_BOOTIMAGE_TARGET)
 
+.PHONY: initbootimage
+bootimage: $(INSTALLED_INIT_BOOT_IMAGE_TARGET)
+
 ifeq (true,$(PRODUCT_EXPORT_BOOT_IMAGE_TO_DIST))
 $(call dist-for-goals, bootimage, $(INSTALLED_BOOTIMAGE_TARGET))
 endif
@@ -1629,6 +1632,7 @@
     $(INSTALLED_SYSTEMIMAGE_TARGET) \
     $(INSTALLED_RAMDISK_TARGET) \
     $(INSTALLED_BOOTIMAGE_TARGET) \
+    $(INSTALLED_INIT_BOOT_IMAGE_TARGET) \
     $(INSTALLED_RADIOIMAGE_TARGET) \
     $(INSTALLED_DEBUG_RAMDISK_TARGET) \
     $(INSTALLED_DEBUG_BOOTIMAGE_TARGET) \
@@ -1873,6 +1877,10 @@
     )
   endif
 
+  ifeq ($(PRODUCT_EXPORT_BOOT_IMAGE_TO_DIST),true)
+    $(call dist-for-goals, droidcore-unbundled, $(INSTALLED_BOOTIMAGE_TARGET))
+  endif
+
   ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
     $(call dist-for-goals, droidcore-unbundled, \
       $(recovery_ramdisk) \
diff --git a/core/package_internal.mk b/core/package_internal.mk
index 800dbbc..8199ad2 100644
--- a/core/package_internal.mk
+++ b/core/package_internal.mk
@@ -97,6 +97,14 @@
 endif
 
 include $(BUILD_SYSTEM)/force_aapt2.mk
+# validate that app contains a manifest file for aapt2
+ifeq (,$(strip $(LOCAL_MANIFEST_FILE)$(LOCAL_FULL_MANIFEST_FILE)))
+  ifeq (,$(wildcard $(LOCAL_PATH)/AndroidManifest.xml))
+    $(call pretty-error,App missing manifest file which is required by aapt2. \
+Provide a manifest file by either setting LOCAL_MANIFEST_FILE in Android.mk \
+or via a AndroidManifest.xml in this directory)
+  endif
+endif
 
 # Process Support Library dependencies.
 include $(BUILD_SYSTEM)/support_libraries.mk
diff --git a/core/product-graph.mk b/core/product-graph.mk
index f28ea3d..d425b22 100644
--- a/core/product-graph.mk
+++ b/core/product-graph.mk
@@ -126,6 +126,7 @@
 	$(hide) echo 'PRODUCT_CHARACTERISTICS=$(call get-product-var,$(1),PRODUCT_CHARACTERISTICS)' >> $$@
 	$(hide) echo 'PRODUCT_COPY_FILES=$(call get-product-var,$(1),PRODUCT_COPY_FILES)' >> $$@
 	$(hide) echo 'PRODUCT_OTA_PUBLIC_KEYS=$(call get-product-var,$(1),PRODUCT_OTA_PUBLIC_KEYS)' >> $$@
+	$(hide) echo 'PRODUCT_EXTRA_OTA_KEYS=$(call get-product-var,$(1),PRODUCT_EXTRA_OTA_KEYS)' >> $$@
 	$(hide) echo 'PRODUCT_EXTRA_RECOVERY_KEYS=$(call get-product-var,$(1),PRODUCT_EXTRA_RECOVERY_KEYS)' >> $$@
 	$(hide) echo 'PRODUCT_PACKAGE_OVERLAYS=$(call get-product-var,$(1),PRODUCT_PACKAGE_OVERLAYS)' >> $$@
 	$(hide) echo 'DEVICE_PACKAGE_OVERLAYS=$(call get-product-var,$(1),DEVICE_PACKAGE_OVERLAYS)' >> $$@
diff --git a/core/product.mk b/core/product.mk
index 31b1beb..7192226 100644
--- a/core/product.mk
+++ b/core/product.mk
@@ -183,6 +183,7 @@
 # signing tools can substitute them for the test key embedded by
 # default.
 _product_list_vars += PRODUCT_OTA_PUBLIC_KEYS
+_product_list_vars += PRODUCT_EXTRA_OTA_KEYS
 _product_list_vars += PRODUCT_EXTRA_RECOVERY_KEYS
 
 # Should we use the default resources or add any product specific overlays
@@ -395,6 +396,7 @@
 _product_single_value_vars += PRODUCT_BUILD_USERDATA_IMAGE
 _product_single_value_vars += PRODUCT_BUILD_RECOVERY_IMAGE
 _product_single_value_vars += PRODUCT_BUILD_BOOT_IMAGE
+_product_single_value_vars += PRODUCT_BUILD_INIT_BOOT_IMAGE
 _product_single_value_vars += PRODUCT_BUILD_DEBUG_BOOT_IMAGE
 _product_single_value_vars += PRODUCT_BUILD_VENDOR_BOOT_IMAGE
 _product_single_value_vars += PRODUCT_BUILD_DEBUG_VENDOR_BOOT_IMAGE
@@ -443,7 +445,7 @@
 
 # Install a copy of the debug policy to the system_ext partition, and allow
 # init-second-stage to load debug policy from system_ext.
-# This option is only meant to be set by GSI products.
+# This option is only meant to be set by compliance GSI targets.
 _product_single_value_vars += PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT
 
 # If set, metadata files for the following artifacts will be generated.
diff --git a/core/product_config.mk b/core/product_config.mk
index 0e969fe..6fae73e 100644
--- a/core/product_config.mk
+++ b/core/product_config.mk
@@ -381,6 +381,7 @@
 ENFORCE_SYSTEM_CERTIFICATE_ALLOW_LIST := $(PRODUCT_ARTIFACT_SYSTEM_CERTIFICATE_REQUIREMENT_ALLOW_LIST)
 
 PRODUCT_OTA_PUBLIC_KEYS := $(sort $(PRODUCT_OTA_PUBLIC_KEYS))
+PRODUCT_EXTRA_OTA_KEYS := $(sort $(PRODUCT_EXTRA_OTA_KEYS))
 PRODUCT_EXTRA_RECOVERY_KEYS := $(sort $(PRODUCT_EXTRA_RECOVERY_KEYS))
 
 # Resolve and setup per-module dex-preopt configs.
@@ -412,16 +413,22 @@
 _psmc_modules :=
 
 # Reset ADB keys for non-debuggable builds
-ifeq (,$(filter eng userdebug,$(TARGET_BUILD_VARIANT)),)
+ifeq (,$(filter eng userdebug,$(TARGET_BUILD_VARIANT)))
   PRODUCT_ADB_KEYS :=
 endif
 ifneq ($(filter-out 0 1,$(words $(PRODUCT_ADB_KEYS))),)
   $(error Only one file may be in PRODUCT_ADB_KEYS: $(PRODUCT_ADB_KEYS))
 endif
 
+# Show a warning wall of text if non-compliance-GSI products set this option.
 ifdef PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT
-  ifeq (,$(filter gsi_arm gsi_arm64 gsi_x86 gsi_x86_64,$(PRODUCT_NAME)))
-    $(error Only GSI products are allowed to set PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT)
+  ifeq (,$(filter gsi_arm gsi_arm64 gsi_x86 gsi_x86_64 gsi_car_arm64 gsi_car_x86_64,$(PRODUCT_NAME)))
+    $(warning PRODUCT_INSTALL_DEBUG_POLICY_TO_SYSTEM_EXT is set but \
+      PRODUCT_NAME ($(PRODUCT_NAME)) doesn't look like a GSI for compliance \
+      testing. This is a special configuration for compliance GSI, so do make \
+      sure you understand the security implications before setting this \
+      option. If you don't know what this option does, then you probably \
+      shouldn't set this.)
   endif
 endif
 
diff --git a/core/product_config.rbc b/core/product_config.rbc
index f26b428..1ccffcc 100644
--- a/core/product_config.rbc
+++ b/core/product_config.rbc
@@ -476,6 +476,12 @@
     return sorted(["%s/%s:%s/%s" % (
         from_dir, f, to_dir, f) for f in rblf_find_files(from_dir, pattern, only_files=1)])
 
+def _findstring(needle, haystack):
+    """Equivalent to GNU make's $(findstring)."""
+    if haystack.find(needle) < 0:
+        return ""
+    return needle
+
 def _filter_out(pattern, text):
     """Return all the words from `text' that do not match any word in `pattern'.
 
@@ -727,6 +733,7 @@
     filter = _filter,
     filter_out = _filter_out,
     find_and_copy = _find_and_copy,
+    findstring = _findstring,
     inherit = _inherit,
     indirect = _indirect,
     mk2rbc_error = _mk2rbc_error,
@@ -748,4 +755,5 @@
     setdefault = _setdefault,
     shell = rblf_shell,
     warning = _mkwarning,
+    words = __words,
 )
diff --git a/core/soong_config.mk b/core/soong_config.mk
index 617abdf..a8071a3 100644
--- a/core/soong_config.mk
+++ b/core/soong_config.mk
@@ -191,6 +191,10 @@
 
 $(call add_json_list, BoardPlatVendorPolicy,             $(BOARD_PLAT_VENDOR_POLICY))
 $(call add_json_list, BoardReqdMaskPolicy,               $(BOARD_REQD_MASK_POLICY))
+$(call add_json_list, BoardSystemExtPublicPrebuiltDirs,  $(BOARD_SYSTEM_EXT_PUBLIC_PREBUILT_DIRS))
+$(call add_json_list, BoardSystemExtPrivatePrebuiltDirs, $(BOARD_SYSTEM_EXT_PRIVATE_PREBUILT_DIRS))
+$(call add_json_list, BoardProductPublicPrebuiltDirs,    $(BOARD_PRODUCT_PUBLIC_PREBUILT_DIRS))
+$(call add_json_list, BoardProductPrivatePrebuiltDirs,   $(BOARD_PRODUCT_PRIVATE_PREBUILT_DIRS))
 $(call add_json_list, BoardVendorSepolicyDirs,           $(BOARD_VENDOR_SEPOLICY_DIRS) $(BOARD_SEPOLICY_DIRS))
 $(call add_json_list, BoardOdmSepolicyDirs,              $(BOARD_ODM_SEPOLICY_DIRS))
 $(call add_json_list, BoardVendorDlkmSepolicyDirs,       $(BOARD_VENDOR_DLKM_SEPOLICY_DIRS))
@@ -203,6 +207,7 @@
 
 $(call add_json_str,  PlatformSepolicyVersion,           $(PLATFORM_SEPOLICY_VERSION))
 $(call add_json_str,  TotSepolicyVersion,                $(TOT_SEPOLICY_VERSION))
+$(call add_json_list, PlatformSepolicyCompatVersions,    $(PLATFORM_SEPOLICY_COMPAT_VERSIONS))
 
 $(call add_json_bool, Flatten_apex,                      $(filter true,$(TARGET_FLATTEN_APEX)))
 $(call add_json_bool, ForceApexSymlinkOptimization,      $(filter true,$(TARGET_FORCE_APEX_SYMLINK_OPTIMIZATION)))
diff --git a/core/static_java_library.mk b/core/static_java_library.mk
index 7a87322..4053985 100644
--- a/core/static_java_library.mk
+++ b/core/static_java_library.mk
@@ -101,6 +101,13 @@
 include $(BUILD_SYSTEM)/java_renderscript.mk
 
 ifeq (true,$(need_compile_res))
+# work around missing manifests by creating a default one
+ifeq (,$(strip $(LOCAL_MANIFEST_FILE)$(LOCAL_FULL_MANIFEST_FILE)))
+  ifeq (,$(wildcard $(LOCAL_PATH)/AndroidManifest.xml))
+    LOCAL_FULL_MANIFEST_FILE := $(call local-intermediates-dir,COMMON)/DefaultManifest.xml
+    $(call create-default-manifest-file,$(LOCAL_FULL_MANIFEST_FILE),$(call module-min-sdk-version))
+  endif
+endif
 include $(BUILD_SYSTEM)/android_manifest.mk
 
 LOCAL_SDK_RES_VERSION:=$(strip $(LOCAL_SDK_RES_VERSION))
diff --git a/core/tasks/module-info.mk b/core/tasks/module-info.mk
index 5d5bfa8..aeeb403 100644
--- a/core/tasks/module-info.mk
+++ b/core/tasks/module-info.mk
@@ -23,6 +23,7 @@
 			'"classes_jar": [$(foreach w,$(sort $(ALL_MODULES.$(m).CLASSES_JAR)),"$(w)", )], ' \
 			'"test_mainline_modules": [$(foreach w,$(sort $(ALL_MODULES.$(m).TEST_MAINLINE_MODULES)),"$(w)", )], ' \
 			'"is_unit_test": "$(ALL_MODULES.$(m).IS_UNIT_TEST)", ' \
+			'"data": [$(foreach w,$(sort $(ALL_MODULES.$(m).TEST_DATA)),"$(w)", )], ' \
 			'},\n' \
 	 ) | sed -e 's/, *\]/]/g' -e 's/, *\}/ }/g' -e '$$s/,$$//' >> $@
 	$(hide) echo '}' >> $@
diff --git a/core/version_defaults.mk b/core/version_defaults.mk
index 1f1ec3f..051de62 100644
--- a/core/version_defaults.mk
+++ b/core/version_defaults.mk
@@ -85,7 +85,7 @@
     #  It must be of the form "YYYY-MM-DD" on production devices.
     #  It must match one of the Android Security Patch Level strings of the Public Security Bulletins.
     #  If there is no $PLATFORM_SECURITY_PATCH set, keep it empty.
-      PLATFORM_SECURITY_PATCH := 2021-11-05
+      PLATFORM_SECURITY_PATCH := 2022-01-05
 endif
 .KATI_READONLY := PLATFORM_SECURITY_PATCH
 
diff --git a/target/board/BoardConfigGkiCommon.mk b/target/board/BoardConfigGkiCommon.mk
index c0f5db9..63ef2b4 100644
--- a/target/board/BoardConfigGkiCommon.mk
+++ b/target/board/BoardConfigGkiCommon.mk
@@ -16,11 +16,7 @@
 # Enable GKI 2.0 signing.
 BOARD_GKI_SIGNING_KEY_PATH := build/make/target/product/gsi/testkey_rsa2048.pem
 BOARD_GKI_SIGNING_ALGORITHM := SHA256_RSA2048
-
-# The following is needed to allow release signing process appends more extra
-# args, e.g., passing --signing_helper_with_files from mkbootimg to avbtool.
-# See b/178559811 for more details.
-BOARD_GKI_SIGNING_SIGNATURE_ARGS := --prop foo:bar
+BOARD_GKI_SIGNING_SIGNATURE_ARGS :=
 
 # Sets boot SPL.
 BOOT_SECURITY_PATCH = $(PLATFORM_SECURITY_PATCH)
diff --git a/target/product/OWNERS b/target/product/OWNERS
index 82e6e88..b3d8998 100644
--- a/target/product/OWNERS
+++ b/target/product/OWNERS
@@ -2,4 +2,4 @@
 
 # GSI
 per-file gsi_release.mk = file:/target/product/gsi/OWNERS
-per-file gsi_keys.mk = file:/target/product/gsi/OWNERS
+per-file developer_gsi_keys.mk = file:/target/product/gsi/OWNERS
diff --git a/target/product/base_system.mk b/target/product/base_system.mk
index a995b8b..3d299fb 100644
--- a/target/product/base_system.mk
+++ b/target/product/base_system.mk
@@ -66,6 +66,7 @@
     com.android.neuralnetworks \
     com.android.scheduling \
     com.android.sdkext \
+    com.android.sepolicy \
     com.android.tethering \
     com.android.tzdata \
     com.android.wifi \
@@ -367,6 +368,7 @@
 PRODUCT_PACKAGES_DEBUG := \
     adb_keys \
     arping \
+    com.android.sepolicy.cert-debug.der \
     dmuserd \
     idlcli \
     init-debug.rc \
diff --git a/target/product/default_art_config.mk b/target/product/default_art_config.mk
index bac9da4..3223002 100644
--- a/target/product/default_art_config.mk
+++ b/target/product/default_art_config.mk
@@ -49,6 +49,7 @@
 
 # APEX boot jars. Keep the list sorted by module names and then library names.
 # Note: core-icu4j is moved back to PRODUCT_BOOT_JARS in product_config.mk at a later stage.
+# Note: For modules available in Q, DO NOT add new entries here.
 PRODUCT_APEX_BOOT_JARS := \
     com.android.appsearch:framework-appsearch \
     com.android.conscrypt:conscrypt \
@@ -68,6 +69,7 @@
 
 # List of system_server classpath jars delivered via apex.
 # Keep the list sorted by module names and then library names.
+# Note: For modules available in Q, DO NOT add new entries here.
 PRODUCT_APEX_SYSTEM_SERVER_JARS := \
     com.android.appsearch:service-appsearch \
     com.android.art:service-art \
@@ -82,6 +84,7 @@
 
 # List of jars delivered via apex that system_server loads dynamically using separate classloaders.
 # Keep the list sorted by module names and then library names.
+# Note: For modules available in Q, DO NOT add new entries here.
 PRODUCT_APEX_STANDALONE_SYSTEM_SERVER_JARS := \
     com.android.os.statsd:service-statsd \
     com.android.scheduling:service-scheduling \
diff --git a/target/product/gsi/Android.mk b/target/product/gsi/Android.mk
index 0d788fa..167ffcf 100644
--- a/target/product/gsi/Android.mk
+++ b/target/product/gsi/Android.mk
@@ -60,11 +60,11 @@
 endif
 
 $(check-vndk-list-timestamp): $(INTERNAL_VNDK_LIB_LIST) $(LATEST_VNDK_LIB_LIST) $(HOST_OUT_EXECUTABLES)/update-vndk-list.sh
-	$(hide) ($(_READ_INTERNAL_VNDK_LIB_LIST) | \
+	$(hide) ($(_READ_INTERNAL_VNDK_LIB_LIST) | sort | \
 	diff --old-line-format="Removed %L" \
 	  --new-line-format="Added %L" \
 	  --unchanged-line-format="" \
-	  $(LATEST_VNDK_LIB_LIST) - \
+	  <(cat $(LATEST_VNDK_LIB_LIST) | sort) - \
 	  || ( echo -e $(_vndk_check_failure_message); exit 1 ))
 	$(hide) mkdir -p $(dir $@)
 	$(hide) touch $@
diff --git a/target/product/gsi/current.txt b/target/product/gsi/current.txt
index 52754d4..3cad6f1 100644
--- a/target/product/gsi/current.txt
+++ b/target/product/gsi/current.txt
@@ -29,10 +29,10 @@
 VNDK-SP: android.hardware.graphics.mapper@3.0.so
 VNDK-SP: android.hardware.graphics.mapper@4.0.so
 VNDK-SP: android.hardware.renderscript@1.0.so
+VNDK-SP: android.hidl.safe_union@1.0.so
 VNDK-SP: android.hidl.memory.token@1.0.so
 VNDK-SP: android.hidl.memory@1.0-impl.so
 VNDK-SP: android.hidl.memory@1.0.so
-VNDK-SP: android.hidl.safe_union@1.0.so
 VNDK-SP: libRSCpuRef.so
 VNDK-SP: libRSDriver.so
 VNDK-SP: libRS_internal.so
@@ -56,9 +56,11 @@
 VNDK-SP: libutils.so
 VNDK-SP: libutilscallstack.so
 VNDK-SP: libz.so
+VNDK-core: android.hardware.audio.common-V1-ndk.so
 VNDK-core: android.hardware.audio.common@2.0.so
 VNDK-core: android.hardware.authsecret-V1-ndk.so
 VNDK-core: android.hardware.automotive.occupant_awareness-V1-ndk.so
+VNDK-core: android.hardware.bluetooth.audio-V1-ndk.so
 VNDK-core: android.hardware.configstore-utils.so
 VNDK-core: android.hardware.configstore@1.0.so
 VNDK-core: android.hardware.configstore@1.1.so
@@ -92,6 +94,7 @@
 VNDK-core: android.hardware.radio.sim-V1-ndk.so
 VNDK-core: android.hardware.radio.voice-V1-ndk.so
 VNDK-core: android.hardware.rebootescrow-V1-ndk.so
+VNDK-core: android.hardware.security.dice-V1-ndk.so
 VNDK-core: android.hardware.security.keymint-V1-ndk.so
 VNDK-core: android.hardware.security.secureclock-V1-ndk.so
 VNDK-core: android.hardware.security.sharedsecret-V1-ndk.so
@@ -100,8 +103,10 @@
 VNDK-core: android.hardware.vibrator-V2-ndk.so
 VNDK-core: android.hardware.weaver-V1-ndk.so
 VNDK-core: android.hardware.wifi.hostapd-V1-ndk.so
+VNDK-core: android.hardware.wifi.supplicant-V1-ndk.so
 VNDK-core: android.hidl.token@1.0-utils.so
 VNDK-core: android.hidl.token@1.0.so
+VNDK-core: android.media.audio.common.types-V1-ndk.so
 VNDK-core: android.system.keystore2-V1-ndk.so
 VNDK-core: android.system.suspend-V1-ndk.so
 VNDK-core: android.system.suspend@1.0.so
diff --git a/target/product/gsi_keys.mk b/target/product/gsi_keys.mk
deleted file mode 100644
index 5a814db..0000000
--- a/target/product/gsi_keys.mk
+++ /dev/null
@@ -1,22 +0,0 @@
-#
-# Copyright (C) 2019 The Android Open-Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-
-# Include GSI keys into first-stage ramdisk, so we can enable verified
-# boot when booting a GSI.
-PRODUCT_PACKAGES += \
-    q-gsi.avbpubkey \
-    r-gsi.avbpubkey \
-    s-gsi.avbpubkey \
diff --git a/target/product/gsi_release.mk b/target/product/gsi_release.mk
index aaeefcb..b1266ee 100644
--- a/target/product/gsi_release.mk
+++ b/target/product/gsi_release.mk
@@ -34,7 +34,7 @@
 # GSI should always support up-to-date platform features.
 # Keep this value at the latest API level to ensure latest build system
 # default configs are applied.
-PRODUCT_SHIPPING_API_LEVEL := 30
+PRODUCT_SHIPPING_API_LEVEL := 31
 
 # Enable dynamic partitions to facilitate mixing onto Cuttlefish
 PRODUCT_USE_DYNAMIC_PARTITIONS := true
diff --git a/target/product/handheld_system.mk b/target/product/handheld_system.mk
index b7a2d0d..3a59f6c 100644
--- a/target/product/handheld_system.mk
+++ b/target/product/handheld_system.mk
@@ -43,7 +43,6 @@
     CameraExtensionsProxy \
     CaptivePortalLogin \
     CertInstaller \
-    clatd \
     DocumentsUI \
     DownloadProviderUi \
     EasterEgg \
diff --git a/target/product/security/Android.mk b/target/product/security/Android.mk
index cedad5b..9daa3bf 100644
--- a/target/product/security/Android.mk
+++ b/target/product/security/Android.mk
@@ -63,9 +63,17 @@
 LOCAL_MODULE_STEM := otacerts.zip
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/security
 include $(BUILD_SYSTEM)/base_rules.mk
+
+extra_ota_keys := $(addsuffix .x509.pem,$(PRODUCT_EXTRA_OTA_KEYS))
+
 $(LOCAL_BUILT_MODULE): PRIVATE_CERT := $(DEFAULT_SYSTEM_DEV_CERTIFICATE).x509.pem
-$(LOCAL_BUILT_MODULE): $(SOONG_ZIP) $(DEFAULT_SYSTEM_DEV_CERTIFICATE).x509.pem
-	$(SOONG_ZIP) -o $@ -j -symlinks=false -f $(PRIVATE_CERT)
+$(LOCAL_BUILT_MODULE): PRIVATE_EXTRA_OTA_KEYS := $(extra_ota_keys)
+$(LOCAL_BUILT_MODULE): \
+	    $(SOONG_ZIP) \
+	    $(DEFAULT_SYSTEM_DEV_CERTIFICATE).x509.pem \
+	    $(extra_ota_keys)
+	$(SOONG_ZIP) -o $@ -j -symlinks=false \
+	    $(addprefix -f ,$(PRIVATE_CERT) $(PRIVATE_EXTRA_OTA_KEYS))
 
 
 #######################################
@@ -80,7 +88,7 @@
 LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/system/etc/security
 include $(BUILD_SYSTEM)/base_rules.mk
 
-extra_recovery_keys := $(patsubst %,%.x509.pem,$(PRODUCT_EXTRA_RECOVERY_KEYS))
+extra_recovery_keys := $(addsuffix .x509.pem,$(PRODUCT_EXTRA_RECOVERY_KEYS))
 
 $(LOCAL_BUILT_MODULE): PRIVATE_CERT := $(DEFAULT_SYSTEM_DEV_CERTIFICATE).x509.pem
 $(LOCAL_BUILT_MODULE): PRIVATE_EXTRA_RECOVERY_KEYS := $(extra_recovery_keys)
@@ -89,4 +97,4 @@
 	    $(DEFAULT_SYSTEM_DEV_CERTIFICATE).x509.pem \
 	    $(extra_recovery_keys)
 	$(SOONG_ZIP) -o $@ -j -symlinks=false \
-	    $(foreach key_file, $(PRIVATE_CERT) $(PRIVATE_EXTRA_RECOVERY_KEYS), -f $(key_file))
+	    $(addprefix -f ,$(PRIVATE_CERT) $(PRIVATE_EXTRA_RECOVERY_KEYS))
diff --git a/tools/Android.bp b/tools/Android.bp
index 269e610..2f3b393 100644
--- a/tools/Android.bp
+++ b/tools/Android.bp
@@ -28,27 +28,11 @@
 python_binary_host {
   name: "generate-self-extracting-archive",
   srcs: ["generate-self-extracting-archive.py"],
-  version: {
-    py2: {
-      enabled: true,
-    },
-    py3: {
-      enabled: false,
-    },
-  },
 }
 
 python_binary_host {
   name: "post_process_props",
   srcs: ["post_process_props.py"],
-  version: {
-    py2: {
-      enabled: false,
-    },
-    py3: {
-      enabled: true,
-    },
-  },
 }
 
 python_test_host {
@@ -58,14 +42,6 @@
     "post_process_props.py",
     "test_post_process_props.py",
   ],
-  version: {
-    py2: {
-      enabled: false,
-    },
-    py3: {
-      enabled: true,
-    },
-  },
   test_config: "post_process_props_unittest.xml",
   test_suites: ["general-tests"],
 }
@@ -73,14 +49,6 @@
 python_binary_host {
   name: "extract_kernel",
   srcs: ["extract_kernel.py"],
-  version: {
-    py2: {
-      enabled: false,
-    },
-    py3: {
-      enabled: true,
-    },
-  },
 }
 
 genrule_defaults {
diff --git a/tools/BUILD.bazel b/tools/BUILD.bazel
index 75b0de6..3170820 100644
--- a/tools/BUILD.bazel
+++ b/tools/BUILD.bazel
@@ -8,7 +8,7 @@
     srcs=["java-event-log-tags.py"],
     deps=[":event_log_tags"],
     visibility = ["//visibility:public"],
-    python_version = "PY2",
+    python_version = "PY3",
 )
 
 py_binary(
@@ -16,5 +16,5 @@
     srcs=["merge-event-log-tags.py"],
     deps=[":event_log_tags"],
     visibility = ["//visibility:public"],
-    python_version = "PY2",
+    python_version = "PY3",
 )
diff --git a/tools/compliance/Android.bp b/tools/compliance/Android.bp
index afb3080..bbeb76f 100644
--- a/tools/compliance/Android.bp
+++ b/tools/compliance/Android.bp
@@ -48,7 +48,6 @@
 bootstrap_go_package {
     name: "compliance-module",
     srcs: [
-        "actionset.go",
         "condition.go",
         "conditionset.go",
         "doc.go",
diff --git a/tools/compliance/actionset.go b/tools/compliance/actionset.go
deleted file mode 100644
index 656c5de..0000000
--- a/tools/compliance/actionset.go
+++ /dev/null
@@ -1,119 +0,0 @@
-// Copyright 2021 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package compliance
-
-import (
-	"fmt"
-	"sort"
-	"strings"
-)
-
-// actionSet maps `actOn` target nodes to the license conditions the actions resolve.
-type actionSet map[*TargetNode]*LicenseConditionSet
-
-// String returns a string representation of the set.
-func (as actionSet) String() string {
-	var sb strings.Builder
-	fmt.Fprintf(&sb, "{")
-	osep := ""
-	for actsOn, cs := range as {
-		cl := cs.AsList()
-		sort.Sort(cl)
-		fmt.Fprintf(&sb, "%s%s -> %s", osep, actsOn.name, cl.String())
-		osep = ", "
-	}
-	fmt.Fprintf(&sb, "}")
-	return sb.String()
-}
-
-// byName returns the subset of `as` actions where the condition name is in `names`.
-func (as actionSet) byName(names ConditionNames) actionSet {
-	result := make(actionSet)
-	for actsOn, cs := range as {
-		bn := cs.ByName(names)
-		if bn.IsEmpty() {
-			continue
-		}
-		result[actsOn] = bn
-	}
-	return result
-}
-
-// byActsOn returns the subset of `as` where `actsOn` is in the `reachable` target node set.
-func (as actionSet) byActsOn(reachable *TargetNodeSet) actionSet {
-	result := make(actionSet)
-	for actsOn, cs := range as {
-		if !reachable.Contains(actsOn) || cs.IsEmpty() {
-			continue
-		}
-		result[actsOn] = cs.Copy()
-	}
-	return result
-}
-
-// copy returns another actionSet with the same value as `as`
-func (as actionSet) copy() actionSet {
-	result := make(actionSet)
-	for actsOn, cs := range as {
-		if cs.IsEmpty() {
-			continue
-		}
-		result[actsOn] = cs.Copy()
-	}
-	return result
-}
-
-// addSet adds all of the actions of `other` if not already present.
-func (as actionSet) addSet(other actionSet) {
-	for actsOn, cs := range other {
-		as.add(actsOn, cs)
-	}
-}
-
-// add makes the action on `actsOn` to resolve the conditions in `cs` a member of the set.
-func (as actionSet) add(actsOn *TargetNode, cs *LicenseConditionSet) {
-	if acs, ok := as[actsOn]; ok {
-		acs.AddSet(cs)
-	} else {
-		as[actsOn] = cs.Copy()
-	}
-}
-
-// addCondition makes the action on `actsOn` to resolve `lc` a member of the set.
-func (as actionSet) addCondition(actsOn *TargetNode, lc LicenseCondition) {
-	if _, ok := as[actsOn]; !ok {
-		as[actsOn] = newLicenseConditionSet()
-	}
-	as[actsOn].Add(lc)
-}
-
-// isEmpty returns true if no action to resolve a condition exists.
-func (as actionSet) isEmpty() bool {
-	for _, cs := range as {
-		if !cs.IsEmpty() {
-			return false
-		}
-	}
-	return true
-}
-
-// conditions returns the set of conditions resolved by the action set.
-func (as actionSet) conditions() *LicenseConditionSet {
-	result := newLicenseConditionSet()
-	for _, cs := range as {
-		result.AddSet(cs)
-	}
-	return result
-}
diff --git a/tools/compliance/cmd/checkshare.go b/tools/compliance/cmd/checkshare.go
index efac8dc..5114a28 100644
--- a/tools/compliance/cmd/checkshare.go
+++ b/tools/compliance/cmd/checkshare.go
@@ -30,8 +30,8 @@
 
 Reports on stderr any targets where policy says that the source both
 must and must not be shared. The error report indicates the target, the
-license condition with origin that has a source privacy policy, and the
-license condition with origin that has a source sharing policy.
+license condition that has a source privacy policy, and the license
+condition that has a source sharing policy.
 
 Any given target may appear multiple times with different combinations
 of conflicting license conditions.
diff --git a/tools/compliance/cmd/checkshare_test.go b/tools/compliance/cmd/checkshare_test.go
index 8ea7748..5036aa5 100644
--- a/tools/compliance/cmd/checkshare_test.go
+++ b/tools/compliance/cmd/checkshare_test.go
@@ -23,15 +23,12 @@
 
 type outcome struct {
 	target           string
-	privacyOrigin    string
 	privacyCondition string
-	shareOrigin      string
 	shareCondition   string
 }
 
 func (o *outcome) String() string {
-	return fmt.Sprintf("%s %s from %s and must share from %s %s",
-		o.target, o.privacyCondition, o.privacyOrigin, o.shareCondition, o.shareOrigin)
+	return fmt.Sprintf("%s %s and must share from %s", o.target, o.privacyCondition, o.shareCondition)
 }
 
 type outcomeList []*outcome
@@ -180,9 +177,7 @@
 			expectedOutcomes: outcomeList{
 				&outcome{
 					target:           "testdata/proprietary/bin/bin2.meta_lic",
-					privacyOrigin:    "testdata/proprietary/bin/bin2.meta_lic",
 					privacyCondition: "proprietary",
-					shareOrigin:      "testdata/proprietary/lib/libb.so.meta_lic",
 					shareCondition:   "restricted",
 				},
 			},
@@ -195,9 +190,7 @@
 			expectedOutcomes: outcomeList{
 				&outcome{
 					target:           "testdata/proprietary/bin/bin2.meta_lic",
-					privacyOrigin:    "testdata/proprietary/bin/bin2.meta_lic",
 					privacyCondition: "proprietary",
-					shareOrigin:      "testdata/proprietary/lib/libb.so.meta_lic",
 					shareCondition:   "restricted",
 				},
 			},
@@ -210,9 +203,7 @@
 			expectedOutcomes: outcomeList{
 				&outcome{
 					target:           "testdata/proprietary/lib/liba.so.meta_lic",
-					privacyOrigin:    "testdata/proprietary/lib/liba.so.meta_lic",
 					privacyCondition: "proprietary",
-					shareOrigin:      "testdata/proprietary/lib/libb.so.meta_lic",
 					shareCondition:   "restricted",
 				},
 			},
@@ -225,9 +216,7 @@
 			expectedOutcomes: outcomeList{
 				&outcome{
 					target:           "testdata/proprietary/bin/bin2.meta_lic",
-					privacyOrigin:    "testdata/proprietary/bin/bin2.meta_lic",
 					privacyCondition: "proprietary",
-					shareOrigin:      "testdata/proprietary/lib/libb.so.meta_lic",
 					shareCondition:   "restricted",
 				},
 			},
@@ -277,10 +266,8 @@
 				cFields := strings.Split(ts, " ")
 				actualOutcomes = append(actualOutcomes, &outcome{
 					target:           cFields[0],
-					privacyOrigin:    cFields[3],
 					privacyCondition: cFields[1],
-					shareOrigin:      cFields[9],
-					shareCondition:   cFields[8],
+					shareCondition:   cFields[6],
 				})
 			}
 			if len(actualOutcomes) != len(tt.expectedOutcomes) {
diff --git a/tools/compliance/cmd/dumpgraph_test.go b/tools/compliance/cmd/dumpgraph_test.go
index b7d66f7..3055022 100644
--- a/tools/compliance/cmd/dumpgraph_test.go
+++ b/tools/compliance/cmd/dumpgraph_test.go
@@ -327,13 +327,13 @@
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx:       context{stripPrefix: "testdata/restricted/", labelConditions: true},
 			expectedOut: []string{
-				"bin/bin1.meta_lic:notice lib/liba.so.meta_lic:restricted static",
+				"bin/bin1.meta_lic:notice lib/liba.so.meta_lic:restricted_allows_dynamic_linking static",
 				"bin/bin1.meta_lic:notice lib/libc.a.meta_lic:reciprocal static",
 				"bin/bin2.meta_lic:notice lib/libb.so.meta_lic:restricted dynamic",
 				"bin/bin2.meta_lic:notice lib/libd.so.meta_lic:notice dynamic",
 				"highest.apex.meta_lic:notice bin/bin1.meta_lic:notice static",
 				"highest.apex.meta_lic:notice bin/bin2.meta_lic:notice static",
-				"highest.apex.meta_lic:notice lib/liba.so.meta_lic:restricted static",
+				"highest.apex.meta_lic:notice lib/liba.so.meta_lic:restricted_allows_dynamic_linking static",
 				"highest.apex.meta_lic:notice lib/libb.so.meta_lic:restricted static",
 			},
 		},
@@ -996,7 +996,7 @@
 				matchTarget("bin/bin1.meta_lic", "notice"),
 				matchTarget("bin/bin2.meta_lic", "notice"),
 				matchTarget("highest.apex.meta_lic", "notice"),
-				matchTarget("lib/liba.so.meta_lic", "restricted"),
+				matchTarget("lib/liba.so.meta_lic", "restricted_allows_dynamic_linking"),
 				matchTarget("lib/libb.so.meta_lic", "restricted"),
 				matchTarget("lib/libc.a.meta_lic", "reciprocal"),
 				matchTarget("lib/libd.so.meta_lic", "notice"),
diff --git a/tools/compliance/cmd/dumpresolutions.go b/tools/compliance/cmd/dumpresolutions.go
index 36fbb7d..318cd91 100644
--- a/tools/compliance/cmd/dumpresolutions.go
+++ b/tools/compliance/cmd/dumpresolutions.go
@@ -36,7 +36,7 @@
 )
 
 type context struct {
-	conditions      []string
+	conditions      []compliance.LicenseCondition
 	graphViz        bool
 	labelConditions bool
 	stripPrefix     string
@@ -50,9 +50,9 @@
 resolution in the graph. When -dot flag given, outputs nodes and edges
 in graphviz directed graph format.
 
-If one or more '-c condition' conditions are given, outputs the joined
-set of resolutions for all of the conditions. Otherwise, outputs the
-result of the bottom-up and top-down resolve only.
+If one or more '-c condition' conditions are given, outputs the
+resolution for the union of the conditions. Otherwise, outputs the
+resolution for all conditions.
 
 In plain text mode, when '-label_conditions' is requested, the Target
 and Origin have colon-separated license conditions appended:
@@ -86,13 +86,17 @@
 		os.Exit(2)
 	}
 
+	lcs := make([]compliance.LicenseCondition, 0, len(*conditions))
+	for _, name := range *conditions {
+		lcs = append(lcs, compliance.RecognizedConditionNames[name])
+	}
 	ctx := &context{
-		conditions:      append([]string{}, *conditions...),
+		conditions:      lcs,
 		graphViz:        *graphViz,
 		labelConditions: *labelConditions,
 		stripPrefix:     *stripPrefix,
 	}
-	err := dumpResolutions(ctx, os.Stdout, os.Stderr, flag.Args()...)
+	_, err := dumpResolutions(ctx, os.Stdout, os.Stderr, flag.Args()...)
 	if err != nil {
 		if err == failNoneRequested {
 			flag.Usage()
@@ -104,36 +108,31 @@
 }
 
 // dumpResolutions implements the dumpresolutions utility.
-func dumpResolutions(ctx *context, stdout, stderr io.Writer, files ...string) error {
+func dumpResolutions(ctx *context, stdout, stderr io.Writer, files ...string) (*compliance.LicenseGraph, error) {
 	if len(files) < 1 {
-		return failNoneRequested
+		return nil, failNoneRequested
 	}
 
 	// Read the license graph from the license metadata files (*.meta_lic).
 	licenseGraph, err := compliance.ReadLicenseGraph(os.DirFS("."), stderr, files)
 	if err != nil {
-		return fmt.Errorf("Unable to read license metadata file(s) %q: %v\n", files, err)
+		return nil, fmt.Errorf("Unable to read license metadata file(s) %q: %v\n", files, err)
 	}
 	if licenseGraph == nil {
-		return failNoLicenses
+		return nil, failNoLicenses
 	}
 
-	// resolutions will contain the requested set of resolutions.
-	var resolutions *compliance.ResolutionSet
-
-	resolutions = compliance.ResolveTopDownConditions(licenseGraph)
+	compliance.ResolveTopDownConditions(licenseGraph)
+	cs := compliance.AllLicenseConditions
 	if len(ctx.conditions) > 0 {
-		rlist := make([]*compliance.ResolutionSet, 0, len(ctx.conditions))
+		cs = compliance.NewLicenseConditionSet()
 		for _, c := range ctx.conditions {
-			rlist = append(rlist, compliance.WalkResolutionsForCondition(licenseGraph, resolutions, compliance.ConditionNames{c}))
-		}
-		if len(rlist) == 1 {
-			resolutions = rlist[0]
-		} else {
-			resolutions = compliance.JoinResolutionSets(rlist...)
+			cs = cs.Plus(c)
 		}
 	}
 
+	resolutions := compliance.WalkResolutionsForCondition(licenseGraph, cs)
+
 	// nodes maps license metadata file names to graphViz node names when graphViz requested.
 	nodes := make(map[string]string)
 	n := 0
@@ -142,11 +141,7 @@
 	targetOut := func(target *compliance.TargetNode, sep string) string {
 		tOut := strings.TrimPrefix(target.Name(), ctx.stripPrefix)
 		if ctx.labelConditions {
-			conditions := make([]string, 0, target.LicenseConditions().Count())
-			for _, lc := range target.LicenseConditions().AsList() {
-				conditions = append(conditions, lc.Name())
-			}
-			sort.Strings(conditions)
+			conditions := target.LicenseConditions().Names()
 			if len(conditions) > 0 {
 				tOut += sep + strings.Join(conditions, sep)
 			}
@@ -168,26 +163,16 @@
 	// outputResolution prints a resolution in the requested format to `stdout`, where one can read
 	// a resolution as `tname` resolves `oname`'s conditions named in `cnames`.
 	// `tname` is the name of the target the resolution applies to.
-	// `oname` is the name of the target where the conditions originate.
 	// `cnames` is the list of conditions to resolve.
-	outputResolution := func(tname, aname, oname string, cnames []string) {
+	outputResolution := func(tname, aname string, cnames []string) {
 		if ctx.graphViz {
 			// ... one edge per line labelled with \\n-separated annotations.
 			tNode := nodes[tname]
 			aNode := nodes[aname]
-			oNode := nodes[oname]
-			fmt.Fprintf(stdout, "\t%s -> %s; %s -> %s [label=\"%s\"];\n", tNode, aNode, aNode, oNode, strings.Join(cnames, "\\n"))
+			fmt.Fprintf(stdout, "\t%s -> %s [label=\"%s\"];\n", tNode, aNode, strings.Join(cnames, "\\n"))
 		} else {
 			// ... one edge per line with names in a colon-separated tuple.
-			fmt.Fprintf(stdout, "%s %s %s %s\n", tname, aname, oname, strings.Join(cnames, ":"))
-		}
-	}
-
-	// outputSingleton prints `tname` to plain text in the unexpected event that `tname` is the name of
-	// a target in `resolutions.AppliesTo()` but has no conditions to resolve.
-	outputSingleton := func(tname, aname string) {
-		if !ctx.graphViz {
-			fmt.Fprintf(stdout, "%s %s\n", tname, aname)
+			fmt.Fprintf(stdout, "%s %s %s\n", tname, aname, strings.Join(cnames, ":"))
 		}
 	}
 
@@ -200,16 +185,11 @@
 		fmt.Fprintf(stdout, "strict digraph {\n\trankdir=LR;\n")
 		for _, target := range targets {
 			makeNode(target)
-			rl := compliance.ResolutionList(resolutions.Resolutions(target))
+			rl := resolutions.Resolutions(target)
 			sort.Sort(rl)
 			for _, r := range rl {
 				makeNode(r.ActsOn())
 			}
-			conditions := rl.AllConditions().AsList()
-			sort.Sort(conditions)
-			for _, lc := range conditions {
-				makeNode(lc.Origin())
-			}
 		}
 	}
 
@@ -222,7 +202,7 @@
 			tname = targetOut(target, ":")
 		}
 
-		rl := compliance.ResolutionList(resolutions.Resolutions(target))
+		rl := resolutions.Resolutions(target)
 		sort.Sort(rl)
 		for _, r := range rl {
 			var aname string
@@ -232,38 +212,11 @@
 				aname = targetOut(r.ActsOn(), ":")
 			}
 
-			conditions := r.Resolves().AsList()
-			sort.Sort(conditions)
-
-			// poname is the previous origin name or "" if no previous
-			poname := ""
-
 			// cnames accumulates the list of condition names originating at a single origin that apply to `target`.
-			cnames := make([]string, 0, len(conditions))
+			cnames := r.Resolves().Names()
 
-			// Output 1 line for each attachesTo+actsOn+origin combination.
-			for _, condition := range conditions {
-				var oname string
-				if ctx.graphViz {
-					oname = condition.Origin().Name()
-				} else {
-					oname = targetOut(condition.Origin(), ":")
-				}
-
-				// Detect when origin changes and output prior origin's conditions.
-				if poname != oname && poname != "" {
-					outputResolution(tname, aname, poname, cnames)
-					cnames = cnames[:0]
-				}
-				poname = oname
-				cnames = append(cnames, condition.Name())
-			}
-			// Output last origin's conditions or a singleton if no origins.
-			if poname == "" {
-				outputSingleton(tname, aname)
-			} else {
-				outputResolution(tname, aname, poname, cnames)
-			}
+			// Output 1 line for each attachesTo+actsOn combination.
+			outputResolution(tname, aname, cnames)
 		}
 	}
 	// If graphViz output, rank the root nodes together, and complete the directed graph.
@@ -280,5 +233,5 @@
 		}
 		fmt.Fprintf(stdout, "}\n}\n")
 	}
-	return nil
+	return licenseGraph, nil
 }
diff --git a/tools/compliance/cmd/dumpresolutions_test.go b/tools/compliance/cmd/dumpresolutions_test.go
index cab1cc8..d904671 100644
--- a/tools/compliance/cmd/dumpresolutions_test.go
+++ b/tools/compliance/cmd/dumpresolutions_test.go
@@ -16,6 +16,7 @@
 
 import (
 	"bytes"
+	"compliance"
 	"fmt"
 	"strings"
 	"testing"
@@ -34,20 +35,18 @@
 			name:      "apex",
 			roots:     []string{"highest.apex.meta_lic"},
 			expectedOut: []string{
-				"testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/bin/bin1.meta_lic notice",
-				"testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice",
-				"testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/libc.a.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice",
-				"testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/bin/bin2.meta_lic notice",
-				"testdata/firstparty/highest.apex.meta_lic testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/bin/bin1.meta_lic notice",
-				"testdata/firstparty/highest.apex.meta_lic testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/bin/bin2.meta_lic notice",
-				"testdata/firstparty/highest.apex.meta_lic testdata/firstparty/highest.apex.meta_lic testdata/firstparty/highest.apex.meta_lic notice",
-				"testdata/firstparty/highest.apex.meta_lic testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice",
-				"testdata/firstparty/highest.apex.meta_lic testdata/firstparty/lib/libb.so.meta_lic testdata/firstparty/lib/libb.so.meta_lic notice",
-				"testdata/firstparty/highest.apex.meta_lic testdata/firstparty/lib/libc.a.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice",
-				"testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice",
-				"testdata/firstparty/lib/libb.so.meta_lic testdata/firstparty/lib/libb.so.meta_lic testdata/firstparty/lib/libb.so.meta_lic notice",
-				"testdata/firstparty/lib/libc.a.meta_lic testdata/firstparty/lib/libc.a.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice",
-				"testdata/firstparty/lib/libd.so.meta_lic testdata/firstparty/lib/libd.so.meta_lic testdata/firstparty/lib/libd.so.meta_lic notice",
+				"testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/bin/bin1.meta_lic notice",
+				"testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice",
+				"testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice",
+				"testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/bin/bin2.meta_lic notice",
+				"testdata/firstparty/highest.apex.meta_lic testdata/firstparty/bin/bin1.meta_lic notice",
+				"testdata/firstparty/highest.apex.meta_lic testdata/firstparty/bin/bin2.meta_lic notice",
+				"testdata/firstparty/highest.apex.meta_lic testdata/firstparty/highest.apex.meta_lic notice",
+				"testdata/firstparty/highest.apex.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice",
+				"testdata/firstparty/highest.apex.meta_lic testdata/firstparty/lib/libb.so.meta_lic notice",
+				"testdata/firstparty/highest.apex.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice",
+				"testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice",
+				"testdata/firstparty/lib/libb.so.meta_lic testdata/firstparty/lib/libb.so.meta_lic notice",
 			},
 		},
 		{
@@ -56,20 +55,18 @@
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx:       context{stripPrefix: "testdata/firstparty/"},
 			expectedOut: []string{
-				"bin/bin1.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
-				"bin/bin1.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic notice",
-				"bin/bin1.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic notice",
-				"bin/bin2.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic notice",
-				"highest.apex.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
-				"highest.apex.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic notice",
-				"highest.apex.meta_lic highest.apex.meta_lic highest.apex.meta_lic notice",
-				"highest.apex.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic notice",
-				"highest.apex.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic notice",
-				"highest.apex.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic notice",
-				"lib/liba.so.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic notice",
-				"lib/libb.so.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic notice",
-				"lib/libc.a.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic notice",
-				"lib/libd.so.meta_lic lib/libd.so.meta_lic lib/libd.so.meta_lic notice",
+				"bin/bin1.meta_lic bin/bin1.meta_lic notice",
+				"bin/bin1.meta_lic lib/liba.so.meta_lic notice",
+				"bin/bin1.meta_lic lib/libc.a.meta_lic notice",
+				"bin/bin2.meta_lic bin/bin2.meta_lic notice",
+				"highest.apex.meta_lic bin/bin1.meta_lic notice",
+				"highest.apex.meta_lic bin/bin2.meta_lic notice",
+				"highest.apex.meta_lic highest.apex.meta_lic notice",
+				"highest.apex.meta_lic lib/liba.so.meta_lic notice",
+				"highest.apex.meta_lic lib/libb.so.meta_lic notice",
+				"highest.apex.meta_lic lib/libc.a.meta_lic notice",
+				"lib/liba.so.meta_lic lib/liba.so.meta_lic notice",
+				"lib/libb.so.meta_lic lib/libb.so.meta_lic notice",
 			},
 		},
 		{
@@ -77,22 +74,22 @@
 			name:      "apex_trimmed_notice",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"notice"},
+				conditions:  []compliance.LicenseCondition{compliance.NoticeCondition},
 				stripPrefix: "testdata/firstparty/",
 			},
 			expectedOut: []string{
-				"bin/bin1.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
-				"bin/bin1.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic notice",
-				"bin/bin1.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic notice",
-				"bin/bin2.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic notice",
-				"highest.apex.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
-				"highest.apex.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic notice",
-				"highest.apex.meta_lic highest.apex.meta_lic highest.apex.meta_lic notice",
-				"highest.apex.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic notice",
-				"highest.apex.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic notice",
-				"highest.apex.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic notice",
-				"lib/liba.so.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic notice",
-				"lib/libb.so.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic notice",
+				"bin/bin1.meta_lic bin/bin1.meta_lic notice",
+				"bin/bin1.meta_lic lib/liba.so.meta_lic notice",
+				"bin/bin1.meta_lic lib/libc.a.meta_lic notice",
+				"bin/bin2.meta_lic bin/bin2.meta_lic notice",
+				"highest.apex.meta_lic bin/bin1.meta_lic notice",
+				"highest.apex.meta_lic bin/bin2.meta_lic notice",
+				"highest.apex.meta_lic highest.apex.meta_lic notice",
+				"highest.apex.meta_lic lib/liba.so.meta_lic notice",
+				"highest.apex.meta_lic lib/libb.so.meta_lic notice",
+				"highest.apex.meta_lic lib/libc.a.meta_lic notice",
+				"lib/liba.so.meta_lic lib/liba.so.meta_lic notice",
+				"lib/libb.so.meta_lic lib/libb.so.meta_lic notice",
 			},
 		},
 		{
@@ -100,7 +97,7 @@
 			name:      "apex_trimmed_share",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"reciprocal", "restricted"},
+				conditions: compliance.ImpliesShared.AsList(),
 				stripPrefix: "testdata/firstparty/",
 			},
 			expectedOut: []string{},
@@ -110,7 +107,7 @@
 			name:      "apex_trimmed_private",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"proprietary"},
+				conditions: compliance.ImpliesPrivate.AsList(),
 				stripPrefix: "testdata/firstparty/",
 			},
 			expectedOut: []string{},
@@ -120,7 +117,7 @@
 			name:      "apex_trimmed_share_private",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"reciprocal", "restricted", "proprietary"},
+				conditions:  append(compliance.ImpliesPrivate.AsList(),compliance.ImpliesShared.AsList()...),
 				stripPrefix: "testdata/firstparty/",
 			},
 			expectedOut: []string{},
@@ -131,20 +128,18 @@
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx:       context{stripPrefix: "testdata/firstparty/", labelConditions: true},
 			expectedOut: []string{
-				"bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice",
-				"bin/bin1.meta_lic:notice lib/liba.so.meta_lic:notice lib/liba.so.meta_lic:notice notice",
-				"bin/bin1.meta_lic:notice lib/libc.a.meta_lic:notice lib/libc.a.meta_lic:notice notice",
-				"bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice notice",
-				"highest.apex.meta_lic:notice bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice",
-				"highest.apex.meta_lic:notice bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice notice",
-				"highest.apex.meta_lic:notice highest.apex.meta_lic:notice highest.apex.meta_lic:notice notice",
-				"highest.apex.meta_lic:notice lib/liba.so.meta_lic:notice lib/liba.so.meta_lic:notice notice",
-				"highest.apex.meta_lic:notice lib/libb.so.meta_lic:notice lib/libb.so.meta_lic:notice notice",
-				"highest.apex.meta_lic:notice lib/libc.a.meta_lic:notice lib/libc.a.meta_lic:notice notice",
-				"lib/liba.so.meta_lic:notice lib/liba.so.meta_lic:notice lib/liba.so.meta_lic:notice notice",
-				"lib/libb.so.meta_lic:notice lib/libb.so.meta_lic:notice lib/libb.so.meta_lic:notice notice",
-				"lib/libc.a.meta_lic:notice lib/libc.a.meta_lic:notice lib/libc.a.meta_lic:notice notice",
-				"lib/libd.so.meta_lic:notice lib/libd.so.meta_lic:notice lib/libd.so.meta_lic:notice notice",
+				"bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice",
+				"bin/bin1.meta_lic:notice lib/liba.so.meta_lic:notice notice",
+				"bin/bin1.meta_lic:notice lib/libc.a.meta_lic:notice notice",
+				"bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice notice",
+				"highest.apex.meta_lic:notice bin/bin1.meta_lic:notice notice",
+				"highest.apex.meta_lic:notice bin/bin2.meta_lic:notice notice",
+				"highest.apex.meta_lic:notice highest.apex.meta_lic:notice notice",
+				"highest.apex.meta_lic:notice lib/liba.so.meta_lic:notice notice",
+				"highest.apex.meta_lic:notice lib/libb.so.meta_lic:notice notice",
+				"highest.apex.meta_lic:notice lib/libc.a.meta_lic:notice notice",
+				"lib/liba.so.meta_lic:notice lib/liba.so.meta_lic:notice notice",
+				"lib/libb.so.meta_lic:notice lib/libb.so.meta_lic:notice notice",
 			},
 		},
 		{
@@ -152,20 +147,18 @@
 			name:      "container",
 			roots:     []string{"container.zip.meta_lic"},
 			expectedOut: []string{
-				"testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/bin/bin1.meta_lic notice",
-				"testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice",
-				"testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/libc.a.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice",
-				"testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/bin/bin2.meta_lic notice",
-				"testdata/firstparty/container.zip.meta_lic testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/bin/bin1.meta_lic notice",
-				"testdata/firstparty/container.zip.meta_lic testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/bin/bin2.meta_lic notice",
-				"testdata/firstparty/container.zip.meta_lic testdata/firstparty/container.zip.meta_lic testdata/firstparty/container.zip.meta_lic notice",
-				"testdata/firstparty/container.zip.meta_lic testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice",
-				"testdata/firstparty/container.zip.meta_lic testdata/firstparty/lib/libb.so.meta_lic testdata/firstparty/lib/libb.so.meta_lic notice",
-				"testdata/firstparty/container.zip.meta_lic testdata/firstparty/lib/libc.a.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice",
-				"testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice",
-				"testdata/firstparty/lib/libb.so.meta_lic testdata/firstparty/lib/libb.so.meta_lic testdata/firstparty/lib/libb.so.meta_lic notice",
-				"testdata/firstparty/lib/libc.a.meta_lic testdata/firstparty/lib/libc.a.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice",
-				"testdata/firstparty/lib/libd.so.meta_lic testdata/firstparty/lib/libd.so.meta_lic testdata/firstparty/lib/libd.so.meta_lic notice",
+				"testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/bin/bin1.meta_lic notice",
+				"testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice",
+				"testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice",
+				"testdata/firstparty/bin/bin2.meta_lic testdata/firstparty/bin/bin2.meta_lic notice",
+				"testdata/firstparty/container.zip.meta_lic testdata/firstparty/bin/bin1.meta_lic notice",
+				"testdata/firstparty/container.zip.meta_lic testdata/firstparty/bin/bin2.meta_lic notice",
+				"testdata/firstparty/container.zip.meta_lic testdata/firstparty/container.zip.meta_lic notice",
+				"testdata/firstparty/container.zip.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice",
+				"testdata/firstparty/container.zip.meta_lic testdata/firstparty/lib/libb.so.meta_lic notice",
+				"testdata/firstparty/container.zip.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice",
+				"testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice",
+				"testdata/firstparty/lib/libb.so.meta_lic testdata/firstparty/lib/libb.so.meta_lic notice",
 			},
 		},
 		{
@@ -173,11 +166,8 @@
 			name:      "application",
 			roots:     []string{"application.meta_lic"},
 			expectedOut: []string{
-				"testdata/firstparty/application.meta_lic testdata/firstparty/application.meta_lic testdata/firstparty/application.meta_lic notice",
-				"testdata/firstparty/application.meta_lic testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice",
-				"testdata/firstparty/bin/bin3.meta_lic testdata/firstparty/bin/bin3.meta_lic testdata/firstparty/bin/bin3.meta_lic notice",
-				"testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice",
-				"testdata/firstparty/lib/libb.so.meta_lic testdata/firstparty/lib/libb.so.meta_lic testdata/firstparty/lib/libb.so.meta_lic notice",
+				"testdata/firstparty/application.meta_lic testdata/firstparty/application.meta_lic notice",
+				"testdata/firstparty/application.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice",
 			},
 		},
 		{
@@ -185,11 +175,9 @@
 			name:      "binary",
 			roots:     []string{"bin/bin1.meta_lic"},
 			expectedOut: []string{
-				"testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/bin/bin1.meta_lic notice",
-				"testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice",
-				"testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/libc.a.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice",
-				"testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice",
-				"testdata/firstparty/lib/libc.a.meta_lic testdata/firstparty/lib/libc.a.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice",
+				"testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/bin/bin1.meta_lic notice",
+				"testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/liba.so.meta_lic notice",
+				"testdata/firstparty/bin/bin1.meta_lic testdata/firstparty/lib/libc.a.meta_lic notice",
 			},
 		},
 		{
@@ -197,7 +185,7 @@
 			name:      "library",
 			roots:     []string{"lib/libd.so.meta_lic"},
 			expectedOut: []string{
-				"testdata/firstparty/lib/libd.so.meta_lic testdata/firstparty/lib/libd.so.meta_lic testdata/firstparty/lib/libd.so.meta_lic notice",
+				"testdata/firstparty/lib/libd.so.meta_lic testdata/firstparty/lib/libd.so.meta_lic notice",
 			},
 		},
 		{
@@ -205,20 +193,18 @@
 			name:      "apex",
 			roots:     []string{"highest.apex.meta_lic"},
 			expectedOut: []string{
-				"testdata/notice/bin/bin1.meta_lic testdata/notice/bin/bin1.meta_lic testdata/notice/bin/bin1.meta_lic notice",
-				"testdata/notice/bin/bin1.meta_lic testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic notice",
-				"testdata/notice/bin/bin1.meta_lic testdata/notice/lib/libc.a.meta_lic testdata/notice/lib/libc.a.meta_lic notice",
-				"testdata/notice/bin/bin2.meta_lic testdata/notice/bin/bin2.meta_lic testdata/notice/bin/bin2.meta_lic notice",
-				"testdata/notice/highest.apex.meta_lic testdata/notice/bin/bin1.meta_lic testdata/notice/bin/bin1.meta_lic notice",
-				"testdata/notice/highest.apex.meta_lic testdata/notice/bin/bin2.meta_lic testdata/notice/bin/bin2.meta_lic notice",
-				"testdata/notice/highest.apex.meta_lic testdata/notice/highest.apex.meta_lic testdata/notice/highest.apex.meta_lic notice",
-				"testdata/notice/highest.apex.meta_lic testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic notice",
-				"testdata/notice/highest.apex.meta_lic testdata/notice/lib/libb.so.meta_lic testdata/notice/lib/libb.so.meta_lic notice",
-				"testdata/notice/highest.apex.meta_lic testdata/notice/lib/libc.a.meta_lic testdata/notice/lib/libc.a.meta_lic notice",
-				"testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic notice",
-				"testdata/notice/lib/libb.so.meta_lic testdata/notice/lib/libb.so.meta_lic testdata/notice/lib/libb.so.meta_lic notice",
-				"testdata/notice/lib/libc.a.meta_lic testdata/notice/lib/libc.a.meta_lic testdata/notice/lib/libc.a.meta_lic notice",
-				"testdata/notice/lib/libd.so.meta_lic testdata/notice/lib/libd.so.meta_lic testdata/notice/lib/libd.so.meta_lic notice",
+				"testdata/notice/bin/bin1.meta_lic testdata/notice/bin/bin1.meta_lic notice",
+				"testdata/notice/bin/bin1.meta_lic testdata/notice/lib/liba.so.meta_lic notice",
+				"testdata/notice/bin/bin1.meta_lic testdata/notice/lib/libc.a.meta_lic notice",
+				"testdata/notice/bin/bin2.meta_lic testdata/notice/bin/bin2.meta_lic notice",
+				"testdata/notice/highest.apex.meta_lic testdata/notice/bin/bin1.meta_lic notice",
+				"testdata/notice/highest.apex.meta_lic testdata/notice/bin/bin2.meta_lic notice",
+				"testdata/notice/highest.apex.meta_lic testdata/notice/highest.apex.meta_lic notice",
+				"testdata/notice/highest.apex.meta_lic testdata/notice/lib/liba.so.meta_lic notice",
+				"testdata/notice/highest.apex.meta_lic testdata/notice/lib/libb.so.meta_lic notice",
+				"testdata/notice/highest.apex.meta_lic testdata/notice/lib/libc.a.meta_lic notice",
+				"testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic notice",
+				"testdata/notice/lib/libb.so.meta_lic testdata/notice/lib/libb.so.meta_lic notice",
 			},
 		},
 		{
@@ -227,20 +213,18 @@
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx:       context{stripPrefix: "testdata/notice/"},
 			expectedOut: []string{
-				"bin/bin1.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
-				"bin/bin1.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic notice",
-				"bin/bin1.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic notice",
-				"bin/bin2.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic notice",
-				"highest.apex.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
-				"highest.apex.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic notice",
-				"highest.apex.meta_lic highest.apex.meta_lic highest.apex.meta_lic notice",
-				"highest.apex.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic notice",
-				"highest.apex.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic notice",
-				"highest.apex.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic notice",
-				"lib/liba.so.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic notice",
-				"lib/libb.so.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic notice",
-				"lib/libc.a.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic notice",
-				"lib/libd.so.meta_lic lib/libd.so.meta_lic lib/libd.so.meta_lic notice",
+				"bin/bin1.meta_lic bin/bin1.meta_lic notice",
+				"bin/bin1.meta_lic lib/liba.so.meta_lic notice",
+				"bin/bin1.meta_lic lib/libc.a.meta_lic notice",
+				"bin/bin2.meta_lic bin/bin2.meta_lic notice",
+				"highest.apex.meta_lic bin/bin1.meta_lic notice",
+				"highest.apex.meta_lic bin/bin2.meta_lic notice",
+				"highest.apex.meta_lic highest.apex.meta_lic notice",
+				"highest.apex.meta_lic lib/liba.so.meta_lic notice",
+				"highest.apex.meta_lic lib/libb.so.meta_lic notice",
+				"highest.apex.meta_lic lib/libc.a.meta_lic notice",
+				"lib/liba.so.meta_lic lib/liba.so.meta_lic notice",
+				"lib/libb.so.meta_lic lib/libb.so.meta_lic notice",
 			},
 		},
 		{
@@ -248,22 +232,22 @@
 			name:      "apex_trimmed_notice",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"notice"},
+				conditions:  []compliance.LicenseCondition{compliance.NoticeCondition},
 				stripPrefix: "testdata/notice/",
 			},
 			expectedOut: []string{
-				"bin/bin1.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
-				"bin/bin1.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic notice",
-				"bin/bin1.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic notice",
-				"bin/bin2.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic notice",
-				"highest.apex.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
-				"highest.apex.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic notice",
-				"highest.apex.meta_lic highest.apex.meta_lic highest.apex.meta_lic notice",
-				"highest.apex.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic notice",
-				"highest.apex.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic notice",
-				"highest.apex.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic notice",
-				"lib/liba.so.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic notice",
-				"lib/libb.so.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic notice",
+				"bin/bin1.meta_lic bin/bin1.meta_lic notice",
+				"bin/bin1.meta_lic lib/liba.so.meta_lic notice",
+				"bin/bin1.meta_lic lib/libc.a.meta_lic notice",
+				"bin/bin2.meta_lic bin/bin2.meta_lic notice",
+				"highest.apex.meta_lic bin/bin1.meta_lic notice",
+				"highest.apex.meta_lic bin/bin2.meta_lic notice",
+				"highest.apex.meta_lic highest.apex.meta_lic notice",
+				"highest.apex.meta_lic lib/liba.so.meta_lic notice",
+				"highest.apex.meta_lic lib/libb.so.meta_lic notice",
+				"highest.apex.meta_lic lib/libc.a.meta_lic notice",
+				"lib/liba.so.meta_lic lib/liba.so.meta_lic notice",
+				"lib/libb.so.meta_lic lib/libb.so.meta_lic notice",
 			},
 		},
 		{
@@ -271,7 +255,7 @@
 			name:      "apex_trimmed_share",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"reciprocal", "restricted"},
+				conditions: compliance.ImpliesShared.AsList(),
 				stripPrefix: "testdata/notice/",
 			},
 			expectedOut: []string{},
@@ -281,7 +265,7 @@
 			name:      "apex_trimmed_private",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"proprietary"},
+				conditions: compliance.ImpliesPrivate.AsList(),
 				stripPrefix: "testdata/notice/",
 			},
 			expectedOut: []string{},
@@ -291,7 +275,7 @@
 			name:      "apex_trimmed_share_private",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"reciprocal", "restricted", "proprietary"},
+				conditions:  append(compliance.ImpliesShared.AsList(),compliance.ImpliesPrivate.AsList()...),
 				stripPrefix: "testdata/notice/",
 			},
 			expectedOut: []string{},
@@ -302,20 +286,18 @@
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx:       context{stripPrefix: "testdata/notice/", labelConditions: true},
 			expectedOut: []string{
-				"bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice",
-				"bin/bin1.meta_lic:notice lib/liba.so.meta_lic:notice lib/liba.so.meta_lic:notice notice",
-				"bin/bin1.meta_lic:notice lib/libc.a.meta_lic:notice lib/libc.a.meta_lic:notice notice",
-				"bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice notice",
-				"highest.apex.meta_lic:notice bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice",
-				"highest.apex.meta_lic:notice bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice notice",
-				"highest.apex.meta_lic:notice highest.apex.meta_lic:notice highest.apex.meta_lic:notice notice",
-				"highest.apex.meta_lic:notice lib/liba.so.meta_lic:notice lib/liba.so.meta_lic:notice notice",
-				"highest.apex.meta_lic:notice lib/libb.so.meta_lic:notice lib/libb.so.meta_lic:notice notice",
-				"highest.apex.meta_lic:notice lib/libc.a.meta_lic:notice lib/libc.a.meta_lic:notice notice",
-				"lib/liba.so.meta_lic:notice lib/liba.so.meta_lic:notice lib/liba.so.meta_lic:notice notice",
-				"lib/libb.so.meta_lic:notice lib/libb.so.meta_lic:notice lib/libb.so.meta_lic:notice notice",
-				"lib/libc.a.meta_lic:notice lib/libc.a.meta_lic:notice lib/libc.a.meta_lic:notice notice",
-				"lib/libd.so.meta_lic:notice lib/libd.so.meta_lic:notice lib/libd.so.meta_lic:notice notice",
+				"bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice",
+				"bin/bin1.meta_lic:notice lib/liba.so.meta_lic:notice notice",
+				"bin/bin1.meta_lic:notice lib/libc.a.meta_lic:notice notice",
+				"bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice notice",
+				"highest.apex.meta_lic:notice bin/bin1.meta_lic:notice notice",
+				"highest.apex.meta_lic:notice bin/bin2.meta_lic:notice notice",
+				"highest.apex.meta_lic:notice highest.apex.meta_lic:notice notice",
+				"highest.apex.meta_lic:notice lib/liba.so.meta_lic:notice notice",
+				"highest.apex.meta_lic:notice lib/libb.so.meta_lic:notice notice",
+				"highest.apex.meta_lic:notice lib/libc.a.meta_lic:notice notice",
+				"lib/liba.so.meta_lic:notice lib/liba.so.meta_lic:notice notice",
+				"lib/libb.so.meta_lic:notice lib/libb.so.meta_lic:notice notice",
 			},
 		},
 		{
@@ -323,20 +305,18 @@
 			name:      "container",
 			roots:     []string{"container.zip.meta_lic"},
 			expectedOut: []string{
-				"testdata/notice/bin/bin1.meta_lic testdata/notice/bin/bin1.meta_lic testdata/notice/bin/bin1.meta_lic notice",
-				"testdata/notice/bin/bin1.meta_lic testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic notice",
-				"testdata/notice/bin/bin1.meta_lic testdata/notice/lib/libc.a.meta_lic testdata/notice/lib/libc.a.meta_lic notice",
-				"testdata/notice/bin/bin2.meta_lic testdata/notice/bin/bin2.meta_lic testdata/notice/bin/bin2.meta_lic notice",
-				"testdata/notice/container.zip.meta_lic testdata/notice/bin/bin1.meta_lic testdata/notice/bin/bin1.meta_lic notice",
-				"testdata/notice/container.zip.meta_lic testdata/notice/bin/bin2.meta_lic testdata/notice/bin/bin2.meta_lic notice",
-				"testdata/notice/container.zip.meta_lic testdata/notice/container.zip.meta_lic testdata/notice/container.zip.meta_lic notice",
-				"testdata/notice/container.zip.meta_lic testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic notice",
-				"testdata/notice/container.zip.meta_lic testdata/notice/lib/libb.so.meta_lic testdata/notice/lib/libb.so.meta_lic notice",
-				"testdata/notice/container.zip.meta_lic testdata/notice/lib/libc.a.meta_lic testdata/notice/lib/libc.a.meta_lic notice",
-				"testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic notice",
-				"testdata/notice/lib/libb.so.meta_lic testdata/notice/lib/libb.so.meta_lic testdata/notice/lib/libb.so.meta_lic notice",
-				"testdata/notice/lib/libc.a.meta_lic testdata/notice/lib/libc.a.meta_lic testdata/notice/lib/libc.a.meta_lic notice",
-				"testdata/notice/lib/libd.so.meta_lic testdata/notice/lib/libd.so.meta_lic testdata/notice/lib/libd.so.meta_lic notice",
+				"testdata/notice/bin/bin1.meta_lic testdata/notice/bin/bin1.meta_lic notice",
+				"testdata/notice/bin/bin1.meta_lic testdata/notice/lib/liba.so.meta_lic notice",
+				"testdata/notice/bin/bin1.meta_lic testdata/notice/lib/libc.a.meta_lic notice",
+				"testdata/notice/bin/bin2.meta_lic testdata/notice/bin/bin2.meta_lic notice",
+				"testdata/notice/container.zip.meta_lic testdata/notice/bin/bin1.meta_lic notice",
+				"testdata/notice/container.zip.meta_lic testdata/notice/bin/bin2.meta_lic notice",
+				"testdata/notice/container.zip.meta_lic testdata/notice/container.zip.meta_lic notice",
+				"testdata/notice/container.zip.meta_lic testdata/notice/lib/liba.so.meta_lic notice",
+				"testdata/notice/container.zip.meta_lic testdata/notice/lib/libb.so.meta_lic notice",
+				"testdata/notice/container.zip.meta_lic testdata/notice/lib/libc.a.meta_lic notice",
+				"testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic notice",
+				"testdata/notice/lib/libb.so.meta_lic testdata/notice/lib/libb.so.meta_lic notice",
 			},
 		},
 		{
@@ -344,11 +324,8 @@
 			name:      "application",
 			roots:     []string{"application.meta_lic"},
 			expectedOut: []string{
-				"testdata/notice/application.meta_lic testdata/notice/application.meta_lic testdata/notice/application.meta_lic notice",
-				"testdata/notice/application.meta_lic testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic notice",
-				"testdata/notice/bin/bin3.meta_lic testdata/notice/bin/bin3.meta_lic testdata/notice/bin/bin3.meta_lic notice",
-				"testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic notice",
-				"testdata/notice/lib/libb.so.meta_lic testdata/notice/lib/libb.so.meta_lic testdata/notice/lib/libb.so.meta_lic notice",
+				"testdata/notice/application.meta_lic testdata/notice/application.meta_lic notice",
+				"testdata/notice/application.meta_lic testdata/notice/lib/liba.so.meta_lic notice",
 			},
 		},
 		{
@@ -356,11 +333,9 @@
 			name:      "binary",
 			roots:     []string{"bin/bin1.meta_lic"},
 			expectedOut: []string{
-				"testdata/notice/bin/bin1.meta_lic testdata/notice/bin/bin1.meta_lic testdata/notice/bin/bin1.meta_lic notice",
-				"testdata/notice/bin/bin1.meta_lic testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic notice",
-				"testdata/notice/bin/bin1.meta_lic testdata/notice/lib/libc.a.meta_lic testdata/notice/lib/libc.a.meta_lic notice",
-				"testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic testdata/notice/lib/liba.so.meta_lic notice",
-				"testdata/notice/lib/libc.a.meta_lic testdata/notice/lib/libc.a.meta_lic testdata/notice/lib/libc.a.meta_lic notice",
+				"testdata/notice/bin/bin1.meta_lic testdata/notice/bin/bin1.meta_lic notice",
+				"testdata/notice/bin/bin1.meta_lic testdata/notice/lib/liba.so.meta_lic notice",
+				"testdata/notice/bin/bin1.meta_lic testdata/notice/lib/libc.a.meta_lic notice",
 			},
 		},
 		{
@@ -368,7 +343,7 @@
 			name:      "library",
 			roots:     []string{"lib/libd.so.meta_lic"},
 			expectedOut: []string{
-				"testdata/notice/lib/libd.so.meta_lic testdata/notice/lib/libd.so.meta_lic testdata/notice/lib/libd.so.meta_lic notice",
+				"testdata/notice/lib/libd.so.meta_lic testdata/notice/lib/libd.so.meta_lic notice",
 			},
 		},
 		{
@@ -376,20 +351,18 @@
 			name:      "apex",
 			roots:     []string{"highest.apex.meta_lic"},
 			expectedOut: []string{
-				"testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/bin/bin1.meta_lic notice",
-				"testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal",
-				"testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/libc.a.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal",
-				"testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/bin/bin2.meta_lic notice",
-				"testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/bin/bin1.meta_lic notice",
-				"testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/bin/bin2.meta_lic notice",
-				"testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/highest.apex.meta_lic notice",
-				"testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal",
-				"testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/lib/libb.so.meta_lic testdata/reciprocal/lib/libb.so.meta_lic notice",
-				"testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/lib/libc.a.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal",
-				"testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal",
-				"testdata/reciprocal/lib/libb.so.meta_lic testdata/reciprocal/lib/libb.so.meta_lic testdata/reciprocal/lib/libb.so.meta_lic notice",
-				"testdata/reciprocal/lib/libc.a.meta_lic testdata/reciprocal/lib/libc.a.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal",
-				"testdata/reciprocal/lib/libd.so.meta_lic testdata/reciprocal/lib/libd.so.meta_lic testdata/reciprocal/lib/libd.so.meta_lic notice",
+				"testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/bin/bin1.meta_lic notice",
+				"testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal",
+				"testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal",
+				"testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/bin/bin2.meta_lic notice",
+				"testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/bin/bin1.meta_lic notice",
+				"testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/bin/bin2.meta_lic notice",
+				"testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/highest.apex.meta_lic notice",
+				"testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal",
+				"testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/lib/libb.so.meta_lic notice",
+				"testdata/reciprocal/highest.apex.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal",
+				"testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal",
+				"testdata/reciprocal/lib/libb.so.meta_lic testdata/reciprocal/lib/libb.so.meta_lic notice",
 			},
 		},
 		{
@@ -398,20 +371,18 @@
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx:       context{stripPrefix: "testdata/reciprocal/"},
 			expectedOut: []string{
-				"bin/bin1.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
-				"bin/bin1.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic reciprocal",
-				"bin/bin1.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic reciprocal",
-				"bin/bin2.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic notice",
-				"highest.apex.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
-				"highest.apex.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic notice",
-				"highest.apex.meta_lic highest.apex.meta_lic highest.apex.meta_lic notice",
-				"highest.apex.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic reciprocal",
-				"highest.apex.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic notice",
-				"highest.apex.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic reciprocal",
-				"lib/liba.so.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic reciprocal",
-				"lib/libb.so.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic notice",
-				"lib/libc.a.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic reciprocal",
-				"lib/libd.so.meta_lic lib/libd.so.meta_lic lib/libd.so.meta_lic notice",
+				"bin/bin1.meta_lic bin/bin1.meta_lic notice",
+				"bin/bin1.meta_lic lib/liba.so.meta_lic reciprocal",
+				"bin/bin1.meta_lic lib/libc.a.meta_lic reciprocal",
+				"bin/bin2.meta_lic bin/bin2.meta_lic notice",
+				"highest.apex.meta_lic bin/bin1.meta_lic notice",
+				"highest.apex.meta_lic bin/bin2.meta_lic notice",
+				"highest.apex.meta_lic highest.apex.meta_lic notice",
+				"highest.apex.meta_lic lib/liba.so.meta_lic reciprocal",
+				"highest.apex.meta_lic lib/libb.so.meta_lic notice",
+				"highest.apex.meta_lic lib/libc.a.meta_lic reciprocal",
+				"lib/liba.so.meta_lic lib/liba.so.meta_lic reciprocal",
+				"lib/libb.so.meta_lic lib/libb.so.meta_lic notice",
 			},
 		},
 		{
@@ -419,17 +390,17 @@
 			name:      "apex_trimmed_notice",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"notice"},
+				conditions:  []compliance.LicenseCondition{compliance.NoticeCondition},
 				stripPrefix: "testdata/reciprocal/",
 			},
 			expectedOut: []string{
-				"bin/bin1.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
-				"bin/bin2.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic notice",
-				"highest.apex.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
-				"highest.apex.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic notice",
-				"highest.apex.meta_lic highest.apex.meta_lic highest.apex.meta_lic notice",
-				"highest.apex.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic notice",
-				"lib/libb.so.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic notice",
+				"bin/bin1.meta_lic bin/bin1.meta_lic notice",
+				"bin/bin2.meta_lic bin/bin2.meta_lic notice",
+				"highest.apex.meta_lic bin/bin1.meta_lic notice",
+				"highest.apex.meta_lic bin/bin2.meta_lic notice",
+				"highest.apex.meta_lic highest.apex.meta_lic notice",
+				"highest.apex.meta_lic lib/libb.so.meta_lic notice",
+				"lib/libb.so.meta_lic lib/libb.so.meta_lic notice",
 			},
 		},
 		{
@@ -437,15 +408,15 @@
 			name:      "apex_trimmed_share",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"reciprocal", "restricted"},
+				conditions: compliance.ImpliesShared.AsList(),
 				stripPrefix: "testdata/reciprocal/",
 			},
 			expectedOut: []string{
-				"bin/bin1.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic reciprocal",
-				"bin/bin1.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic reciprocal",
-				"highest.apex.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic reciprocal",
-				"highest.apex.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic reciprocal",
-				"lib/liba.so.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic reciprocal",
+				"bin/bin1.meta_lic lib/liba.so.meta_lic reciprocal",
+				"bin/bin1.meta_lic lib/libc.a.meta_lic reciprocal",
+				"highest.apex.meta_lic lib/liba.so.meta_lic reciprocal",
+				"highest.apex.meta_lic lib/libc.a.meta_lic reciprocal",
+				"lib/liba.so.meta_lic lib/liba.so.meta_lic reciprocal",
 			},
 		},
 		{
@@ -453,7 +424,7 @@
 			name:      "apex_trimmed_private",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"proprietary"},
+				conditions: compliance.ImpliesPrivate.AsList(),
 				stripPrefix: "testdata/reciprocal/",
 			},
 			expectedOut: []string{},
@@ -463,15 +434,15 @@
 			name:      "apex_trimmed_share_private",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"reciprocal", "restricted", "proprietary"},
+				conditions: append(compliance.ImpliesShared.AsList(),compliance.ImpliesPrivate.AsList()...),
 				stripPrefix: "testdata/reciprocal/",
 			},
 			expectedOut: []string{
-				"bin/bin1.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic reciprocal",
-				"bin/bin1.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic reciprocal",
-				"highest.apex.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic reciprocal",
-				"highest.apex.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic reciprocal",
-				"lib/liba.so.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic reciprocal",
+				"bin/bin1.meta_lic lib/liba.so.meta_lic reciprocal",
+				"bin/bin1.meta_lic lib/libc.a.meta_lic reciprocal",
+				"highest.apex.meta_lic lib/liba.so.meta_lic reciprocal",
+				"highest.apex.meta_lic lib/libc.a.meta_lic reciprocal",
+				"lib/liba.so.meta_lic lib/liba.so.meta_lic reciprocal",
 			},
 		},
 		{
@@ -480,20 +451,18 @@
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx:       context{stripPrefix: "testdata/reciprocal/", labelConditions: true},
 			expectedOut: []string{
-				"bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice",
-				"bin/bin1.meta_lic:notice lib/liba.so.meta_lic:reciprocal lib/liba.so.meta_lic:reciprocal reciprocal",
-				"bin/bin1.meta_lic:notice lib/libc.a.meta_lic:reciprocal lib/libc.a.meta_lic:reciprocal reciprocal",
-				"bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice notice",
-				"highest.apex.meta_lic:notice bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice",
-				"highest.apex.meta_lic:notice bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice notice",
-				"highest.apex.meta_lic:notice highest.apex.meta_lic:notice highest.apex.meta_lic:notice notice",
-				"highest.apex.meta_lic:notice lib/liba.so.meta_lic:reciprocal lib/liba.so.meta_lic:reciprocal reciprocal",
-				"highest.apex.meta_lic:notice lib/libb.so.meta_lic:notice lib/libb.so.meta_lic:notice notice",
-				"highest.apex.meta_lic:notice lib/libc.a.meta_lic:reciprocal lib/libc.a.meta_lic:reciprocal reciprocal",
-				"lib/liba.so.meta_lic:reciprocal lib/liba.so.meta_lic:reciprocal lib/liba.so.meta_lic:reciprocal reciprocal",
-				"lib/libb.so.meta_lic:notice lib/libb.so.meta_lic:notice lib/libb.so.meta_lic:notice notice",
-				"lib/libc.a.meta_lic:reciprocal lib/libc.a.meta_lic:reciprocal lib/libc.a.meta_lic:reciprocal reciprocal",
-				"lib/libd.so.meta_lic:notice lib/libd.so.meta_lic:notice lib/libd.so.meta_lic:notice notice",
+				"bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice",
+				"bin/bin1.meta_lic:notice lib/liba.so.meta_lic:reciprocal reciprocal",
+				"bin/bin1.meta_lic:notice lib/libc.a.meta_lic:reciprocal reciprocal",
+				"bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice notice",
+				"highest.apex.meta_lic:notice bin/bin1.meta_lic:notice notice",
+				"highest.apex.meta_lic:notice bin/bin2.meta_lic:notice notice",
+				"highest.apex.meta_lic:notice highest.apex.meta_lic:notice notice",
+				"highest.apex.meta_lic:notice lib/liba.so.meta_lic:reciprocal reciprocal",
+				"highest.apex.meta_lic:notice lib/libb.so.meta_lic:notice notice",
+				"highest.apex.meta_lic:notice lib/libc.a.meta_lic:reciprocal reciprocal",
+				"lib/liba.so.meta_lic:reciprocal lib/liba.so.meta_lic:reciprocal reciprocal",
+				"lib/libb.so.meta_lic:notice lib/libb.so.meta_lic:notice notice",
 			},
 		},
 		{
@@ -501,20 +470,18 @@
 			name:      "container",
 			roots:     []string{"container.zip.meta_lic"},
 			expectedOut: []string{
-				"testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/bin/bin1.meta_lic notice",
-				"testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal",
-				"testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/libc.a.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal",
-				"testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/bin/bin2.meta_lic notice",
-				"testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/bin/bin1.meta_lic notice",
-				"testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/bin/bin2.meta_lic notice",
-				"testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/container.zip.meta_lic notice",
-				"testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal",
-				"testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/lib/libb.so.meta_lic testdata/reciprocal/lib/libb.so.meta_lic notice",
-				"testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/lib/libc.a.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal",
-				"testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal",
-				"testdata/reciprocal/lib/libb.so.meta_lic testdata/reciprocal/lib/libb.so.meta_lic testdata/reciprocal/lib/libb.so.meta_lic notice",
-				"testdata/reciprocal/lib/libc.a.meta_lic testdata/reciprocal/lib/libc.a.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal",
-				"testdata/reciprocal/lib/libd.so.meta_lic testdata/reciprocal/lib/libd.so.meta_lic testdata/reciprocal/lib/libd.so.meta_lic notice",
+				"testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/bin/bin1.meta_lic notice",
+				"testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal",
+				"testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal",
+				"testdata/reciprocal/bin/bin2.meta_lic testdata/reciprocal/bin/bin2.meta_lic notice",
+				"testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/bin/bin1.meta_lic notice",
+				"testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/bin/bin2.meta_lic notice",
+				"testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/container.zip.meta_lic notice",
+				"testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal",
+				"testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/lib/libb.so.meta_lic notice",
+				"testdata/reciprocal/container.zip.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal",
+				"testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal",
+				"testdata/reciprocal/lib/libb.so.meta_lic testdata/reciprocal/lib/libb.so.meta_lic notice",
 			},
 		},
 		{
@@ -522,11 +489,8 @@
 			name:      "application",
 			roots:     []string{"application.meta_lic"},
 			expectedOut: []string{
-				"testdata/reciprocal/application.meta_lic testdata/reciprocal/application.meta_lic testdata/reciprocal/application.meta_lic notice",
-				"testdata/reciprocal/application.meta_lic testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal",
-				"testdata/reciprocal/bin/bin3.meta_lic testdata/reciprocal/bin/bin3.meta_lic testdata/reciprocal/bin/bin3.meta_lic notice",
-				"testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal",
-				"testdata/reciprocal/lib/libb.so.meta_lic testdata/reciprocal/lib/libb.so.meta_lic testdata/reciprocal/lib/libb.so.meta_lic notice",
+				"testdata/reciprocal/application.meta_lic testdata/reciprocal/application.meta_lic notice",
+				"testdata/reciprocal/application.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal",
 			},
 		},
 		{
@@ -534,11 +498,9 @@
 			name:      "binary",
 			roots:     []string{"bin/bin1.meta_lic"},
 			expectedOut: []string{
-				"testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/bin/bin1.meta_lic notice",
-				"testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal",
-				"testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/libc.a.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal",
-				"testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal",
-				"testdata/reciprocal/lib/libc.a.meta_lic testdata/reciprocal/lib/libc.a.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal",
+				"testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/bin/bin1.meta_lic notice",
+				"testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/liba.so.meta_lic reciprocal",
+				"testdata/reciprocal/bin/bin1.meta_lic testdata/reciprocal/lib/libc.a.meta_lic reciprocal",
 			},
 		},
 		{
@@ -546,7 +508,7 @@
 			name:      "library",
 			roots:     []string{"lib/libd.so.meta_lic"},
 			expectedOut: []string{
-				"testdata/reciprocal/lib/libd.so.meta_lic testdata/reciprocal/lib/libd.so.meta_lic testdata/reciprocal/lib/libd.so.meta_lic notice",
+				"testdata/reciprocal/lib/libd.so.meta_lic testdata/reciprocal/lib/libd.so.meta_lic notice",
 			},
 		},
 		{
@@ -554,33 +516,19 @@
 			name:      "apex",
 			roots:     []string{"highest.apex.meta_lic"},
 			expectedOut: []string{
-				"testdata/restricted/bin/bin1.meta_lic testdata/restricted/bin/bin1.meta_lic testdata/restricted/bin/bin1.meta_lic notice",
-				"testdata/restricted/bin/bin1.meta_lic testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
-				"testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
-				"testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
-				"testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal",
-				"testdata/restricted/bin/bin2.meta_lic testdata/restricted/bin/bin2.meta_lic testdata/restricted/bin/bin2.meta_lic notice",
-				"testdata/restricted/bin/bin2.meta_lic testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
-				"testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libb.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
-				"testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libd.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
-				"testdata/restricted/highest.apex.meta_lic testdata/restricted/bin/bin1.meta_lic testdata/restricted/bin/bin1.meta_lic notice",
-				"testdata/restricted/highest.apex.meta_lic testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
-				"testdata/restricted/highest.apex.meta_lic testdata/restricted/bin/bin2.meta_lic testdata/restricted/bin/bin2.meta_lic notice",
-				"testdata/restricted/highest.apex.meta_lic testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
-				"testdata/restricted/highest.apex.meta_lic testdata/restricted/highest.apex.meta_lic testdata/restricted/highest.apex.meta_lic notice",
-				"testdata/restricted/highest.apex.meta_lic testdata/restricted/highest.apex.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
-				"testdata/restricted/highest.apex.meta_lic testdata/restricted/highest.apex.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
-				"testdata/restricted/highest.apex.meta_lic testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
-				"testdata/restricted/highest.apex.meta_lic testdata/restricted/lib/libb.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
-				"testdata/restricted/highest.apex.meta_lic testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
-				"testdata/restricted/highest.apex.meta_lic testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal",
-				"testdata/restricted/highest.apex.meta_lic testdata/restricted/lib/libd.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
-				"testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
-				"testdata/restricted/lib/libb.so.meta_lic testdata/restricted/lib/libb.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
-				"testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
-				"testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal",
-				"testdata/restricted/lib/libd.so.meta_lic testdata/restricted/lib/libd.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
-				"testdata/restricted/lib/libd.so.meta_lic testdata/restricted/lib/libd.so.meta_lic testdata/restricted/lib/libd.so.meta_lic notice",
+				"testdata/restricted/bin/bin1.meta_lic testdata/restricted/bin/bin1.meta_lic notice:restricted_allows_dynamic_linking",
+				"testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted_allows_dynamic_linking",
+				"testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal:restricted_allows_dynamic_linking",
+				"testdata/restricted/bin/bin2.meta_lic testdata/restricted/bin/bin2.meta_lic notice:restricted",
+				"testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
+				"testdata/restricted/highest.apex.meta_lic testdata/restricted/bin/bin1.meta_lic notice:restricted_allows_dynamic_linking",
+				"testdata/restricted/highest.apex.meta_lic testdata/restricted/bin/bin2.meta_lic notice:restricted",
+				"testdata/restricted/highest.apex.meta_lic testdata/restricted/highest.apex.meta_lic notice:restricted:restricted_allows_dynamic_linking",
+				"testdata/restricted/highest.apex.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted_allows_dynamic_linking",
+				"testdata/restricted/highest.apex.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
+				"testdata/restricted/highest.apex.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal:restricted_allows_dynamic_linking",
+				"testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted_allows_dynamic_linking",
+				"testdata/restricted/lib/libb.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
 			},
 		},
 		{
@@ -589,33 +537,19 @@
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx:       context{stripPrefix: "testdata/restricted/"},
 			expectedOut: []string{
-				"bin/bin1.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
-				"bin/bin1.meta_lic bin/bin1.meta_lic lib/liba.so.meta_lic restricted",
-				"bin/bin1.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic restricted",
-				"bin/bin1.meta_lic lib/libc.a.meta_lic lib/liba.so.meta_lic restricted",
-				"bin/bin1.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic reciprocal",
-				"bin/bin2.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic notice",
-				"bin/bin2.meta_lic bin/bin2.meta_lic lib/libb.so.meta_lic restricted",
-				"bin/bin2.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
-				"bin/bin2.meta_lic lib/libd.so.meta_lic lib/libb.so.meta_lic restricted",
-				"highest.apex.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
-				"highest.apex.meta_lic bin/bin1.meta_lic lib/liba.so.meta_lic restricted",
-				"highest.apex.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic notice",
-				"highest.apex.meta_lic bin/bin2.meta_lic lib/libb.so.meta_lic restricted",
-				"highest.apex.meta_lic highest.apex.meta_lic highest.apex.meta_lic notice",
-				"highest.apex.meta_lic highest.apex.meta_lic lib/liba.so.meta_lic restricted",
-				"highest.apex.meta_lic highest.apex.meta_lic lib/libb.so.meta_lic restricted",
-				"highest.apex.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic restricted",
-				"highest.apex.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
-				"highest.apex.meta_lic lib/libc.a.meta_lic lib/liba.so.meta_lic restricted",
-				"highest.apex.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic reciprocal",
-				"highest.apex.meta_lic lib/libd.so.meta_lic lib/libb.so.meta_lic restricted",
-				"lib/liba.so.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic restricted",
-				"lib/libb.so.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
-				"lib/libc.a.meta_lic lib/libc.a.meta_lic lib/liba.so.meta_lic restricted",
-				"lib/libc.a.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic reciprocal",
-				"lib/libd.so.meta_lic lib/libd.so.meta_lic lib/libb.so.meta_lic restricted",
-				"lib/libd.so.meta_lic lib/libd.so.meta_lic lib/libd.so.meta_lic notice",
+				"bin/bin1.meta_lic bin/bin1.meta_lic notice:restricted_allows_dynamic_linking",
+				"bin/bin1.meta_lic lib/liba.so.meta_lic restricted_allows_dynamic_linking",
+				"bin/bin1.meta_lic lib/libc.a.meta_lic reciprocal:restricted_allows_dynamic_linking",
+				"bin/bin2.meta_lic bin/bin2.meta_lic notice:restricted",
+				"bin/bin2.meta_lic lib/libb.so.meta_lic restricted",
+				"highest.apex.meta_lic bin/bin1.meta_lic notice:restricted_allows_dynamic_linking",
+				"highest.apex.meta_lic bin/bin2.meta_lic notice:restricted",
+				"highest.apex.meta_lic highest.apex.meta_lic notice:restricted:restricted_allows_dynamic_linking",
+				"highest.apex.meta_lic lib/liba.so.meta_lic restricted_allows_dynamic_linking",
+				"highest.apex.meta_lic lib/libb.so.meta_lic restricted",
+				"highest.apex.meta_lic lib/libc.a.meta_lic reciprocal:restricted_allows_dynamic_linking",
+				"lib/liba.so.meta_lic lib/liba.so.meta_lic restricted_allows_dynamic_linking",
+				"lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
 			},
 		},
 		{
@@ -623,15 +557,15 @@
 			name:      "apex_trimmed_notice",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"notice"},
+				conditions:  []compliance.LicenseCondition{compliance.NoticeCondition},
 				stripPrefix: "testdata/restricted/",
 			},
 			expectedOut: []string{
-				"bin/bin1.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
-				"bin/bin2.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic notice",
-				"highest.apex.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
-				"highest.apex.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic notice",
-				"highest.apex.meta_lic highest.apex.meta_lic highest.apex.meta_lic notice",
+				"bin/bin1.meta_lic bin/bin1.meta_lic notice",
+				"bin/bin2.meta_lic bin/bin2.meta_lic notice",
+				"highest.apex.meta_lic bin/bin1.meta_lic notice",
+				"highest.apex.meta_lic bin/bin2.meta_lic notice",
+				"highest.apex.meta_lic highest.apex.meta_lic notice",
 			},
 		},
 		{
@@ -639,26 +573,23 @@
 			name:      "apex_trimmed_share",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"reciprocal", "restricted"},
+				conditions: compliance.ImpliesShared.AsList(),
 				stripPrefix: "testdata/restricted/",
 			},
 			expectedOut: []string{
-				"bin/bin1.meta_lic bin/bin1.meta_lic lib/liba.so.meta_lic restricted",
-				"bin/bin1.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic restricted",
-				"bin/bin1.meta_lic lib/libc.a.meta_lic lib/liba.so.meta_lic restricted",
-				"bin/bin1.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic reciprocal",
-				"bin/bin2.meta_lic bin/bin2.meta_lic lib/libb.so.meta_lic restricted",
-				"bin/bin2.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
-				"highest.apex.meta_lic bin/bin1.meta_lic lib/liba.so.meta_lic restricted",
-				"highest.apex.meta_lic bin/bin2.meta_lic lib/libb.so.meta_lic restricted",
-				"highest.apex.meta_lic highest.apex.meta_lic lib/liba.so.meta_lic restricted",
-				"highest.apex.meta_lic highest.apex.meta_lic lib/libb.so.meta_lic restricted",
-				"highest.apex.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic restricted",
-				"highest.apex.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
-				"highest.apex.meta_lic lib/libc.a.meta_lic lib/liba.so.meta_lic restricted",
-				"highest.apex.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic reciprocal",
-				"lib/liba.so.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic restricted",
-				"lib/libb.so.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
+				"bin/bin1.meta_lic bin/bin1.meta_lic restricted_allows_dynamic_linking",
+				"bin/bin1.meta_lic lib/liba.so.meta_lic restricted_allows_dynamic_linking",
+				"bin/bin1.meta_lic lib/libc.a.meta_lic reciprocal:restricted_allows_dynamic_linking",
+				"bin/bin2.meta_lic bin/bin2.meta_lic restricted",
+				"bin/bin2.meta_lic lib/libb.so.meta_lic restricted",
+				"highest.apex.meta_lic bin/bin1.meta_lic restricted_allows_dynamic_linking",
+				"highest.apex.meta_lic bin/bin2.meta_lic restricted",
+				"highest.apex.meta_lic highest.apex.meta_lic restricted:restricted_allows_dynamic_linking",
+				"highest.apex.meta_lic lib/liba.so.meta_lic restricted_allows_dynamic_linking",
+				"highest.apex.meta_lic lib/libb.so.meta_lic restricted",
+				"highest.apex.meta_lic lib/libc.a.meta_lic reciprocal:restricted_allows_dynamic_linking",
+				"lib/liba.so.meta_lic lib/liba.so.meta_lic restricted_allows_dynamic_linking",
+				"lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
 			},
 		},
 		{
@@ -666,7 +597,7 @@
 			name:      "apex_trimmed_private",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"proprietary"},
+				conditions: compliance.ImpliesPrivate.AsList(),
 				stripPrefix: "testdata/restricted/",
 			},
 			expectedOut: []string{},
@@ -676,26 +607,23 @@
 			name:      "apex_trimmed_share_private",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"reciprocal", "restricted", "proprietary"},
+				conditions:  append(compliance.ImpliesShared.AsList(),compliance.ImpliesPrivate.AsList()...),
 				stripPrefix: "testdata/restricted/",
 			},
 			expectedOut: []string{
-				"bin/bin1.meta_lic bin/bin1.meta_lic lib/liba.so.meta_lic restricted",
-				"bin/bin1.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic restricted",
-				"bin/bin1.meta_lic lib/libc.a.meta_lic lib/liba.so.meta_lic restricted",
-				"bin/bin1.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic reciprocal",
-				"bin/bin2.meta_lic bin/bin2.meta_lic lib/libb.so.meta_lic restricted",
-				"bin/bin2.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
-				"highest.apex.meta_lic bin/bin1.meta_lic lib/liba.so.meta_lic restricted",
-				"highest.apex.meta_lic bin/bin2.meta_lic lib/libb.so.meta_lic restricted",
-				"highest.apex.meta_lic highest.apex.meta_lic lib/liba.so.meta_lic restricted",
-				"highest.apex.meta_lic highest.apex.meta_lic lib/libb.so.meta_lic restricted",
-				"highest.apex.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic restricted",
-				"highest.apex.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
-				"highest.apex.meta_lic lib/libc.a.meta_lic lib/liba.so.meta_lic restricted",
-				"highest.apex.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic reciprocal",
-				"lib/liba.so.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic restricted",
-				"lib/libb.so.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
+				"bin/bin1.meta_lic bin/bin1.meta_lic restricted_allows_dynamic_linking",
+				"bin/bin1.meta_lic lib/liba.so.meta_lic restricted_allows_dynamic_linking",
+				"bin/bin1.meta_lic lib/libc.a.meta_lic reciprocal:restricted_allows_dynamic_linking",
+				"bin/bin2.meta_lic bin/bin2.meta_lic restricted",
+				"bin/bin2.meta_lic lib/libb.so.meta_lic restricted",
+				"highest.apex.meta_lic bin/bin1.meta_lic restricted_allows_dynamic_linking",
+				"highest.apex.meta_lic bin/bin2.meta_lic restricted",
+				"highest.apex.meta_lic highest.apex.meta_lic restricted:restricted_allows_dynamic_linking",
+				"highest.apex.meta_lic lib/liba.so.meta_lic restricted_allows_dynamic_linking",
+				"highest.apex.meta_lic lib/libb.so.meta_lic restricted",
+				"highest.apex.meta_lic lib/libc.a.meta_lic reciprocal:restricted_allows_dynamic_linking",
+				"lib/liba.so.meta_lic lib/liba.so.meta_lic restricted_allows_dynamic_linking",
+				"lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
 			},
 		},
 		{
@@ -704,33 +632,19 @@
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx:       context{stripPrefix: "testdata/restricted/", labelConditions: true},
 			expectedOut: []string{
-				"bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice",
-				"bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice lib/liba.so.meta_lic:restricted restricted",
-				"bin/bin1.meta_lic:notice lib/liba.so.meta_lic:restricted lib/liba.so.meta_lic:restricted restricted",
-				"bin/bin1.meta_lic:notice lib/libc.a.meta_lic:reciprocal lib/liba.so.meta_lic:restricted restricted",
-				"bin/bin1.meta_lic:notice lib/libc.a.meta_lic:reciprocal lib/libc.a.meta_lic:reciprocal reciprocal",
-				"bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice notice",
-				"bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice lib/libb.so.meta_lic:restricted restricted",
-				"bin/bin2.meta_lic:notice lib/libb.so.meta_lic:restricted lib/libb.so.meta_lic:restricted restricted",
-				"bin/bin2.meta_lic:notice lib/libd.so.meta_lic:notice lib/libb.so.meta_lic:restricted restricted",
-				"highest.apex.meta_lic:notice bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice",
-				"highest.apex.meta_lic:notice bin/bin1.meta_lic:notice lib/liba.so.meta_lic:restricted restricted",
-				"highest.apex.meta_lic:notice bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice notice",
-				"highest.apex.meta_lic:notice bin/bin2.meta_lic:notice lib/libb.so.meta_lic:restricted restricted",
-				"highest.apex.meta_lic:notice highest.apex.meta_lic:notice highest.apex.meta_lic:notice notice",
-				"highest.apex.meta_lic:notice highest.apex.meta_lic:notice lib/liba.so.meta_lic:restricted restricted",
-				"highest.apex.meta_lic:notice highest.apex.meta_lic:notice lib/libb.so.meta_lic:restricted restricted",
-				"highest.apex.meta_lic:notice lib/liba.so.meta_lic:restricted lib/liba.so.meta_lic:restricted restricted",
-				"highest.apex.meta_lic:notice lib/libb.so.meta_lic:restricted lib/libb.so.meta_lic:restricted restricted",
-				"highest.apex.meta_lic:notice lib/libc.a.meta_lic:reciprocal lib/liba.so.meta_lic:restricted restricted",
-				"highest.apex.meta_lic:notice lib/libc.a.meta_lic:reciprocal lib/libc.a.meta_lic:reciprocal reciprocal",
-				"highest.apex.meta_lic:notice lib/libd.so.meta_lic:notice lib/libb.so.meta_lic:restricted restricted",
-				"lib/liba.so.meta_lic:restricted lib/liba.so.meta_lic:restricted lib/liba.so.meta_lic:restricted restricted",
-				"lib/libb.so.meta_lic:restricted lib/libb.so.meta_lic:restricted lib/libb.so.meta_lic:restricted restricted",
-				"lib/libc.a.meta_lic:reciprocal lib/libc.a.meta_lic:reciprocal lib/liba.so.meta_lic:restricted restricted",
-				"lib/libc.a.meta_lic:reciprocal lib/libc.a.meta_lic:reciprocal lib/libc.a.meta_lic:reciprocal reciprocal",
-				"lib/libd.so.meta_lic:notice lib/libd.so.meta_lic:notice lib/libb.so.meta_lic:restricted restricted",
-				"lib/libd.so.meta_lic:notice lib/libd.so.meta_lic:notice lib/libd.so.meta_lic:notice notice",
+				"bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice:restricted_allows_dynamic_linking",
+				"bin/bin1.meta_lic:notice lib/liba.so.meta_lic:restricted_allows_dynamic_linking restricted_allows_dynamic_linking",
+				"bin/bin1.meta_lic:notice lib/libc.a.meta_lic:reciprocal reciprocal:restricted_allows_dynamic_linking",
+				"bin/bin2.meta_lic:notice bin/bin2.meta_lic:notice notice:restricted",
+				"bin/bin2.meta_lic:notice lib/libb.so.meta_lic:restricted restricted",
+				"highest.apex.meta_lic:notice bin/bin1.meta_lic:notice notice:restricted_allows_dynamic_linking",
+				"highest.apex.meta_lic:notice bin/bin2.meta_lic:notice notice:restricted",
+				"highest.apex.meta_lic:notice highest.apex.meta_lic:notice notice:restricted:restricted_allows_dynamic_linking",
+				"highest.apex.meta_lic:notice lib/liba.so.meta_lic:restricted_allows_dynamic_linking restricted_allows_dynamic_linking",
+				"highest.apex.meta_lic:notice lib/libb.so.meta_lic:restricted restricted",
+				"highest.apex.meta_lic:notice lib/libc.a.meta_lic:reciprocal reciprocal:restricted_allows_dynamic_linking",
+				"lib/liba.so.meta_lic:restricted_allows_dynamic_linking lib/liba.so.meta_lic:restricted_allows_dynamic_linking restricted_allows_dynamic_linking",
+				"lib/libb.so.meta_lic:restricted lib/libb.so.meta_lic:restricted restricted",
 			},
 		},
 		{
@@ -738,33 +652,19 @@
 			name:      "container",
 			roots:     []string{"container.zip.meta_lic"},
 			expectedOut: []string{
-				"testdata/restricted/bin/bin1.meta_lic testdata/restricted/bin/bin1.meta_lic testdata/restricted/bin/bin1.meta_lic notice",
-				"testdata/restricted/bin/bin1.meta_lic testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
-				"testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
-				"testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
-				"testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal",
-				"testdata/restricted/bin/bin2.meta_lic testdata/restricted/bin/bin2.meta_lic testdata/restricted/bin/bin2.meta_lic notice",
-				"testdata/restricted/bin/bin2.meta_lic testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
-				"testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libb.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
-				"testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libd.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
-				"testdata/restricted/container.zip.meta_lic testdata/restricted/bin/bin1.meta_lic testdata/restricted/bin/bin1.meta_lic notice",
-				"testdata/restricted/container.zip.meta_lic testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
-				"testdata/restricted/container.zip.meta_lic testdata/restricted/bin/bin2.meta_lic testdata/restricted/bin/bin2.meta_lic notice",
-				"testdata/restricted/container.zip.meta_lic testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
-				"testdata/restricted/container.zip.meta_lic testdata/restricted/container.zip.meta_lic testdata/restricted/container.zip.meta_lic notice",
-				"testdata/restricted/container.zip.meta_lic testdata/restricted/container.zip.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
-				"testdata/restricted/container.zip.meta_lic testdata/restricted/container.zip.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
-				"testdata/restricted/container.zip.meta_lic testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
-				"testdata/restricted/container.zip.meta_lic testdata/restricted/lib/libb.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
-				"testdata/restricted/container.zip.meta_lic testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
-				"testdata/restricted/container.zip.meta_lic testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal",
-				"testdata/restricted/container.zip.meta_lic testdata/restricted/lib/libd.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
-				"testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
-				"testdata/restricted/lib/libb.so.meta_lic testdata/restricted/lib/libb.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
-				"testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
-				"testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal",
-				"testdata/restricted/lib/libd.so.meta_lic testdata/restricted/lib/libd.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
-				"testdata/restricted/lib/libd.so.meta_lic testdata/restricted/lib/libd.so.meta_lic testdata/restricted/lib/libd.so.meta_lic notice",
+				"testdata/restricted/bin/bin1.meta_lic testdata/restricted/bin/bin1.meta_lic notice:restricted_allows_dynamic_linking",
+				"testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted_allows_dynamic_linking",
+				"testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal:restricted_allows_dynamic_linking",
+				"testdata/restricted/bin/bin2.meta_lic testdata/restricted/bin/bin2.meta_lic notice:restricted",
+				"testdata/restricted/bin/bin2.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
+				"testdata/restricted/container.zip.meta_lic testdata/restricted/bin/bin1.meta_lic notice:restricted_allows_dynamic_linking",
+				"testdata/restricted/container.zip.meta_lic testdata/restricted/bin/bin2.meta_lic notice:restricted",
+				"testdata/restricted/container.zip.meta_lic testdata/restricted/container.zip.meta_lic notice:restricted:restricted_allows_dynamic_linking",
+				"testdata/restricted/container.zip.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted_allows_dynamic_linking",
+				"testdata/restricted/container.zip.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
+				"testdata/restricted/container.zip.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal:restricted_allows_dynamic_linking",
+				"testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted_allows_dynamic_linking",
+				"testdata/restricted/lib/libb.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
 			},
 		},
 		{
@@ -772,16 +672,8 @@
 			name:      "application",
 			roots:     []string{"application.meta_lic"},
 			expectedOut: []string{
-				"testdata/restricted/application.meta_lic testdata/restricted/application.meta_lic testdata/restricted/application.meta_lic notice",
-				"testdata/restricted/application.meta_lic testdata/restricted/application.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
-				"testdata/restricted/application.meta_lic testdata/restricted/application.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
-				"testdata/restricted/application.meta_lic testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
-				"testdata/restricted/application.meta_lic testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
-				"testdata/restricted/application.meta_lic testdata/restricted/lib/libb.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
-				"testdata/restricted/bin/bin3.meta_lic testdata/restricted/bin/bin3.meta_lic testdata/restricted/bin/bin3.meta_lic restricted",
-				"testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
-				"testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
-				"testdata/restricted/lib/libb.so.meta_lic testdata/restricted/lib/libb.so.meta_lic testdata/restricted/lib/libb.so.meta_lic restricted",
+				"testdata/restricted/application.meta_lic testdata/restricted/application.meta_lic notice:restricted:restricted_allows_dynamic_linking",
+				"testdata/restricted/application.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted:restricted_allows_dynamic_linking",
 			},
 		},
 		{
@@ -789,14 +681,9 @@
 			name:      "binary",
 			roots:     []string{"bin/bin1.meta_lic"},
 			expectedOut: []string{
-				"testdata/restricted/bin/bin1.meta_lic testdata/restricted/bin/bin1.meta_lic testdata/restricted/bin/bin1.meta_lic notice",
-				"testdata/restricted/bin/bin1.meta_lic testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
-				"testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
-				"testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
-				"testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal",
-				"testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
-				"testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted",
-				"testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/libc.a.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal",
+				"testdata/restricted/bin/bin1.meta_lic testdata/restricted/bin/bin1.meta_lic notice:restricted_allows_dynamic_linking",
+				"testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/liba.so.meta_lic restricted_allows_dynamic_linking",
+				"testdata/restricted/bin/bin1.meta_lic testdata/restricted/lib/libc.a.meta_lic reciprocal:restricted_allows_dynamic_linking",
 			},
 		},
 		{
@@ -804,7 +691,7 @@
 			name:      "library",
 			roots:     []string{"lib/libd.so.meta_lic"},
 			expectedOut: []string{
-				"testdata/restricted/lib/libd.so.meta_lic testdata/restricted/lib/libd.so.meta_lic testdata/restricted/lib/libd.so.meta_lic notice",
+				"testdata/restricted/lib/libd.so.meta_lic testdata/restricted/lib/libd.so.meta_lic notice",
 			},
 		},
 		{
@@ -812,27 +699,19 @@
 			name:      "apex",
 			roots:     []string{"highest.apex.meta_lic"},
 			expectedOut: []string{
-				"testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/bin/bin1.meta_lic notice",
-				"testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic by_exception_only:proprietary",
-				"testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/libc.a.meta_lic testdata/proprietary/lib/libc.a.meta_lic by_exception_only:proprietary",
-				"testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/bin/bin2.meta_lic by_exception_only:proprietary",
-				"testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
-				"testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libb.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
-				"testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libd.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
-				"testdata/proprietary/highest.apex.meta_lic testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/bin/bin1.meta_lic notice",
-				"testdata/proprietary/highest.apex.meta_lic testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/bin/bin2.meta_lic by_exception_only:proprietary",
-				"testdata/proprietary/highest.apex.meta_lic testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
-				"testdata/proprietary/highest.apex.meta_lic testdata/proprietary/highest.apex.meta_lic testdata/proprietary/highest.apex.meta_lic notice",
-				"testdata/proprietary/highest.apex.meta_lic testdata/proprietary/highest.apex.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
-				"testdata/proprietary/highest.apex.meta_lic testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic by_exception_only:proprietary",
-				"testdata/proprietary/highest.apex.meta_lic testdata/proprietary/lib/libb.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
-				"testdata/proprietary/highest.apex.meta_lic testdata/proprietary/lib/libc.a.meta_lic testdata/proprietary/lib/libc.a.meta_lic by_exception_only:proprietary",
-				"testdata/proprietary/highest.apex.meta_lic testdata/proprietary/lib/libd.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
-				"testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic by_exception_only:proprietary",
-				"testdata/proprietary/lib/libb.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
-				"testdata/proprietary/lib/libc.a.meta_lic testdata/proprietary/lib/libc.a.meta_lic testdata/proprietary/lib/libc.a.meta_lic by_exception_only:proprietary",
-				"testdata/proprietary/lib/libd.so.meta_lic testdata/proprietary/lib/libd.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
-				"testdata/proprietary/lib/libd.so.meta_lic testdata/proprietary/lib/libd.so.meta_lic testdata/proprietary/lib/libd.so.meta_lic notice",
+				"testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/bin/bin1.meta_lic notice",
+				"testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/liba.so.meta_lic proprietary:by_exception_only",
+				"testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/libc.a.meta_lic proprietary:by_exception_only",
+				"testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/bin/bin2.meta_lic restricted:proprietary:by_exception_only",
+				"testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
+				"testdata/proprietary/highest.apex.meta_lic testdata/proprietary/bin/bin1.meta_lic notice",
+				"testdata/proprietary/highest.apex.meta_lic testdata/proprietary/bin/bin2.meta_lic restricted:proprietary:by_exception_only",
+				"testdata/proprietary/highest.apex.meta_lic testdata/proprietary/highest.apex.meta_lic notice:restricted",
+				"testdata/proprietary/highest.apex.meta_lic testdata/proprietary/lib/liba.so.meta_lic proprietary:by_exception_only",
+				"testdata/proprietary/highest.apex.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
+				"testdata/proprietary/highest.apex.meta_lic testdata/proprietary/lib/libc.a.meta_lic proprietary:by_exception_only",
+				"testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic proprietary:by_exception_only",
+				"testdata/proprietary/lib/libb.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
 			},
 		},
 		{
@@ -841,27 +720,19 @@
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx:       context{stripPrefix: "testdata/proprietary/"},
 			expectedOut: []string{
-				"bin/bin1.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
-				"bin/bin1.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic by_exception_only:proprietary",
-				"bin/bin1.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic by_exception_only:proprietary",
-				"bin/bin2.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic by_exception_only:proprietary",
-				"bin/bin2.meta_lic bin/bin2.meta_lic lib/libb.so.meta_lic restricted",
-				"bin/bin2.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
-				"bin/bin2.meta_lic lib/libd.so.meta_lic lib/libb.so.meta_lic restricted",
-				"highest.apex.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
-				"highest.apex.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic by_exception_only:proprietary",
-				"highest.apex.meta_lic bin/bin2.meta_lic lib/libb.so.meta_lic restricted",
-				"highest.apex.meta_lic highest.apex.meta_lic highest.apex.meta_lic notice",
-				"highest.apex.meta_lic highest.apex.meta_lic lib/libb.so.meta_lic restricted",
-				"highest.apex.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic by_exception_only:proprietary",
-				"highest.apex.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
-				"highest.apex.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic by_exception_only:proprietary",
-				"highest.apex.meta_lic lib/libd.so.meta_lic lib/libb.so.meta_lic restricted",
-				"lib/liba.so.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic by_exception_only:proprietary",
-				"lib/libb.so.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
-				"lib/libc.a.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic by_exception_only:proprietary",
-				"lib/libd.so.meta_lic lib/libd.so.meta_lic lib/libb.so.meta_lic restricted",
-				"lib/libd.so.meta_lic lib/libd.so.meta_lic lib/libd.so.meta_lic notice",
+				"bin/bin1.meta_lic bin/bin1.meta_lic notice",
+				"bin/bin1.meta_lic lib/liba.so.meta_lic proprietary:by_exception_only",
+				"bin/bin1.meta_lic lib/libc.a.meta_lic proprietary:by_exception_only",
+				"bin/bin2.meta_lic bin/bin2.meta_lic restricted:proprietary:by_exception_only",
+				"bin/bin2.meta_lic lib/libb.so.meta_lic restricted",
+				"highest.apex.meta_lic bin/bin1.meta_lic notice",
+				"highest.apex.meta_lic bin/bin2.meta_lic restricted:proprietary:by_exception_only",
+				"highest.apex.meta_lic highest.apex.meta_lic notice:restricted",
+				"highest.apex.meta_lic lib/liba.so.meta_lic proprietary:by_exception_only",
+				"highest.apex.meta_lic lib/libb.so.meta_lic restricted",
+				"highest.apex.meta_lic lib/libc.a.meta_lic proprietary:by_exception_only",
+				"lib/liba.so.meta_lic lib/liba.so.meta_lic proprietary:by_exception_only",
+				"lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
 			},
 		},
 		{
@@ -869,13 +740,13 @@
 			name:      "apex_trimmed_notice",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"notice"},
+				conditions:  []compliance.LicenseCondition{compliance.NoticeCondition},
 				stripPrefix: "testdata/proprietary/",
 			},
 			expectedOut: []string{
-				"bin/bin1.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
-				"highest.apex.meta_lic bin/bin1.meta_lic bin/bin1.meta_lic notice",
-				"highest.apex.meta_lic highest.apex.meta_lic highest.apex.meta_lic notice",
+				"bin/bin1.meta_lic bin/bin1.meta_lic notice",
+				"highest.apex.meta_lic bin/bin1.meta_lic notice",
+				"highest.apex.meta_lic highest.apex.meta_lic notice",
 			},
 		},
 		{
@@ -883,16 +754,16 @@
 			name:      "apex_trimmed_share",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"reciprocal", "restricted"},
+				conditions: compliance.ImpliesShared.AsList(),
 				stripPrefix: "testdata/proprietary/",
 			},
 			expectedOut: []string{
-				"bin/bin2.meta_lic bin/bin2.meta_lic lib/libb.so.meta_lic restricted",
-				"bin/bin2.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
-				"highest.apex.meta_lic bin/bin2.meta_lic lib/libb.so.meta_lic restricted",
-				"highest.apex.meta_lic highest.apex.meta_lic lib/libb.so.meta_lic restricted",
-				"highest.apex.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
-				"lib/libb.so.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
+				"bin/bin2.meta_lic bin/bin2.meta_lic restricted",
+				"bin/bin2.meta_lic lib/libb.so.meta_lic restricted",
+				"highest.apex.meta_lic bin/bin2.meta_lic restricted",
+				"highest.apex.meta_lic highest.apex.meta_lic restricted",
+				"highest.apex.meta_lic lib/libb.so.meta_lic restricted",
+				"lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
 			},
 		},
 		{
@@ -900,17 +771,17 @@
 			name:      "apex_trimmed_private",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"proprietary"},
+				conditions: compliance.ImpliesPrivate.AsList(),
 				stripPrefix: "testdata/proprietary/",
 			},
 			expectedOut: []string{
-				"bin/bin1.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic proprietary",
-				"bin/bin1.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic proprietary",
-				"bin/bin2.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic proprietary",
-				"highest.apex.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic proprietary",
-				"highest.apex.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic proprietary",
-				"highest.apex.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic proprietary",
-				"lib/liba.so.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic proprietary",
+				"bin/bin1.meta_lic lib/liba.so.meta_lic proprietary",
+				"bin/bin1.meta_lic lib/libc.a.meta_lic proprietary",
+				"bin/bin2.meta_lic bin/bin2.meta_lic proprietary",
+				"highest.apex.meta_lic bin/bin2.meta_lic proprietary",
+				"highest.apex.meta_lic lib/liba.so.meta_lic proprietary",
+				"highest.apex.meta_lic lib/libc.a.meta_lic proprietary",
+				"lib/liba.so.meta_lic lib/liba.so.meta_lic proprietary",
 			},
 		},
 		{
@@ -918,23 +789,21 @@
 			name:      "apex_trimmed_share_private",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"reciprocal", "restricted", "proprietary"},
+				conditions:  append(compliance.ImpliesShared.AsList(),compliance.ImpliesPrivate.AsList()...),
 				stripPrefix: "testdata/proprietary/",
 			},
 			expectedOut: []string{
-				"bin/bin1.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic proprietary",
-				"bin/bin1.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic proprietary",
-				"bin/bin2.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic proprietary",
-				"bin/bin2.meta_lic bin/bin2.meta_lic lib/libb.so.meta_lic restricted",
-				"bin/bin2.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
-				"highest.apex.meta_lic bin/bin2.meta_lic bin/bin2.meta_lic proprietary",
-				"highest.apex.meta_lic bin/bin2.meta_lic lib/libb.so.meta_lic restricted",
-				"highest.apex.meta_lic highest.apex.meta_lic lib/libb.so.meta_lic restricted",
-				"highest.apex.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic proprietary",
-				"highest.apex.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
-				"highest.apex.meta_lic lib/libc.a.meta_lic lib/libc.a.meta_lic proprietary",
-				"lib/liba.so.meta_lic lib/liba.so.meta_lic lib/liba.so.meta_lic proprietary",
-				"lib/libb.so.meta_lic lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
+				"bin/bin1.meta_lic lib/liba.so.meta_lic proprietary",
+				"bin/bin1.meta_lic lib/libc.a.meta_lic proprietary",
+				"bin/bin2.meta_lic bin/bin2.meta_lic restricted:proprietary",
+				"bin/bin2.meta_lic lib/libb.so.meta_lic restricted",
+				"highest.apex.meta_lic bin/bin2.meta_lic restricted:proprietary",
+				"highest.apex.meta_lic highest.apex.meta_lic restricted",
+				"highest.apex.meta_lic lib/liba.so.meta_lic proprietary",
+				"highest.apex.meta_lic lib/libb.so.meta_lic restricted",
+				"highest.apex.meta_lic lib/libc.a.meta_lic proprietary",
+				"lib/liba.so.meta_lic lib/liba.so.meta_lic proprietary",
+				"lib/libb.so.meta_lic lib/libb.so.meta_lic restricted",
 			},
 		},
 		{
@@ -943,27 +812,19 @@
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx:       context{stripPrefix: "testdata/proprietary/", labelConditions: true},
 			expectedOut: []string{
-				"bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice",
-				"bin/bin1.meta_lic:notice lib/liba.so.meta_lic:by_exception_only:proprietary lib/liba.so.meta_lic:by_exception_only:proprietary by_exception_only:proprietary",
-				"bin/bin1.meta_lic:notice lib/libc.a.meta_lic:by_exception_only:proprietary lib/libc.a.meta_lic:by_exception_only:proprietary by_exception_only:proprietary",
-				"bin/bin2.meta_lic:by_exception_only:proprietary bin/bin2.meta_lic:by_exception_only:proprietary bin/bin2.meta_lic:by_exception_only:proprietary by_exception_only:proprietary",
-				"bin/bin2.meta_lic:by_exception_only:proprietary bin/bin2.meta_lic:by_exception_only:proprietary lib/libb.so.meta_lic:restricted restricted",
-				"bin/bin2.meta_lic:by_exception_only:proprietary lib/libb.so.meta_lic:restricted lib/libb.so.meta_lic:restricted restricted",
-				"bin/bin2.meta_lic:by_exception_only:proprietary lib/libd.so.meta_lic:notice lib/libb.so.meta_lic:restricted restricted",
-				"highest.apex.meta_lic:notice bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice",
-				"highest.apex.meta_lic:notice bin/bin2.meta_lic:by_exception_only:proprietary bin/bin2.meta_lic:by_exception_only:proprietary by_exception_only:proprietary",
-				"highest.apex.meta_lic:notice bin/bin2.meta_lic:by_exception_only:proprietary lib/libb.so.meta_lic:restricted restricted",
-				"highest.apex.meta_lic:notice highest.apex.meta_lic:notice highest.apex.meta_lic:notice notice",
-				"highest.apex.meta_lic:notice highest.apex.meta_lic:notice lib/libb.so.meta_lic:restricted restricted",
-				"highest.apex.meta_lic:notice lib/liba.so.meta_lic:by_exception_only:proprietary lib/liba.so.meta_lic:by_exception_only:proprietary by_exception_only:proprietary",
-				"highest.apex.meta_lic:notice lib/libb.so.meta_lic:restricted lib/libb.so.meta_lic:restricted restricted",
-				"highest.apex.meta_lic:notice lib/libc.a.meta_lic:by_exception_only:proprietary lib/libc.a.meta_lic:by_exception_only:proprietary by_exception_only:proprietary",
-				"highest.apex.meta_lic:notice lib/libd.so.meta_lic:notice lib/libb.so.meta_lic:restricted restricted",
-				"lib/liba.so.meta_lic:by_exception_only:proprietary lib/liba.so.meta_lic:by_exception_only:proprietary lib/liba.so.meta_lic:by_exception_only:proprietary by_exception_only:proprietary",
-				"lib/libb.so.meta_lic:restricted lib/libb.so.meta_lic:restricted lib/libb.so.meta_lic:restricted restricted",
-				"lib/libc.a.meta_lic:by_exception_only:proprietary lib/libc.a.meta_lic:by_exception_only:proprietary lib/libc.a.meta_lic:by_exception_only:proprietary by_exception_only:proprietary",
-				"lib/libd.so.meta_lic:notice lib/libd.so.meta_lic:notice lib/libb.so.meta_lic:restricted restricted",
-				"lib/libd.so.meta_lic:notice lib/libd.so.meta_lic:notice lib/libd.so.meta_lic:notice notice",
+				"bin/bin1.meta_lic:notice bin/bin1.meta_lic:notice notice",
+				"bin/bin1.meta_lic:notice lib/liba.so.meta_lic:proprietary:by_exception_only proprietary:by_exception_only",
+				"bin/bin1.meta_lic:notice lib/libc.a.meta_lic:proprietary:by_exception_only proprietary:by_exception_only",
+				"bin/bin2.meta_lic:proprietary:by_exception_only bin/bin2.meta_lic:proprietary:by_exception_only restricted:proprietary:by_exception_only",
+				"bin/bin2.meta_lic:proprietary:by_exception_only lib/libb.so.meta_lic:restricted restricted",
+				"highest.apex.meta_lic:notice bin/bin1.meta_lic:notice notice",
+				"highest.apex.meta_lic:notice bin/bin2.meta_lic:proprietary:by_exception_only restricted:proprietary:by_exception_only",
+				"highest.apex.meta_lic:notice highest.apex.meta_lic:notice notice:restricted",
+				"highest.apex.meta_lic:notice lib/liba.so.meta_lic:proprietary:by_exception_only proprietary:by_exception_only",
+				"highest.apex.meta_lic:notice lib/libb.so.meta_lic:restricted restricted",
+				"highest.apex.meta_lic:notice lib/libc.a.meta_lic:proprietary:by_exception_only proprietary:by_exception_only",
+				"lib/liba.so.meta_lic:proprietary:by_exception_only lib/liba.so.meta_lic:proprietary:by_exception_only proprietary:by_exception_only",
+				"lib/libb.so.meta_lic:restricted lib/libb.so.meta_lic:restricted restricted",
 			},
 		},
 		{
@@ -971,27 +832,19 @@
 			name:      "container",
 			roots:     []string{"container.zip.meta_lic"},
 			expectedOut: []string{
-				"testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/bin/bin1.meta_lic notice",
-				"testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic by_exception_only:proprietary",
-				"testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/libc.a.meta_lic testdata/proprietary/lib/libc.a.meta_lic by_exception_only:proprietary",
-				"testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/bin/bin2.meta_lic by_exception_only:proprietary",
-				"testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
-				"testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libb.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
-				"testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libd.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
-				"testdata/proprietary/container.zip.meta_lic testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/bin/bin1.meta_lic notice",
-				"testdata/proprietary/container.zip.meta_lic testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/bin/bin2.meta_lic by_exception_only:proprietary",
-				"testdata/proprietary/container.zip.meta_lic testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
-				"testdata/proprietary/container.zip.meta_lic testdata/proprietary/container.zip.meta_lic testdata/proprietary/container.zip.meta_lic notice",
-				"testdata/proprietary/container.zip.meta_lic testdata/proprietary/container.zip.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
-				"testdata/proprietary/container.zip.meta_lic testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic by_exception_only:proprietary",
-				"testdata/proprietary/container.zip.meta_lic testdata/proprietary/lib/libb.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
-				"testdata/proprietary/container.zip.meta_lic testdata/proprietary/lib/libc.a.meta_lic testdata/proprietary/lib/libc.a.meta_lic by_exception_only:proprietary",
-				"testdata/proprietary/container.zip.meta_lic testdata/proprietary/lib/libd.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
-				"testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic by_exception_only:proprietary",
-				"testdata/proprietary/lib/libb.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
-				"testdata/proprietary/lib/libc.a.meta_lic testdata/proprietary/lib/libc.a.meta_lic testdata/proprietary/lib/libc.a.meta_lic by_exception_only:proprietary",
-				"testdata/proprietary/lib/libd.so.meta_lic testdata/proprietary/lib/libd.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
-				"testdata/proprietary/lib/libd.so.meta_lic testdata/proprietary/lib/libd.so.meta_lic testdata/proprietary/lib/libd.so.meta_lic notice",
+				"testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/bin/bin1.meta_lic notice",
+				"testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/liba.so.meta_lic proprietary:by_exception_only",
+				"testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/libc.a.meta_lic proprietary:by_exception_only",
+				"testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/bin/bin2.meta_lic restricted:proprietary:by_exception_only",
+				"testdata/proprietary/bin/bin2.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
+				"testdata/proprietary/container.zip.meta_lic testdata/proprietary/bin/bin1.meta_lic notice",
+				"testdata/proprietary/container.zip.meta_lic testdata/proprietary/bin/bin2.meta_lic restricted:proprietary:by_exception_only",
+				"testdata/proprietary/container.zip.meta_lic testdata/proprietary/container.zip.meta_lic notice:restricted",
+				"testdata/proprietary/container.zip.meta_lic testdata/proprietary/lib/liba.so.meta_lic proprietary:by_exception_only",
+				"testdata/proprietary/container.zip.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
+				"testdata/proprietary/container.zip.meta_lic testdata/proprietary/lib/libc.a.meta_lic proprietary:by_exception_only",
+				"testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic proprietary:by_exception_only",
+				"testdata/proprietary/lib/libb.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
 			},
 		},
 		{
@@ -999,15 +852,8 @@
 			name:      "application",
 			roots:     []string{"application.meta_lic"},
 			expectedOut: []string{
-				"testdata/proprietary/application.meta_lic testdata/proprietary/application.meta_lic testdata/proprietary/application.meta_lic notice",
-				"testdata/proprietary/application.meta_lic testdata/proprietary/application.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
-				"testdata/proprietary/application.meta_lic testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic by_exception_only:proprietary",
-				"testdata/proprietary/application.meta_lic testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
-				"testdata/proprietary/application.meta_lic testdata/proprietary/lib/libb.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
-				"testdata/proprietary/bin/bin3.meta_lic testdata/proprietary/bin/bin3.meta_lic testdata/proprietary/bin/bin3.meta_lic restricted",
-				"testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic by_exception_only:proprietary",
-				"testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
-				"testdata/proprietary/lib/libb.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic testdata/proprietary/lib/libb.so.meta_lic restricted",
+				"testdata/proprietary/application.meta_lic testdata/proprietary/application.meta_lic notice:restricted",
+				"testdata/proprietary/application.meta_lic testdata/proprietary/lib/liba.so.meta_lic restricted:proprietary:by_exception_only",
 			},
 		},
 		{
@@ -1015,11 +861,9 @@
 			name:      "binary",
 			roots:     []string{"bin/bin1.meta_lic"},
 			expectedOut: []string{
-				"testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/bin/bin1.meta_lic notice",
-				"testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic by_exception_only:proprietary",
-				"testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/libc.a.meta_lic testdata/proprietary/lib/libc.a.meta_lic by_exception_only:proprietary",
-				"testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic testdata/proprietary/lib/liba.so.meta_lic by_exception_only:proprietary",
-				"testdata/proprietary/lib/libc.a.meta_lic testdata/proprietary/lib/libc.a.meta_lic testdata/proprietary/lib/libc.a.meta_lic by_exception_only:proprietary",
+				"testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/bin/bin1.meta_lic notice",
+				"testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/liba.so.meta_lic proprietary:by_exception_only",
+				"testdata/proprietary/bin/bin1.meta_lic testdata/proprietary/lib/libc.a.meta_lic proprietary:by_exception_only",
 			},
 		},
 		{
@@ -1027,7 +871,7 @@
 			name:      "library",
 			roots:     []string{"lib/libd.so.meta_lic"},
 			expectedOut: []string{
-				"testdata/proprietary/lib/libd.so.meta_lic testdata/proprietary/lib/libd.so.meta_lic testdata/proprietary/lib/libd.so.meta_lic notice",
+				"testdata/proprietary/lib/libd.so.meta_lic testdata/proprietary/lib/libd.so.meta_lic notice",
 			},
 		},
 	}
@@ -1046,7 +890,7 @@
 			for _, r := range tt.roots {
 				rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r)
 			}
-			err := dumpResolutions(&tt.ctx, stdout, stderr, rootFiles...)
+			_, err := dumpResolutions(&tt.ctx, stdout, stderr, rootFiles...)
 			if err != nil {
 				t.Fatalf("dumpresolutions: error = %v, stderr = %v", err, stderr)
 				return
@@ -1076,7 +920,7 @@
 }
 
 type matcher interface {
-	matchString(*testContext) string
+	matchString(*testContext, *compliance.LicenseGraph) string
 	typeString() string
 }
 
@@ -1085,10 +929,23 @@
 	conditions []string
 }
 
-func (tm *targetMatcher) matchString(ctx *testContext) string {
+// newTestCondition constructs a test license condition in the license graph.
+func newTestCondition(lg *compliance.LicenseGraph, conditionName... string) compliance.LicenseConditionSet {
+	cs := compliance.NewLicenseConditionSet()
+	for _, name := range conditionName {
+		cs = cs.Plus(compliance.RecognizedConditionNames[name])
+	}
+	if cs.IsEmpty() && len(conditionName) != 0 {
+		panic(fmt.Errorf("attempt to create unrecognized condition: %q", conditionName))
+	}
+	return cs
+}
+
+func (tm *targetMatcher) matchString(ctx *testContext, lg *compliance.LicenseGraph) string {
+	cs := newTestCondition(lg, tm.conditions...)
 	m := tm.target
-	if len(tm.conditions) > 0 {
-		m += "\\n" + strings.Join(tm.conditions, "\\n")
+	if !cs.IsEmpty() {
+		m += "\\n" + strings.Join(cs.Names(), "\\n")
 	}
 	m = ctx.nodes[tm.target] + " [label=\"" + m + "\"];"
 	return m
@@ -1101,14 +958,13 @@
 type resolutionMatcher struct {
 	appliesTo  string
 	actsOn     string
-	origin     string
 	conditions []string
 }
 
-func (rm *resolutionMatcher) matchString(ctx *testContext) string {
-	return ctx.nodes[rm.appliesTo] + " -> " + ctx.nodes[rm.actsOn] + "; " +
-		ctx.nodes[rm.actsOn] + " -> " + ctx.nodes[rm.origin] +
-		" [label=\"" + strings.Join(rm.conditions, "\\n") + "\"];"
+func (rm *resolutionMatcher) matchString(ctx *testContext, lg *compliance.LicenseGraph) string {
+	cs := newTestCondition(lg, rm.conditions...)
+	return ctx.nodes[rm.appliesTo] + " -> " + ctx.nodes[rm.actsOn] +
+		" [label=\"" + strings.Join(cs.Names(), "\\n") + "\"];"
 }
 
 func (rm *resolutionMatcher) typeString() string {
@@ -1125,7 +981,7 @@
 	}
 }
 
-func matchResolution(appliesTo, actsOn, origin string, conditions ...string) getMatcher {
+func matchResolution(appliesTo, actsOn string, conditions ...string) getMatcher {
 	return func(ctx *testContext) matcher {
 		if _, ok := ctx.nodes[appliesTo]; !ok {
 			ctx.nodes[appliesTo] = fmt.Sprintf("unknown%d", ctx.nextNode)
@@ -1135,11 +991,7 @@
 			ctx.nodes[actsOn] = fmt.Sprintf("unknown%d", ctx.nextNode)
 			ctx.nextNode++
 		}
-		if _, ok := ctx.nodes[origin]; !ok {
-			ctx.nodes[origin] = fmt.Sprintf("unknown%d", ctx.nextNode)
-			ctx.nextNode++
-		}
-		return &resolutionMatcher{appliesTo, actsOn, origin, append([]string{}, conditions...)}
+		return &resolutionMatcher{appliesTo, actsOn, append([]string{}, conditions...)}
 	}
 }
 
@@ -1162,76 +1014,53 @@
 				matchTarget("testdata/firstparty/bin/bin2.meta_lic"),
 				matchTarget("testdata/firstparty/highest.apex.meta_lic"),
 				matchTarget("testdata/firstparty/lib/libb.so.meta_lic"),
-				matchTarget("testdata/firstparty/lib/libd.so.meta_lic"),
 				matchResolution(
 					"testdata/firstparty/bin/bin1.meta_lic",
 					"testdata/firstparty/bin/bin1.meta_lic",
-					"testdata/firstparty/bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/firstparty/bin/bin1.meta_lic",
 					"testdata/firstparty/lib/liba.so.meta_lic",
-					"testdata/firstparty/lib/liba.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/firstparty/bin/bin1.meta_lic",
 					"testdata/firstparty/lib/libc.a.meta_lic",
-					"testdata/firstparty/lib/libc.a.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/firstparty/bin/bin2.meta_lic",
 					"testdata/firstparty/bin/bin2.meta_lic",
-					"testdata/firstparty/bin/bin2.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/firstparty/highest.apex.meta_lic",
 					"testdata/firstparty/bin/bin1.meta_lic",
-					"testdata/firstparty/bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/firstparty/highest.apex.meta_lic",
 					"testdata/firstparty/bin/bin2.meta_lic",
-					"testdata/firstparty/bin/bin2.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/firstparty/highest.apex.meta_lic",
 					"testdata/firstparty/highest.apex.meta_lic",
-					"testdata/firstparty/highest.apex.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/firstparty/highest.apex.meta_lic",
 					"testdata/firstparty/lib/liba.so.meta_lic",
-					"testdata/firstparty/lib/liba.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/firstparty/highest.apex.meta_lic",
 					"testdata/firstparty/lib/libb.so.meta_lic",
-					"testdata/firstparty/lib/libb.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/firstparty/highest.apex.meta_lic",
 					"testdata/firstparty/lib/libc.a.meta_lic",
-					"testdata/firstparty/lib/libc.a.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/firstparty/lib/liba.so.meta_lic",
 					"testdata/firstparty/lib/liba.so.meta_lic",
-					"testdata/firstparty/lib/liba.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/firstparty/lib/libb.so.meta_lic",
 					"testdata/firstparty/lib/libb.so.meta_lic",
-					"testdata/firstparty/lib/libb.so.meta_lic",
-					"notice"),
-				matchResolution(
-					"testdata/firstparty/lib/libc.a.meta_lic",
-					"testdata/firstparty/lib/libc.a.meta_lic",
-					"testdata/firstparty/lib/libc.a.meta_lic",
-					"notice"),
-				matchResolution(
-					"testdata/firstparty/lib/libd.so.meta_lic",
-					"testdata/firstparty/lib/libd.so.meta_lic",
-					"testdata/firstparty/lib/libd.so.meta_lic",
 					"notice"),
 			},
 		},
@@ -1247,76 +1076,53 @@
 				matchTarget("bin/bin2.meta_lic"),
 				matchTarget("highest.apex.meta_lic"),
 				matchTarget("lib/libb.so.meta_lic"),
-				matchTarget("lib/libd.so.meta_lic"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"bin/bin1.meta_lic",
-					"bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
 					"notice"),
 				matchResolution(
 					"bin/bin2.meta_lic",
 					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"bin/bin1.meta_lic",
-					"bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"highest.apex.meta_lic",
-					"highest.apex.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
 					"notice"),
 				matchResolution(
 					"lib/liba.so.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"lib/libb.so.meta_lic",
 					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
-					"notice"),
-				matchResolution(
-					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
-					"notice"),
-				matchResolution(
-					"lib/libd.so.meta_lic",
-					"lib/libd.so.meta_lic",
-					"lib/libd.so.meta_lic",
 					"notice"),
 			},
 		},
@@ -1325,7 +1131,7 @@
 			name:      "apex_trimmed_notice",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"notice"},
+				conditions:  []compliance.LicenseCondition{compliance.NoticeCondition},
 				stripPrefix: "testdata/firstparty/",
 			},
 			expectedOut: []getMatcher{
@@ -1338,62 +1144,50 @@
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"bin/bin1.meta_lic",
-					"bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
 					"notice"),
 				matchResolution(
 					"bin/bin2.meta_lic",
 					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"bin/bin1.meta_lic",
-					"bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"highest.apex.meta_lic",
-					"highest.apex.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
 					"notice"),
 				matchResolution(
 					"lib/liba.so.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"lib/libb.so.meta_lic",
 					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
 					"notice"),
 			},
 		},
@@ -1402,7 +1196,7 @@
 			name:      "apex_trimmed_share",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"reciprocal", "restricted"},
+				conditions:  compliance.ImpliesShared.AsList(),
 				stripPrefix: "testdata/firstparty/",
 			},
 			expectedOut: []getMatcher{},
@@ -1412,7 +1206,7 @@
 			name:      "apex_trimmed_private",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"proprietary"},
+				conditions:  compliance.ImpliesPrivate.AsList(),
 				stripPrefix: "testdata/firstparty/",
 			},
 			expectedOut: []getMatcher{},
@@ -1422,7 +1216,7 @@
 			name:      "apex_trimmed_share_private",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"reciprocal", "restricted", "proprietary"},
+				conditions:  compliance.ImpliesShared.Union(compliance.ImpliesPrivate).AsList(),
 				stripPrefix: "testdata/firstparty/",
 			},
 			expectedOut: []getMatcher{},
@@ -1439,76 +1233,53 @@
 				matchTarget("bin/bin2.meta_lic", "notice"),
 				matchTarget("highest.apex.meta_lic", "notice"),
 				matchTarget("lib/libb.so.meta_lic", "notice"),
-				matchTarget("lib/libd.so.meta_lic", "notice"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"bin/bin1.meta_lic",
-					"bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
 					"notice"),
 				matchResolution(
 					"bin/bin2.meta_lic",
 					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"bin/bin1.meta_lic",
-					"bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"highest.apex.meta_lic",
-					"highest.apex.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
 					"notice"),
 				matchResolution(
 					"lib/liba.so.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"lib/libb.so.meta_lic",
 					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
-					"notice"),
-				matchResolution(
-					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
-					"notice"),
-				matchResolution(
-					"lib/libd.so.meta_lic",
-					"lib/libd.so.meta_lic",
-					"lib/libd.so.meta_lic",
 					"notice"),
 			},
 		},
@@ -1523,76 +1294,53 @@
 				matchTarget("testdata/firstparty/bin/bin2.meta_lic"),
 				matchTarget("testdata/firstparty/container.zip.meta_lic"),
 				matchTarget("testdata/firstparty/lib/libb.so.meta_lic"),
-				matchTarget("testdata/firstparty/lib/libd.so.meta_lic"),
 				matchResolution(
 					"testdata/firstparty/bin/bin1.meta_lic",
 					"testdata/firstparty/bin/bin1.meta_lic",
-					"testdata/firstparty/bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/firstparty/bin/bin1.meta_lic",
 					"testdata/firstparty/lib/liba.so.meta_lic",
-					"testdata/firstparty/lib/liba.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/firstparty/bin/bin1.meta_lic",
 					"testdata/firstparty/lib/libc.a.meta_lic",
-					"testdata/firstparty/lib/libc.a.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/firstparty/bin/bin2.meta_lic",
 					"testdata/firstparty/bin/bin2.meta_lic",
-					"testdata/firstparty/bin/bin2.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/firstparty/container.zip.meta_lic",
 					"testdata/firstparty/bin/bin1.meta_lic",
-					"testdata/firstparty/bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/firstparty/container.zip.meta_lic",
 					"testdata/firstparty/bin/bin2.meta_lic",
-					"testdata/firstparty/bin/bin2.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/firstparty/container.zip.meta_lic",
 					"testdata/firstparty/container.zip.meta_lic",
-					"testdata/firstparty/container.zip.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/firstparty/container.zip.meta_lic",
 					"testdata/firstparty/lib/liba.so.meta_lic",
-					"testdata/firstparty/lib/liba.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/firstparty/container.zip.meta_lic",
 					"testdata/firstparty/lib/libb.so.meta_lic",
-					"testdata/firstparty/lib/libb.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/firstparty/container.zip.meta_lic",
 					"testdata/firstparty/lib/libc.a.meta_lic",
-					"testdata/firstparty/lib/libc.a.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/firstparty/lib/liba.so.meta_lic",
 					"testdata/firstparty/lib/liba.so.meta_lic",
-					"testdata/firstparty/lib/liba.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/firstparty/lib/libb.so.meta_lic",
 					"testdata/firstparty/lib/libb.so.meta_lic",
-					"testdata/firstparty/lib/libb.so.meta_lic",
-					"notice"),
-				matchResolution(
-					"testdata/firstparty/lib/libc.a.meta_lic",
-					"testdata/firstparty/lib/libc.a.meta_lic",
-					"testdata/firstparty/lib/libc.a.meta_lic",
-					"notice"),
-				matchResolution(
-					"testdata/firstparty/lib/libd.so.meta_lic",
-					"testdata/firstparty/lib/libd.so.meta_lic",
-					"testdata/firstparty/lib/libd.so.meta_lic",
 					"notice"),
 			},
 		},
@@ -1603,32 +1351,13 @@
 			expectedOut: []getMatcher{
 				matchTarget("testdata/firstparty/application.meta_lic"),
 				matchTarget("testdata/firstparty/lib/liba.so.meta_lic"),
-				matchTarget("testdata/firstparty/bin/bin3.meta_lic"),
-				matchTarget("testdata/firstparty/lib/libb.so.meta_lic"),
 				matchResolution(
 					"testdata/firstparty/application.meta_lic",
 					"testdata/firstparty/application.meta_lic",
-					"testdata/firstparty/application.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/firstparty/application.meta_lic",
 					"testdata/firstparty/lib/liba.so.meta_lic",
-					"testdata/firstparty/lib/liba.so.meta_lic",
-					"notice"),
-				matchResolution(
-					"testdata/firstparty/bin/bin3.meta_lic",
-					"testdata/firstparty/bin/bin3.meta_lic",
-					"testdata/firstparty/bin/bin3.meta_lic",
-					"notice"),
-				matchResolution(
-					"testdata/firstparty/lib/liba.so.meta_lic",
-					"testdata/firstparty/lib/liba.so.meta_lic",
-					"testdata/firstparty/lib/liba.so.meta_lic",
-					"notice"),
-				matchResolution(
-					"testdata/firstparty/lib/libb.so.meta_lic",
-					"testdata/firstparty/lib/libb.so.meta_lic",
-					"testdata/firstparty/lib/libb.so.meta_lic",
 					"notice"),
 			},
 		},
@@ -1643,27 +1372,14 @@
 				matchResolution(
 					"testdata/firstparty/bin/bin1.meta_lic",
 					"testdata/firstparty/bin/bin1.meta_lic",
-					"testdata/firstparty/bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/firstparty/bin/bin1.meta_lic",
 					"testdata/firstparty/lib/liba.so.meta_lic",
-					"testdata/firstparty/lib/liba.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/firstparty/bin/bin1.meta_lic",
 					"testdata/firstparty/lib/libc.a.meta_lic",
-					"testdata/firstparty/lib/libc.a.meta_lic",
-					"notice"),
-				matchResolution(
-					"testdata/firstparty/lib/liba.so.meta_lic",
-					"testdata/firstparty/lib/liba.so.meta_lic",
-					"testdata/firstparty/lib/liba.so.meta_lic",
-					"notice"),
-				matchResolution(
-					"testdata/firstparty/lib/libc.a.meta_lic",
-					"testdata/firstparty/lib/libc.a.meta_lic",
-					"testdata/firstparty/lib/libc.a.meta_lic",
 					"notice"),
 			},
 		},
@@ -1676,7 +1392,6 @@
 				matchResolution(
 					"testdata/firstparty/lib/libd.so.meta_lic",
 					"testdata/firstparty/lib/libd.so.meta_lic",
-					"testdata/firstparty/lib/libd.so.meta_lic",
 					"notice"),
 			},
 		},
@@ -1691,76 +1406,53 @@
 				matchTarget("testdata/notice/bin/bin2.meta_lic"),
 				matchTarget("testdata/notice/highest.apex.meta_lic"),
 				matchTarget("testdata/notice/lib/libb.so.meta_lic"),
-				matchTarget("testdata/notice/lib/libd.so.meta_lic"),
 				matchResolution(
 					"testdata/notice/bin/bin1.meta_lic",
 					"testdata/notice/bin/bin1.meta_lic",
-					"testdata/notice/bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/notice/bin/bin1.meta_lic",
 					"testdata/notice/lib/liba.so.meta_lic",
-					"testdata/notice/lib/liba.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/notice/bin/bin1.meta_lic",
 					"testdata/notice/lib/libc.a.meta_lic",
-					"testdata/notice/lib/libc.a.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/notice/bin/bin2.meta_lic",
 					"testdata/notice/bin/bin2.meta_lic",
-					"testdata/notice/bin/bin2.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/notice/highest.apex.meta_lic",
 					"testdata/notice/bin/bin1.meta_lic",
-					"testdata/notice/bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/notice/highest.apex.meta_lic",
 					"testdata/notice/bin/bin2.meta_lic",
-					"testdata/notice/bin/bin2.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/notice/highest.apex.meta_lic",
 					"testdata/notice/highest.apex.meta_lic",
-					"testdata/notice/highest.apex.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/notice/highest.apex.meta_lic",
 					"testdata/notice/lib/liba.so.meta_lic",
-					"testdata/notice/lib/liba.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/notice/highest.apex.meta_lic",
 					"testdata/notice/lib/libb.so.meta_lic",
-					"testdata/notice/lib/libb.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/notice/highest.apex.meta_lic",
 					"testdata/notice/lib/libc.a.meta_lic",
-					"testdata/notice/lib/libc.a.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/notice/lib/liba.so.meta_lic",
 					"testdata/notice/lib/liba.so.meta_lic",
-					"testdata/notice/lib/liba.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/notice/lib/libb.so.meta_lic",
 					"testdata/notice/lib/libb.so.meta_lic",
-					"testdata/notice/lib/libb.so.meta_lic",
-					"notice"),
-				matchResolution(
-					"testdata/notice/lib/libc.a.meta_lic",
-					"testdata/notice/lib/libc.a.meta_lic",
-					"testdata/notice/lib/libc.a.meta_lic",
-					"notice"),
-				matchResolution(
-					"testdata/notice/lib/libd.so.meta_lic",
-					"testdata/notice/lib/libd.so.meta_lic",
-					"testdata/notice/lib/libd.so.meta_lic",
 					"notice"),
 			},
 		},
@@ -1776,76 +1468,53 @@
 				matchTarget("bin/bin2.meta_lic"),
 				matchTarget("highest.apex.meta_lic"),
 				matchTarget("lib/libb.so.meta_lic"),
-				matchTarget("lib/libd.so.meta_lic"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"bin/bin1.meta_lic",
-					"bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
 					"notice"),
 				matchResolution(
 					"bin/bin2.meta_lic",
 					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"bin/bin1.meta_lic",
-					"bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"highest.apex.meta_lic",
-					"highest.apex.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
 					"notice"),
 				matchResolution(
 					"lib/liba.so.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"lib/libb.so.meta_lic",
 					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
-					"notice"),
-				matchResolution(
-					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
-					"notice"),
-				matchResolution(
-					"lib/libd.so.meta_lic",
-					"lib/libd.so.meta_lic",
-					"lib/libd.so.meta_lic",
 					"notice"),
 			},
 		},
@@ -1854,7 +1523,7 @@
 			name:      "apex_trimmed_notice",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"notice"},
+				conditions:  []compliance.LicenseCondition{compliance.NoticeCondition},
 				stripPrefix: "testdata/notice/",
 			},
 			expectedOut: []getMatcher{
@@ -1867,62 +1536,50 @@
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"bin/bin1.meta_lic",
-					"bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
 					"notice"),
 				matchResolution(
 					"bin/bin2.meta_lic",
 					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"bin/bin1.meta_lic",
-					"bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"highest.apex.meta_lic",
-					"highest.apex.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
 					"notice"),
 				matchResolution(
 					"lib/liba.so.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"lib/libb.so.meta_lic",
 					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
 					"notice"),
 			},
 		},
@@ -1931,7 +1588,7 @@
 			name:      "apex_trimmed_share",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"reciprocal", "restricted"},
+				conditions:  compliance.ImpliesShared.AsList(),
 				stripPrefix: "testdata/notice/",
 			},
 			expectedOut: []getMatcher{},
@@ -1941,7 +1598,7 @@
 			name:      "apex_trimmed_private",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"proprietary"},
+				conditions:  compliance.ImpliesPrivate.AsList(),
 				stripPrefix: "testdata/notice/",
 			},
 			expectedOut: []getMatcher{},
@@ -1951,7 +1608,7 @@
 			name:      "apex_trimmed_share_private",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"reciprocal", "restricted", "proprietary"},
+				conditions:  compliance.ImpliesShared.Union(compliance.ImpliesPrivate).AsList(),
 				stripPrefix: "testdata/notice/",
 			},
 			expectedOut: []getMatcher{},
@@ -1968,76 +1625,53 @@
 				matchTarget("bin/bin2.meta_lic", "notice"),
 				matchTarget("highest.apex.meta_lic", "notice"),
 				matchTarget("lib/libb.so.meta_lic", "notice"),
-				matchTarget("lib/libd.so.meta_lic", "notice"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"bin/bin1.meta_lic",
-					"bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
 					"notice"),
 				matchResolution(
 					"bin/bin2.meta_lic",
 					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"bin/bin1.meta_lic",
-					"bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"highest.apex.meta_lic",
-					"highest.apex.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
 					"notice"),
 				matchResolution(
 					"lib/liba.so.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"lib/libb.so.meta_lic",
 					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
-					"notice"),
-				matchResolution(
-					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
-					"notice"),
-				matchResolution(
-					"lib/libd.so.meta_lic",
-					"lib/libd.so.meta_lic",
-					"lib/libd.so.meta_lic",
 					"notice"),
 			},
 		},
@@ -2052,76 +1686,53 @@
 				matchTarget("testdata/notice/bin/bin2.meta_lic"),
 				matchTarget("testdata/notice/container.zip.meta_lic"),
 				matchTarget("testdata/notice/lib/libb.so.meta_lic"),
-				matchTarget("testdata/notice/lib/libd.so.meta_lic"),
 				matchResolution(
 					"testdata/notice/bin/bin1.meta_lic",
 					"testdata/notice/bin/bin1.meta_lic",
-					"testdata/notice/bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/notice/bin/bin1.meta_lic",
 					"testdata/notice/lib/liba.so.meta_lic",
-					"testdata/notice/lib/liba.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/notice/bin/bin1.meta_lic",
 					"testdata/notice/lib/libc.a.meta_lic",
-					"testdata/notice/lib/libc.a.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/notice/bin/bin2.meta_lic",
 					"testdata/notice/bin/bin2.meta_lic",
-					"testdata/notice/bin/bin2.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/notice/container.zip.meta_lic",
 					"testdata/notice/bin/bin1.meta_lic",
-					"testdata/notice/bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/notice/container.zip.meta_lic",
 					"testdata/notice/bin/bin2.meta_lic",
-					"testdata/notice/bin/bin2.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/notice/container.zip.meta_lic",
 					"testdata/notice/container.zip.meta_lic",
-					"testdata/notice/container.zip.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/notice/container.zip.meta_lic",
 					"testdata/notice/lib/liba.so.meta_lic",
-					"testdata/notice/lib/liba.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/notice/container.zip.meta_lic",
 					"testdata/notice/lib/libb.so.meta_lic",
-					"testdata/notice/lib/libb.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/notice/container.zip.meta_lic",
 					"testdata/notice/lib/libc.a.meta_lic",
-					"testdata/notice/lib/libc.a.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/notice/lib/liba.so.meta_lic",
 					"testdata/notice/lib/liba.so.meta_lic",
-					"testdata/notice/lib/liba.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/notice/lib/libb.so.meta_lic",
 					"testdata/notice/lib/libb.so.meta_lic",
-					"testdata/notice/lib/libb.so.meta_lic",
-					"notice"),
-				matchResolution(
-					"testdata/notice/lib/libc.a.meta_lic",
-					"testdata/notice/lib/libc.a.meta_lic",
-					"testdata/notice/lib/libc.a.meta_lic",
-					"notice"),
-				matchResolution(
-					"testdata/notice/lib/libd.so.meta_lic",
-					"testdata/notice/lib/libd.so.meta_lic",
-					"testdata/notice/lib/libd.so.meta_lic",
 					"notice"),
 			},
 		},
@@ -2132,32 +1743,13 @@
 			expectedOut: []getMatcher{
 				matchTarget("testdata/notice/application.meta_lic"),
 				matchTarget("testdata/notice/lib/liba.so.meta_lic"),
-				matchTarget("testdata/notice/bin/bin3.meta_lic"),
-				matchTarget("testdata/notice/lib/libb.so.meta_lic"),
 				matchResolution(
 					"testdata/notice/application.meta_lic",
 					"testdata/notice/application.meta_lic",
-					"testdata/notice/application.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/notice/application.meta_lic",
 					"testdata/notice/lib/liba.so.meta_lic",
-					"testdata/notice/lib/liba.so.meta_lic",
-					"notice"),
-				matchResolution(
-					"testdata/notice/bin/bin3.meta_lic",
-					"testdata/notice/bin/bin3.meta_lic",
-					"testdata/notice/bin/bin3.meta_lic",
-					"notice"),
-				matchResolution(
-					"testdata/notice/lib/liba.so.meta_lic",
-					"testdata/notice/lib/liba.so.meta_lic",
-					"testdata/notice/lib/liba.so.meta_lic",
-					"notice"),
-				matchResolution(
-					"testdata/notice/lib/libb.so.meta_lic",
-					"testdata/notice/lib/libb.so.meta_lic",
-					"testdata/notice/lib/libb.so.meta_lic",
 					"notice"),
 			},
 		},
@@ -2172,27 +1764,14 @@
 				matchResolution(
 					"testdata/notice/bin/bin1.meta_lic",
 					"testdata/notice/bin/bin1.meta_lic",
-					"testdata/notice/bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/notice/bin/bin1.meta_lic",
 					"testdata/notice/lib/liba.so.meta_lic",
-					"testdata/notice/lib/liba.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/notice/bin/bin1.meta_lic",
 					"testdata/notice/lib/libc.a.meta_lic",
-					"testdata/notice/lib/libc.a.meta_lic",
-					"notice"),
-				matchResolution(
-					"testdata/notice/lib/liba.so.meta_lic",
-					"testdata/notice/lib/liba.so.meta_lic",
-					"testdata/notice/lib/liba.so.meta_lic",
-					"notice"),
-				matchResolution(
-					"testdata/notice/lib/libc.a.meta_lic",
-					"testdata/notice/lib/libc.a.meta_lic",
-					"testdata/notice/lib/libc.a.meta_lic",
 					"notice"),
 			},
 		},
@@ -2205,7 +1784,6 @@
 				matchResolution(
 					"testdata/notice/lib/libd.so.meta_lic",
 					"testdata/notice/lib/libd.so.meta_lic",
-					"testdata/notice/lib/libd.so.meta_lic",
 					"notice"),
 			},
 		},
@@ -2220,76 +1798,53 @@
 				matchTarget("testdata/reciprocal/bin/bin2.meta_lic"),
 				matchTarget("testdata/reciprocal/highest.apex.meta_lic"),
 				matchTarget("testdata/reciprocal/lib/libb.so.meta_lic"),
-				matchTarget("testdata/reciprocal/lib/libd.so.meta_lic"),
 				matchResolution(
 					"testdata/reciprocal/bin/bin1.meta_lic",
 					"testdata/reciprocal/bin/bin1.meta_lic",
-					"testdata/reciprocal/bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/reciprocal/bin/bin1.meta_lic",
 					"testdata/reciprocal/lib/liba.so.meta_lic",
-					"testdata/reciprocal/lib/liba.so.meta_lic",
 					"reciprocal"),
 				matchResolution(
 					"testdata/reciprocal/bin/bin1.meta_lic",
 					"testdata/reciprocal/lib/libc.a.meta_lic",
-					"testdata/reciprocal/lib/libc.a.meta_lic",
 					"reciprocal"),
 				matchResolution(
 					"testdata/reciprocal/bin/bin2.meta_lic",
 					"testdata/reciprocal/bin/bin2.meta_lic",
-					"testdata/reciprocal/bin/bin2.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/reciprocal/highest.apex.meta_lic",
 					"testdata/reciprocal/bin/bin1.meta_lic",
-					"testdata/reciprocal/bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/reciprocal/highest.apex.meta_lic",
 					"testdata/reciprocal/bin/bin2.meta_lic",
-					"testdata/reciprocal/bin/bin2.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/reciprocal/highest.apex.meta_lic",
 					"testdata/reciprocal/highest.apex.meta_lic",
-					"testdata/reciprocal/highest.apex.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/reciprocal/highest.apex.meta_lic",
 					"testdata/reciprocal/lib/liba.so.meta_lic",
-					"testdata/reciprocal/lib/liba.so.meta_lic",
 					"reciprocal"),
 				matchResolution(
 					"testdata/reciprocal/highest.apex.meta_lic",
 					"testdata/reciprocal/lib/libb.so.meta_lic",
-					"testdata/reciprocal/lib/libb.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/reciprocal/highest.apex.meta_lic",
 					"testdata/reciprocal/lib/libc.a.meta_lic",
-					"testdata/reciprocal/lib/libc.a.meta_lic",
 					"reciprocal"),
 				matchResolution(
 					"testdata/reciprocal/lib/liba.so.meta_lic",
 					"testdata/reciprocal/lib/liba.so.meta_lic",
-					"testdata/reciprocal/lib/liba.so.meta_lic",
 					"reciprocal"),
 				matchResolution(
 					"testdata/reciprocal/lib/libb.so.meta_lic",
 					"testdata/reciprocal/lib/libb.so.meta_lic",
-					"testdata/reciprocal/lib/libb.so.meta_lic",
-					"notice"),
-				matchResolution(
-					"testdata/reciprocal/lib/libc.a.meta_lic",
-					"testdata/reciprocal/lib/libc.a.meta_lic",
-					"testdata/reciprocal/lib/libc.a.meta_lic",
-					"reciprocal"),
-				matchResolution(
-					"testdata/reciprocal/lib/libd.so.meta_lic",
-					"testdata/reciprocal/lib/libd.so.meta_lic",
-					"testdata/reciprocal/lib/libd.so.meta_lic",
 					"notice"),
 			},
 		},
@@ -2305,76 +1860,53 @@
 				matchTarget("bin/bin2.meta_lic"),
 				matchTarget("highest.apex.meta_lic"),
 				matchTarget("lib/libb.so.meta_lic"),
-				matchTarget("lib/libd.so.meta_lic"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"bin/bin1.meta_lic",
-					"bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"reciprocal"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
 					"reciprocal"),
 				matchResolution(
 					"bin/bin2.meta_lic",
 					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"bin/bin1.meta_lic",
-					"bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"highest.apex.meta_lic",
-					"highest.apex.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"reciprocal"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
 					"reciprocal"),
 				matchResolution(
 					"lib/liba.so.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"reciprocal"),
 				matchResolution(
 					"lib/libb.so.meta_lic",
 					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
-					"notice"),
-				matchResolution(
-					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
-					"reciprocal"),
-				matchResolution(
-					"lib/libd.so.meta_lic",
-					"lib/libd.so.meta_lic",
-					"lib/libd.so.meta_lic",
 					"notice"),
 			},
 		},
@@ -2383,7 +1915,7 @@
 			name:      "apex_trimmed_notice",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"notice"},
+				conditions:  []compliance.LicenseCondition{compliance.NoticeCondition},
 				stripPrefix: "testdata/reciprocal/",
 			},
 			expectedOut: []getMatcher{
@@ -2394,37 +1926,30 @@
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"bin/bin1.meta_lic",
-					"bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"bin/bin2.meta_lic",
 					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"bin/bin1.meta_lic",
-					"bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"highest.apex.meta_lic",
-					"highest.apex.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"lib/libb.so.meta_lic",
 					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
 					"notice"),
 			},
 		},
@@ -2433,7 +1958,7 @@
 			name:      "apex_trimmed_share",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"reciprocal", "restricted"},
+				conditions:  compliance.ImpliesShared.AsList(),
 				stripPrefix: "testdata/reciprocal/",
 			},
 			expectedOut: []getMatcher{
@@ -2444,27 +1969,22 @@
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"reciprocal"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
 					"reciprocal"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"reciprocal"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
 					"reciprocal"),
 				matchResolution(
 					"lib/liba.so.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"reciprocal"),
 			},
 		},
@@ -2473,7 +1993,7 @@
 			name:      "apex_trimmed_private",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"proprietary"},
+				conditions:  compliance.ImpliesPrivate.AsList(),
 				stripPrefix: "testdata/reciprocal/",
 			},
 			expectedOut: []getMatcher{},
@@ -2483,7 +2003,7 @@
 			name:      "apex_trimmed_share_private",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"reciprocal", "restricted", "proprietary"},
+				conditions:  compliance.ImpliesShared.Union(compliance.ImpliesPrivate).AsList(),
 				stripPrefix: "testdata/reciprocal/",
 			},
 			expectedOut: []getMatcher{
@@ -2494,27 +2014,22 @@
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"reciprocal"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
 					"reciprocal"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"reciprocal"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
 					"reciprocal"),
 				matchResolution(
 					"lib/liba.so.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"reciprocal"),
 			},
 		},
@@ -2530,76 +2045,53 @@
 				matchTarget("bin/bin2.meta_lic", "notice"),
 				matchTarget("highest.apex.meta_lic", "notice"),
 				matchTarget("lib/libb.so.meta_lic", "notice"),
-				matchTarget("lib/libd.so.meta_lic", "notice"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"bin/bin1.meta_lic",
-					"bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"reciprocal"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
 					"reciprocal"),
 				matchResolution(
 					"bin/bin2.meta_lic",
 					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"bin/bin1.meta_lic",
-					"bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"highest.apex.meta_lic",
-					"highest.apex.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"reciprocal"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
 					"reciprocal"),
 				matchResolution(
 					"lib/liba.so.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"reciprocal"),
 				matchResolution(
 					"lib/libb.so.meta_lic",
 					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
-					"notice"),
-				matchResolution(
-					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
-					"reciprocal"),
-				matchResolution(
-					"lib/libd.so.meta_lic",
-					"lib/libd.so.meta_lic",
-					"lib/libd.so.meta_lic",
 					"notice"),
 			},
 		},
@@ -2614,76 +2106,53 @@
 				matchTarget("testdata/reciprocal/bin/bin2.meta_lic"),
 				matchTarget("testdata/reciprocal/container.zip.meta_lic"),
 				matchTarget("testdata/reciprocal/lib/libb.so.meta_lic"),
-				matchTarget("testdata/reciprocal/lib/libd.so.meta_lic"),
 				matchResolution(
 					"testdata/reciprocal/bin/bin1.meta_lic",
 					"testdata/reciprocal/bin/bin1.meta_lic",
-					"testdata/reciprocal/bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/reciprocal/bin/bin1.meta_lic",
 					"testdata/reciprocal/lib/liba.so.meta_lic",
-					"testdata/reciprocal/lib/liba.so.meta_lic",
 					"reciprocal"),
 				matchResolution(
 					"testdata/reciprocal/bin/bin1.meta_lic",
 					"testdata/reciprocal/lib/libc.a.meta_lic",
-					"testdata/reciprocal/lib/libc.a.meta_lic",
 					"reciprocal"),
 				matchResolution(
 					"testdata/reciprocal/bin/bin2.meta_lic",
 					"testdata/reciprocal/bin/bin2.meta_lic",
-					"testdata/reciprocal/bin/bin2.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/reciprocal/container.zip.meta_lic",
 					"testdata/reciprocal/bin/bin1.meta_lic",
-					"testdata/reciprocal/bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/reciprocal/container.zip.meta_lic",
 					"testdata/reciprocal/bin/bin2.meta_lic",
-					"testdata/reciprocal/bin/bin2.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/reciprocal/container.zip.meta_lic",
 					"testdata/reciprocal/container.zip.meta_lic",
-					"testdata/reciprocal/container.zip.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/reciprocal/container.zip.meta_lic",
 					"testdata/reciprocal/lib/liba.so.meta_lic",
-					"testdata/reciprocal/lib/liba.so.meta_lic",
 					"reciprocal"),
 				matchResolution(
 					"testdata/reciprocal/container.zip.meta_lic",
 					"testdata/reciprocal/lib/libb.so.meta_lic",
-					"testdata/reciprocal/lib/libb.so.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/reciprocal/container.zip.meta_lic",
 					"testdata/reciprocal/lib/libc.a.meta_lic",
-					"testdata/reciprocal/lib/libc.a.meta_lic",
 					"reciprocal"),
 				matchResolution(
 					"testdata/reciprocal/lib/liba.so.meta_lic",
 					"testdata/reciprocal/lib/liba.so.meta_lic",
-					"testdata/reciprocal/lib/liba.so.meta_lic",
 					"reciprocal"),
 				matchResolution(
 					"testdata/reciprocal/lib/libb.so.meta_lic",
 					"testdata/reciprocal/lib/libb.so.meta_lic",
-					"testdata/reciprocal/lib/libb.so.meta_lic",
-					"notice"),
-				matchResolution(
-					"testdata/reciprocal/lib/libc.a.meta_lic",
-					"testdata/reciprocal/lib/libc.a.meta_lic",
-					"testdata/reciprocal/lib/libc.a.meta_lic",
-					"reciprocal"),
-				matchResolution(
-					"testdata/reciprocal/lib/libd.so.meta_lic",
-					"testdata/reciprocal/lib/libd.so.meta_lic",
-					"testdata/reciprocal/lib/libd.so.meta_lic",
 					"notice"),
 			},
 		},
@@ -2694,33 +2163,14 @@
 			expectedOut: []getMatcher{
 				matchTarget("testdata/reciprocal/application.meta_lic"),
 				matchTarget("testdata/reciprocal/lib/liba.so.meta_lic"),
-				matchTarget("testdata/reciprocal/bin/bin3.meta_lic"),
-				matchTarget("testdata/reciprocal/lib/libb.so.meta_lic"),
 				matchResolution(
 					"testdata/reciprocal/application.meta_lic",
 					"testdata/reciprocal/application.meta_lic",
-					"testdata/reciprocal/application.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/reciprocal/application.meta_lic",
 					"testdata/reciprocal/lib/liba.so.meta_lic",
-					"testdata/reciprocal/lib/liba.so.meta_lic",
 					"reciprocal"),
-				matchResolution(
-					"testdata/reciprocal/bin/bin3.meta_lic",
-					"testdata/reciprocal/bin/bin3.meta_lic",
-					"testdata/reciprocal/bin/bin3.meta_lic",
-					"notice"),
-				matchResolution(
-					"testdata/reciprocal/lib/liba.so.meta_lic",
-					"testdata/reciprocal/lib/liba.so.meta_lic",
-					"testdata/reciprocal/lib/liba.so.meta_lic",
-					"reciprocal"),
-				matchResolution(
-					"testdata/reciprocal/lib/libb.so.meta_lic",
-					"testdata/reciprocal/lib/libb.so.meta_lic",
-					"testdata/reciprocal/lib/libb.so.meta_lic",
-					"notice"),
 			},
 		},
 		{
@@ -2734,27 +2184,14 @@
 				matchResolution(
 					"testdata/reciprocal/bin/bin1.meta_lic",
 					"testdata/reciprocal/bin/bin1.meta_lic",
-					"testdata/reciprocal/bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/reciprocal/bin/bin1.meta_lic",
 					"testdata/reciprocal/lib/liba.so.meta_lic",
-					"testdata/reciprocal/lib/liba.so.meta_lic",
 					"reciprocal"),
 				matchResolution(
 					"testdata/reciprocal/bin/bin1.meta_lic",
 					"testdata/reciprocal/lib/libc.a.meta_lic",
-					"testdata/reciprocal/lib/libc.a.meta_lic",
-					"reciprocal"),
-				matchResolution(
-					"testdata/reciprocal/lib/liba.so.meta_lic",
-					"testdata/reciprocal/lib/liba.so.meta_lic",
-					"testdata/reciprocal/lib/liba.so.meta_lic",
-					"reciprocal"),
-				matchResolution(
-					"testdata/reciprocal/lib/libc.a.meta_lic",
-					"testdata/reciprocal/lib/libc.a.meta_lic",
-					"testdata/reciprocal/lib/libc.a.meta_lic",
 					"reciprocal"),
 			},
 		},
@@ -2767,7 +2204,6 @@
 				matchResolution(
 					"testdata/reciprocal/lib/libd.so.meta_lic",
 					"testdata/reciprocal/lib/libd.so.meta_lic",
-					"testdata/reciprocal/lib/libd.so.meta_lic",
 					"notice"),
 			},
 		},
@@ -2781,143 +2217,67 @@
 				matchTarget("testdata/restricted/lib/libc.a.meta_lic"),
 				matchTarget("testdata/restricted/bin/bin2.meta_lic"),
 				matchTarget("testdata/restricted/lib/libb.so.meta_lic"),
-				matchTarget("testdata/restricted/lib/libd.so.meta_lic"),
 				matchTarget("testdata/restricted/highest.apex.meta_lic"),
 				matchResolution(
 					"testdata/restricted/bin/bin1.meta_lic",
 					"testdata/restricted/bin/bin1.meta_lic",
-					"testdata/restricted/bin/bin1.meta_lic",
+					"restricted_allows_dynamic_linking",
 					"notice"),
 				matchResolution(
 					"testdata/restricted/bin/bin1.meta_lic",
-					"testdata/restricted/bin/bin1.meta_lic",
 					"testdata/restricted/lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/bin/bin1.meta_lic",
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"restricted"),
+					"restricted_allows_dynamic_linking"),
 				matchResolution(
 					"testdata/restricted/bin/bin1.meta_lic",
 					"testdata/restricted/lib/libc.a.meta_lic",
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/bin/bin1.meta_lic",
-					"testdata/restricted/lib/libc.a.meta_lic",
-					"testdata/restricted/lib/libc.a.meta_lic",
-					"reciprocal"),
+					"reciprocal",
+					"restricted_allows_dynamic_linking"),
 				matchResolution(
 					"testdata/restricted/bin/bin2.meta_lic",
 					"testdata/restricted/bin/bin2.meta_lic",
-					"testdata/restricted/bin/bin2.meta_lic",
+					"restricted",
 					"notice"),
 				matchResolution(
 					"testdata/restricted/bin/bin2.meta_lic",
-					"testdata/restricted/bin/bin2.meta_lic",
-					"testdata/restricted/lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/bin/bin2.meta_lic",
-					"testdata/restricted/lib/libb.so.meta_lic",
-					"testdata/restricted/lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/bin/bin2.meta_lic",
-					"testdata/restricted/lib/libd.so.meta_lic",
 					"testdata/restricted/lib/libb.so.meta_lic",
 					"restricted"),
 				matchResolution(
 					"testdata/restricted/highest.apex.meta_lic",
 					"testdata/restricted/bin/bin1.meta_lic",
-					"testdata/restricted/bin/bin1.meta_lic",
-					"notice"),
-				matchResolution(
-					"testdata/restricted/highest.apex.meta_lic",
-					"testdata/restricted/bin/bin1.meta_lic",
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/highest.apex.meta_lic",
-					"testdata/restricted/bin/bin2.meta_lic",
-					"testdata/restricted/bin/bin2.meta_lic",
+					"restricted_allows_dynamic_linking",
 					"notice"),
 				matchResolution(
 					"testdata/restricted/highest.apex.meta_lic",
 					"testdata/restricted/bin/bin2.meta_lic",
-					"testdata/restricted/lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/highest.apex.meta_lic",
-					"testdata/restricted/highest.apex.meta_lic",
-					"testdata/restricted/highest.apex.meta_lic",
+					"restricted",
 					"notice"),
 				matchResolution(
 					"testdata/restricted/highest.apex.meta_lic",
 					"testdata/restricted/highest.apex.meta_lic",
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/highest.apex.meta_lic",
-					"testdata/restricted/highest.apex.meta_lic",
-					"testdata/restricted/lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/highest.apex.meta_lic",
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/highest.apex.meta_lic",
-					"testdata/restricted/lib/libb.so.meta_lic",
-					"testdata/restricted/lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/highest.apex.meta_lic",
-					"testdata/restricted/lib/libc.a.meta_lic",
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/highest.apex.meta_lic",
-					"testdata/restricted/lib/libc.a.meta_lic",
-					"testdata/restricted/lib/libc.a.meta_lic",
-					"reciprocal"),
-				matchResolution(
-					"testdata/restricted/highest.apex.meta_lic",
-					"testdata/restricted/lib/libd.so.meta_lic",
-					"testdata/restricted/lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/lib/libb.so.meta_lic",
-					"testdata/restricted/lib/libb.so.meta_lic",
-					"testdata/restricted/lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/lib/libc.a.meta_lic",
-					"testdata/restricted/lib/libc.a.meta_lic",
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/lib/libc.a.meta_lic",
-					"testdata/restricted/lib/libc.a.meta_lic",
-					"testdata/restricted/lib/libc.a.meta_lic",
-					"reciprocal"),
-				matchResolution(
-					"testdata/restricted/lib/libd.so.meta_lic",
-					"testdata/restricted/lib/libd.so.meta_lic",
-					"testdata/restricted/lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/lib/libd.so.meta_lic",
-					"testdata/restricted/lib/libd.so.meta_lic",
-					"testdata/restricted/lib/libd.so.meta_lic",
+					"restricted",
+					"restricted_allows_dynamic_linking",
 					"notice"),
+				matchResolution(
+					"testdata/restricted/highest.apex.meta_lic",
+					"testdata/restricted/lib/liba.so.meta_lic",
+					"restricted_allows_dynamic_linking"),
+				matchResolution(
+					"testdata/restricted/highest.apex.meta_lic",
+					"testdata/restricted/lib/libb.so.meta_lic",
+					"restricted"),
+				matchResolution(
+					"testdata/restricted/highest.apex.meta_lic",
+					"testdata/restricted/lib/libc.a.meta_lic",
+					"reciprocal",
+					"restricted_allows_dynamic_linking"),
+				matchResolution(
+					"testdata/restricted/lib/liba.so.meta_lic",
+					"testdata/restricted/lib/liba.so.meta_lic",
+					"restricted_allows_dynamic_linking"),
+				matchResolution(
+					"testdata/restricted/lib/libb.so.meta_lic",
+					"testdata/restricted/lib/libb.so.meta_lic",
+					"restricted"),
 			},
 		},
 		{
@@ -2931,143 +2291,67 @@
 				matchTarget("lib/libc.a.meta_lic"),
 				matchTarget("bin/bin2.meta_lic"),
 				matchTarget("lib/libb.so.meta_lic"),
-				matchTarget("lib/libd.so.meta_lic"),
 				matchTarget("highest.apex.meta_lic"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"bin/bin1.meta_lic",
-					"bin/bin1.meta_lic",
+					"restricted_allows_dynamic_linking",
 					"notice"),
 				matchResolution(
 					"bin/bin1.meta_lic",
-					"bin/bin1.meta_lic",
 					"lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"bin/bin1.meta_lic",
-					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
-					"restricted"),
+					"restricted_allows_dynamic_linking"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"lib/libc.a.meta_lic",
-					"lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"bin/bin1.meta_lic",
-					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
-					"reciprocal"),
+					"reciprocal",
+					"restricted_allows_dynamic_linking"),
 				matchResolution(
 					"bin/bin2.meta_lic",
 					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
+					"restricted",
 					"notice"),
 				matchResolution(
 					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
-					"lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"bin/bin2.meta_lic",
-					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"bin/bin2.meta_lic",
-					"lib/libd.so.meta_lic",
 					"lib/libb.so.meta_lic",
 					"restricted"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"bin/bin1.meta_lic",
-					"bin/bin1.meta_lic",
-					"notice"),
-				matchResolution(
-					"highest.apex.meta_lic",
-					"bin/bin1.meta_lic",
-					"lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"highest.apex.meta_lic",
-					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
+					"restricted_allows_dynamic_linking",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"bin/bin2.meta_lic",
-					"lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"highest.apex.meta_lic",
-					"highest.apex.meta_lic",
-					"highest.apex.meta_lic",
+					"restricted",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"highest.apex.meta_lic",
-					"lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"highest.apex.meta_lic",
-					"highest.apex.meta_lic",
-					"lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"highest.apex.meta_lic",
-					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"highest.apex.meta_lic",
-					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"highest.apex.meta_lic",
-					"lib/libc.a.meta_lic",
-					"lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"highest.apex.meta_lic",
-					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
-					"reciprocal"),
-				matchResolution(
-					"highest.apex.meta_lic",
-					"lib/libd.so.meta_lic",
-					"lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
-					"lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
-					"reciprocal"),
-				matchResolution(
-					"lib/libd.so.meta_lic",
-					"lib/libd.so.meta_lic",
-					"lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"lib/libd.so.meta_lic",
-					"lib/libd.so.meta_lic",
-					"lib/libd.so.meta_lic",
+					"restricted",
+					"restricted_allows_dynamic_linking",
 					"notice"),
+				matchResolution(
+					"highest.apex.meta_lic",
+					"lib/liba.so.meta_lic",
+					"restricted_allows_dynamic_linking"),
+				matchResolution(
+					"highest.apex.meta_lic",
+					"lib/libb.so.meta_lic",
+					"restricted"),
+				matchResolution(
+					"highest.apex.meta_lic",
+					"lib/libc.a.meta_lic",
+					"reciprocal",
+					"restricted_allows_dynamic_linking"),
+				matchResolution(
+					"lib/liba.so.meta_lic",
+					"lib/liba.so.meta_lic",
+					"restricted_allows_dynamic_linking"),
+				matchResolution(
+					"lib/libb.so.meta_lic",
+					"lib/libb.so.meta_lic",
+					"restricted"),
 			},
 		},
 		{
@@ -3075,7 +2359,7 @@
 			name:      "apex_trimmed_notice",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"notice"},
+				conditions:  []compliance.LicenseCondition{compliance.NoticeCondition},
 				stripPrefix: "testdata/restricted/",
 			},
 			expectedOut: []getMatcher{
@@ -3085,27 +2369,22 @@
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"bin/bin1.meta_lic",
-					"bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"bin/bin2.meta_lic",
 					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"bin/bin1.meta_lic",
-					"bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"highest.apex.meta_lic",
-					"highest.apex.meta_lic",
 					"notice"),
 			},
 		},
@@ -3114,7 +2393,7 @@
 			name:      "apex_trimmed_share",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"reciprocal", "restricted"},
+				conditions:  compliance.ImpliesShared.AsList(),
 				stripPrefix: "testdata/restricted/",
 			},
 			expectedOut: []getMatcher{
@@ -3127,82 +2406,57 @@
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"bin/bin1.meta_lic",
-					"lib/liba.so.meta_lic",
-					"restricted"),
+					"restricted_allows_dynamic_linking"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
-					"restricted"),
+					"restricted_allows_dynamic_linking"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"lib/libc.a.meta_lic",
-					"lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"bin/bin1.meta_lic",
-					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
-					"reciprocal"),
+					"reciprocal",
+					"restricted_allows_dynamic_linking"),
 				matchResolution(
 					"bin/bin2.meta_lic",
 					"bin/bin2.meta_lic",
-					"lib/libb.so.meta_lic",
 					"restricted"),
 				matchResolution(
 					"bin/bin2.meta_lic",
 					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"highest.apex.meta_lic",
-					"bin/bin1.meta_lic",
-					"lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"highest.apex.meta_lic",
-					"bin/bin2.meta_lic",
-					"lib/libb.so.meta_lic",
 					"restricted"),
 				matchResolution(
 					"highest.apex.meta_lic",
+					"bin/bin1.meta_lic",
+					"restricted_allows_dynamic_linking"),
+				matchResolution(
 					"highest.apex.meta_lic",
-					"lib/liba.so.meta_lic",
+					"bin/bin2.meta_lic",
 					"restricted"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"highest.apex.meta_lic",
-					"lib/libb.so.meta_lic",
-					"restricted"),
+					"restricted",
+					"restricted_allows_dynamic_linking"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
-					"restricted"),
+					"restricted_allows_dynamic_linking"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
 					"restricted"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/libc.a.meta_lic",
-					"lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"highest.apex.meta_lic",
-					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
-					"reciprocal"),
+					"reciprocal",
+					"restricted_allows_dynamic_linking"),
 				matchResolution(
 					"lib/liba.so.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
-					"restricted"),
+					"restricted_allows_dynamic_linking"),
 				matchResolution(
 					"lib/libb.so.meta_lic",
 					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
 					"restricted"),
 			},
 		},
@@ -3211,7 +2465,7 @@
 			name:      "apex_trimmed_private",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"proprietary"},
+				conditions:  compliance.ImpliesPrivate.AsList(),
 				stripPrefix: "testdata/restricted/",
 			},
 			expectedOut: []getMatcher{},
@@ -3221,7 +2475,7 @@
 			name:      "apex_trimmed_share_private",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"reciprocal", "restricted", "proprietary"},
+				conditions:  compliance.ImpliesShared.Union(compliance.ImpliesPrivate).AsList(),
 				stripPrefix: "testdata/restricted/",
 			},
 			expectedOut: []getMatcher{
@@ -3234,82 +2488,57 @@
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"bin/bin1.meta_lic",
-					"lib/liba.so.meta_lic",
-					"restricted"),
+					"restricted_allows_dynamic_linking"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
-					"restricted"),
+					"restricted_allows_dynamic_linking"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"lib/libc.a.meta_lic",
-					"lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"bin/bin1.meta_lic",
-					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
-					"reciprocal"),
+					"reciprocal",
+					"restricted_allows_dynamic_linking"),
 				matchResolution(
 					"bin/bin2.meta_lic",
 					"bin/bin2.meta_lic",
-					"lib/libb.so.meta_lic",
 					"restricted"),
 				matchResolution(
 					"bin/bin2.meta_lic",
 					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"highest.apex.meta_lic",
-					"bin/bin1.meta_lic",
-					"lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"highest.apex.meta_lic",
-					"bin/bin2.meta_lic",
-					"lib/libb.so.meta_lic",
 					"restricted"),
 				matchResolution(
 					"highest.apex.meta_lic",
+					"bin/bin1.meta_lic",
+					"restricted_allows_dynamic_linking"),
+				matchResolution(
 					"highest.apex.meta_lic",
-					"lib/liba.so.meta_lic",
+					"bin/bin2.meta_lic",
 					"restricted"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"highest.apex.meta_lic",
-					"lib/libb.so.meta_lic",
-					"restricted"),
+					"restricted",
+					"restricted_allows_dynamic_linking"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
-					"restricted"),
+					"restricted_allows_dynamic_linking"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
 					"restricted"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/libc.a.meta_lic",
-					"lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"highest.apex.meta_lic",
-					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
-					"reciprocal"),
+					"reciprocal",
+					"restricted_allows_dynamic_linking"),
 				matchResolution(
 					"lib/liba.so.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
-					"restricted"),
+					"restricted_allows_dynamic_linking"),
 				matchResolution(
 					"lib/libb.so.meta_lic",
 					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
 					"restricted"),
 			},
 		},
@@ -3320,147 +2549,71 @@
 			ctx:       context{stripPrefix: "testdata/restricted/", labelConditions: true},
 			expectedOut: []getMatcher{
 				matchTarget("bin/bin1.meta_lic", "notice"),
-				matchTarget("lib/liba.so.meta_lic", "restricted"),
+				matchTarget("lib/liba.so.meta_lic", "restricted_allows_dynamic_linking"),
 				matchTarget("lib/libc.a.meta_lic", "reciprocal"),
 				matchTarget("bin/bin2.meta_lic", "notice"),
 				matchTarget("lib/libb.so.meta_lic", "restricted"),
-				matchTarget("lib/libd.so.meta_lic", "notice"),
 				matchTarget("highest.apex.meta_lic", "notice"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"bin/bin1.meta_lic",
-					"bin/bin1.meta_lic",
+					"restricted_allows_dynamic_linking",
 					"notice"),
 				matchResolution(
 					"bin/bin1.meta_lic",
-					"bin/bin1.meta_lic",
 					"lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"bin/bin1.meta_lic",
-					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
-					"restricted"),
+					"restricted_allows_dynamic_linking"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"lib/libc.a.meta_lic",
-					"lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"bin/bin1.meta_lic",
-					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
-					"reciprocal"),
+					"reciprocal",
+					"restricted_allows_dynamic_linking"),
 				matchResolution(
 					"bin/bin2.meta_lic",
 					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
+					"restricted",
 					"notice"),
 				matchResolution(
 					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
-					"lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"bin/bin2.meta_lic",
-					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"bin/bin2.meta_lic",
-					"lib/libd.so.meta_lic",
 					"lib/libb.so.meta_lic",
 					"restricted"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"bin/bin1.meta_lic",
-					"bin/bin1.meta_lic",
-					"notice"),
-				matchResolution(
-					"highest.apex.meta_lic",
-					"bin/bin1.meta_lic",
-					"lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"highest.apex.meta_lic",
-					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
+					"restricted_allows_dynamic_linking",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"bin/bin2.meta_lic",
-					"lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"highest.apex.meta_lic",
-					"highest.apex.meta_lic",
-					"highest.apex.meta_lic",
+					"restricted",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"highest.apex.meta_lic",
-					"lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"highest.apex.meta_lic",
-					"highest.apex.meta_lic",
-					"lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"highest.apex.meta_lic",
-					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"highest.apex.meta_lic",
-					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"highest.apex.meta_lic",
-					"lib/libc.a.meta_lic",
-					"lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"highest.apex.meta_lic",
-					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
-					"reciprocal"),
-				matchResolution(
-					"highest.apex.meta_lic",
-					"lib/libd.so.meta_lic",
-					"lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
-					"lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
-					"reciprocal"),
-				matchResolution(
-					"lib/libd.so.meta_lic",
-					"lib/libd.so.meta_lic",
-					"lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"lib/libd.so.meta_lic",
-					"lib/libd.so.meta_lic",
-					"lib/libd.so.meta_lic",
+					"restricted",
+					"restricted_allows_dynamic_linking",
 					"notice"),
+				matchResolution(
+					"highest.apex.meta_lic",
+					"lib/liba.so.meta_lic",
+					"restricted_allows_dynamic_linking"),
+				matchResolution(
+					"highest.apex.meta_lic",
+					"lib/libb.so.meta_lic",
+					"restricted"),
+				matchResolution(
+					"highest.apex.meta_lic",
+					"lib/libc.a.meta_lic",
+					"reciprocal",
+					"restricted_allows_dynamic_linking"),
+				matchResolution(
+					"lib/liba.so.meta_lic",
+					"lib/liba.so.meta_lic",
+					"restricted_allows_dynamic_linking"),
+				matchResolution(
+					"lib/libb.so.meta_lic",
+					"lib/libb.so.meta_lic",
+					"restricted"),
 			},
 		},
 		{
@@ -3473,143 +2626,67 @@
 				matchTarget("testdata/restricted/lib/libc.a.meta_lic"),
 				matchTarget("testdata/restricted/bin/bin2.meta_lic"),
 				matchTarget("testdata/restricted/lib/libb.so.meta_lic"),
-				matchTarget("testdata/restricted/lib/libd.so.meta_lic"),
 				matchTarget("testdata/restricted/container.zip.meta_lic"),
 				matchResolution(
 					"testdata/restricted/bin/bin1.meta_lic",
 					"testdata/restricted/bin/bin1.meta_lic",
-					"testdata/restricted/bin/bin1.meta_lic",
+					"restricted_allows_dynamic_linking",
 					"notice"),
 				matchResolution(
 					"testdata/restricted/bin/bin1.meta_lic",
-					"testdata/restricted/bin/bin1.meta_lic",
 					"testdata/restricted/lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/bin/bin1.meta_lic",
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"restricted"),
+					"restricted_allows_dynamic_linking"),
 				matchResolution(
 					"testdata/restricted/bin/bin1.meta_lic",
 					"testdata/restricted/lib/libc.a.meta_lic",
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/bin/bin1.meta_lic",
-					"testdata/restricted/lib/libc.a.meta_lic",
-					"testdata/restricted/lib/libc.a.meta_lic",
-					"reciprocal"),
+					"reciprocal",
+					"restricted_allows_dynamic_linking"),
 				matchResolution(
 					"testdata/restricted/bin/bin2.meta_lic",
 					"testdata/restricted/bin/bin2.meta_lic",
-					"testdata/restricted/bin/bin2.meta_lic",
+					"restricted",
 					"notice"),
 				matchResolution(
 					"testdata/restricted/bin/bin2.meta_lic",
-					"testdata/restricted/bin/bin2.meta_lic",
-					"testdata/restricted/lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/bin/bin2.meta_lic",
-					"testdata/restricted/lib/libb.so.meta_lic",
-					"testdata/restricted/lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/bin/bin2.meta_lic",
-					"testdata/restricted/lib/libd.so.meta_lic",
 					"testdata/restricted/lib/libb.so.meta_lic",
 					"restricted"),
 				matchResolution(
 					"testdata/restricted/container.zip.meta_lic",
 					"testdata/restricted/bin/bin1.meta_lic",
-					"testdata/restricted/bin/bin1.meta_lic",
-					"notice"),
-				matchResolution(
-					"testdata/restricted/container.zip.meta_lic",
-					"testdata/restricted/bin/bin1.meta_lic",
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/container.zip.meta_lic",
-					"testdata/restricted/bin/bin2.meta_lic",
-					"testdata/restricted/bin/bin2.meta_lic",
+					"restricted_allows_dynamic_linking",
 					"notice"),
 				matchResolution(
 					"testdata/restricted/container.zip.meta_lic",
 					"testdata/restricted/bin/bin2.meta_lic",
-					"testdata/restricted/lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/container.zip.meta_lic",
-					"testdata/restricted/container.zip.meta_lic",
-					"testdata/restricted/container.zip.meta_lic",
+					"restricted",
 					"notice"),
 				matchResolution(
 					"testdata/restricted/container.zip.meta_lic",
 					"testdata/restricted/container.zip.meta_lic",
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/container.zip.meta_lic",
-					"testdata/restricted/container.zip.meta_lic",
-					"testdata/restricted/lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/container.zip.meta_lic",
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/container.zip.meta_lic",
-					"testdata/restricted/lib/libb.so.meta_lic",
-					"testdata/restricted/lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/container.zip.meta_lic",
-					"testdata/restricted/lib/libc.a.meta_lic",
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/container.zip.meta_lic",
-					"testdata/restricted/lib/libc.a.meta_lic",
-					"testdata/restricted/lib/libc.a.meta_lic",
-					"reciprocal"),
-				matchResolution(
-					"testdata/restricted/container.zip.meta_lic",
-					"testdata/restricted/lib/libd.so.meta_lic",
-					"testdata/restricted/lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/lib/libb.so.meta_lic",
-					"testdata/restricted/lib/libb.so.meta_lic",
-					"testdata/restricted/lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/lib/libc.a.meta_lic",
-					"testdata/restricted/lib/libc.a.meta_lic",
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/lib/libc.a.meta_lic",
-					"testdata/restricted/lib/libc.a.meta_lic",
-					"testdata/restricted/lib/libc.a.meta_lic",
-					"reciprocal"),
-				matchResolution(
-					"testdata/restricted/lib/libd.so.meta_lic",
-					"testdata/restricted/lib/libd.so.meta_lic",
-					"testdata/restricted/lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/lib/libd.so.meta_lic",
-					"testdata/restricted/lib/libd.so.meta_lic",
-					"testdata/restricted/lib/libd.so.meta_lic",
+					"restricted",
+					"restricted_allows_dynamic_linking",
 					"notice"),
+				matchResolution(
+					"testdata/restricted/container.zip.meta_lic",
+					"testdata/restricted/lib/liba.so.meta_lic",
+					"restricted_allows_dynamic_linking"),
+				matchResolution(
+					"testdata/restricted/container.zip.meta_lic",
+					"testdata/restricted/lib/libb.so.meta_lic",
+					"restricted"),
+				matchResolution(
+					"testdata/restricted/container.zip.meta_lic",
+					"testdata/restricted/lib/libc.a.meta_lic",
+					"reciprocal",
+					"restricted_allows_dynamic_linking"),
+				matchResolution(
+					"testdata/restricted/lib/liba.so.meta_lic",
+					"testdata/restricted/lib/liba.so.meta_lic",
+					"restricted_allows_dynamic_linking"),
+				matchResolution(
+					"testdata/restricted/lib/libb.so.meta_lic",
+					"testdata/restricted/lib/libb.so.meta_lic",
+					"restricted"),
 			},
 		},
 		{
@@ -3619,57 +2696,16 @@
 			expectedOut: []getMatcher{
 				matchTarget("testdata/restricted/application.meta_lic"),
 				matchTarget("testdata/restricted/lib/liba.so.meta_lic"),
-				matchTarget("testdata/restricted/lib/libb.so.meta_lic"),
-				matchTarget("testdata/restricted/bin/bin3.meta_lic"),
 				matchResolution(
 					"testdata/restricted/application.meta_lic",
 					"testdata/restricted/application.meta_lic",
-					"testdata/restricted/application.meta_lic",
+					"restricted",
+					"restricted_allows_dynamic_linking",
 					"notice"),
 				matchResolution(
 					"testdata/restricted/application.meta_lic",
-					"testdata/restricted/application.meta_lic",
 					"testdata/restricted/lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/application.meta_lic",
-					"testdata/restricted/application.meta_lic",
-					"testdata/restricted/lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/application.meta_lic",
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/application.meta_lic",
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"testdata/restricted/lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/application.meta_lic",
-					"testdata/restricted/lib/libb.so.meta_lic",
-					"testdata/restricted/lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/bin/bin3.meta_lic",
-					"testdata/restricted/bin/bin3.meta_lic",
-					"testdata/restricted/bin/bin3.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"testdata/restricted/lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/lib/libb.so.meta_lic",
-					"testdata/restricted/lib/libb.so.meta_lic",
-					"testdata/restricted/lib/libb.so.meta_lic",
+					"restricted_allows_dynamic_linking",
 					"restricted"),
 			},
 		},
@@ -3684,42 +2720,16 @@
 				matchResolution(
 					"testdata/restricted/bin/bin1.meta_lic",
 					"testdata/restricted/bin/bin1.meta_lic",
-					"testdata/restricted/bin/bin1.meta_lic",
+					"restricted_allows_dynamic_linking",
 					"notice"),
 				matchResolution(
 					"testdata/restricted/bin/bin1.meta_lic",
-					"testdata/restricted/bin/bin1.meta_lic",
 					"testdata/restricted/lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/bin/bin1.meta_lic",
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"restricted"),
+					"restricted_allows_dynamic_linking"),
 				matchResolution(
 					"testdata/restricted/bin/bin1.meta_lic",
 					"testdata/restricted/lib/libc.a.meta_lic",
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/bin/bin1.meta_lic",
-					"testdata/restricted/lib/libc.a.meta_lic",
-					"testdata/restricted/lib/libc.a.meta_lic",
-					"reciprocal"),
-				matchResolution(
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/lib/libc.a.meta_lic",
-					"testdata/restricted/lib/libc.a.meta_lic",
-					"testdata/restricted/lib/liba.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/restricted/lib/libc.a.meta_lic",
-					"testdata/restricted/lib/libc.a.meta_lic",
-					"testdata/restricted/lib/libc.a.meta_lic",
+					"restricted_allows_dynamic_linking",
 					"reciprocal"),
 			},
 		},
@@ -3732,7 +2742,6 @@
 				matchResolution(
 					"testdata/restricted/lib/libd.so.meta_lic",
 					"testdata/restricted/lib/libd.so.meta_lic",
-					"testdata/restricted/lib/libd.so.meta_lic",
 					"notice"),
 			},
 		},
@@ -3746,96 +2755,61 @@
 				matchTarget("testdata/proprietary/lib/libc.a.meta_lic"),
 				matchTarget("testdata/proprietary/bin/bin2.meta_lic"),
 				matchTarget("testdata/proprietary/lib/libb.so.meta_lic"),
-				matchTarget("testdata/proprietary/lib/libd.so.meta_lic"),
 				matchTarget("testdata/proprietary/highest.apex.meta_lic"),
 				matchResolution(
 					"testdata/proprietary/bin/bin1.meta_lic",
 					"testdata/proprietary/bin/bin1.meta_lic",
-					"testdata/proprietary/bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/proprietary/bin/bin1.meta_lic",
 					"testdata/proprietary/lib/liba.so.meta_lic",
-					"testdata/proprietary/lib/liba.so.meta_lic",
 					"by_exception_only",
 					"proprietary"),
 				matchResolution(
 					"testdata/proprietary/bin/bin1.meta_lic",
 					"testdata/proprietary/lib/libc.a.meta_lic",
-					"testdata/proprietary/lib/libc.a.meta_lic",
 					"by_exception_only",
 					"proprietary"),
 				matchResolution(
 					"testdata/proprietary/bin/bin2.meta_lic",
 					"testdata/proprietary/bin/bin2.meta_lic",
-					"testdata/proprietary/bin/bin2.meta_lic",
+					"restricted",
 					"by_exception_only",
 					"proprietary"),
 				matchResolution(
 					"testdata/proprietary/bin/bin2.meta_lic",
-					"testdata/proprietary/bin/bin2.meta_lic",
-					"testdata/proprietary/lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/proprietary/bin/bin2.meta_lic",
-					"testdata/proprietary/lib/libb.so.meta_lic",
-					"testdata/proprietary/lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/proprietary/bin/bin2.meta_lic",
-					"testdata/proprietary/lib/libd.so.meta_lic",
 					"testdata/proprietary/lib/libb.so.meta_lic",
 					"restricted"),
 				matchResolution(
 					"testdata/proprietary/highest.apex.meta_lic",
 					"testdata/proprietary/bin/bin1.meta_lic",
-					"testdata/proprietary/bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/proprietary/highest.apex.meta_lic",
 					"testdata/proprietary/bin/bin2.meta_lic",
-					"testdata/proprietary/bin/bin2.meta_lic",
+					"restricted",
 					"by_exception_only",
 					"proprietary"),
 				matchResolution(
 					"testdata/proprietary/highest.apex.meta_lic",
-					"testdata/proprietary/bin/bin2.meta_lic",
-					"testdata/proprietary/lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
 					"testdata/proprietary/highest.apex.meta_lic",
-					"testdata/proprietary/highest.apex.meta_lic",
-					"testdata/proprietary/highest.apex.meta_lic",
+					"restricted",
 					"notice"),
 				matchResolution(
 					"testdata/proprietary/highest.apex.meta_lic",
-					"testdata/proprietary/highest.apex.meta_lic",
-					"testdata/proprietary/lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/proprietary/highest.apex.meta_lic",
-					"testdata/proprietary/lib/liba.so.meta_lic",
 					"testdata/proprietary/lib/liba.so.meta_lic",
 					"by_exception_only",
 					"proprietary"),
 				matchResolution(
 					"testdata/proprietary/highest.apex.meta_lic",
 					"testdata/proprietary/lib/libb.so.meta_lic",
-					"testdata/proprietary/lib/libb.so.meta_lic",
 					"restricted"),
 				matchResolution(
 					"testdata/proprietary/highest.apex.meta_lic",
 					"testdata/proprietary/lib/libc.a.meta_lic",
-					"testdata/proprietary/lib/libc.a.meta_lic",
 					"by_exception_only",
 					"proprietary"),
 				matchResolution(
-					"testdata/proprietary/highest.apex.meta_lic",
-					"testdata/proprietary/lib/libd.so.meta_lic",
-					"testdata/proprietary/lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/proprietary/lib/liba.so.meta_lic",
 					"testdata/proprietary/lib/liba.so.meta_lic",
 					"testdata/proprietary/lib/liba.so.meta_lic",
 					"by_exception_only",
@@ -3843,24 +2817,7 @@
 				matchResolution(
 					"testdata/proprietary/lib/libb.so.meta_lic",
 					"testdata/proprietary/lib/libb.so.meta_lic",
-					"testdata/proprietary/lib/libb.so.meta_lic",
 					"restricted"),
-				matchResolution(
-					"testdata/proprietary/lib/libc.a.meta_lic",
-					"testdata/proprietary/lib/libc.a.meta_lic",
-					"testdata/proprietary/lib/libc.a.meta_lic",
-					"by_exception_only",
-					"proprietary"),
-				matchResolution(
-					"testdata/proprietary/lib/libd.so.meta_lic",
-					"testdata/proprietary/lib/libd.so.meta_lic",
-					"testdata/proprietary/lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/proprietary/lib/libd.so.meta_lic",
-					"testdata/proprietary/lib/libd.so.meta_lic",
-					"testdata/proprietary/lib/libd.so.meta_lic",
-					"notice"),
 			},
 		},
 		{
@@ -3874,96 +2831,61 @@
 				matchTarget("lib/libc.a.meta_lic"),
 				matchTarget("bin/bin2.meta_lic"),
 				matchTarget("lib/libb.so.meta_lic"),
-				matchTarget("lib/libd.so.meta_lic"),
 				matchTarget("highest.apex.meta_lic"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"bin/bin1.meta_lic",
-					"bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"by_exception_only",
 					"proprietary"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
 					"by_exception_only",
 					"proprietary"),
 				matchResolution(
 					"bin/bin2.meta_lic",
 					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
 					"by_exception_only",
+					"restricted",
 					"proprietary"),
 				matchResolution(
 					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
-					"lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"bin/bin2.meta_lic",
-					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"bin/bin2.meta_lic",
-					"lib/libd.so.meta_lic",
 					"lib/libb.so.meta_lic",
 					"restricted"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"bin/bin1.meta_lic",
-					"bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
+					"restricted",
 					"by_exception_only",
 					"proprietary"),
 				matchResolution(
 					"highest.apex.meta_lic",
-					"bin/bin2.meta_lic",
-					"lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
 					"highest.apex.meta_lic",
-					"highest.apex.meta_lic",
-					"highest.apex.meta_lic",
+					"restricted",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
-					"highest.apex.meta_lic",
-					"lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"highest.apex.meta_lic",
-					"lib/liba.so.meta_lic",
 					"lib/liba.so.meta_lic",
 					"by_exception_only",
 					"proprietary"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
 					"restricted"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
 					"by_exception_only",
 					"proprietary"),
 				matchResolution(
-					"highest.apex.meta_lic",
-					"lib/libd.so.meta_lic",
-					"lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"lib/liba.so.meta_lic",
 					"lib/liba.so.meta_lic",
 					"lib/liba.so.meta_lic",
 					"by_exception_only",
@@ -3971,24 +2893,7 @@
 				matchResolution(
 					"lib/libb.so.meta_lic",
 					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
 					"restricted"),
-				matchResolution(
-					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
-					"by_exception_only",
-					"proprietary"),
-				matchResolution(
-					"lib/libd.so.meta_lic",
-					"lib/libd.so.meta_lic",
-					"lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"lib/libd.so.meta_lic",
-					"lib/libd.so.meta_lic",
-					"lib/libd.so.meta_lic",
-					"notice"),
 			},
 		},
 		{
@@ -3996,7 +2901,7 @@
 			name:      "apex_trimmed_notice",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"notice"},
+				conditions:  []compliance.LicenseCondition{compliance.NoticeCondition},
 				stripPrefix: "testdata/proprietary/",
 			},
 			expectedOut: []getMatcher{
@@ -4005,17 +2910,14 @@
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"bin/bin1.meta_lic",
-					"bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"bin/bin1.meta_lic",
-					"bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"highest.apex.meta_lic",
-					"highest.apex.meta_lic",
 					"notice"),
 			},
 		},
@@ -4024,7 +2926,7 @@
 			name:      "apex_trimmed_share",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"reciprocal", "restricted"},
+				conditions:  compliance.ImpliesShared.AsList(),
 				stripPrefix: "testdata/proprietary/",
 			},
 			expectedOut: []getMatcher{
@@ -4034,32 +2936,26 @@
 				matchResolution(
 					"bin/bin2.meta_lic",
 					"bin/bin2.meta_lic",
-					"lib/libb.so.meta_lic",
 					"restricted"),
 				matchResolution(
 					"bin/bin2.meta_lic",
 					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
 					"restricted"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"bin/bin2.meta_lic",
-					"lib/libb.so.meta_lic",
 					"restricted"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"highest.apex.meta_lic",
-					"lib/libb.so.meta_lic",
 					"restricted"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
 					"restricted"),
 				matchResolution(
 					"lib/libb.so.meta_lic",
 					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
 					"restricted"),
 			},
 		},
@@ -4068,7 +2964,7 @@
 			name:      "apex_trimmed_private",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"proprietary"},
+				conditions:  compliance.ImpliesPrivate.AsList(),
 				stripPrefix: "testdata/proprietary/",
 			},
 			expectedOut: []getMatcher{
@@ -4080,37 +2976,30 @@
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"proprietary"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
 					"proprietary"),
 				matchResolution(
 					"bin/bin2.meta_lic",
 					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
 					"proprietary"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
 					"proprietary"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"proprietary"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
 					"proprietary"),
 				matchResolution(
 					"lib/liba.so.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"proprietary"),
 			},
 		},
@@ -4119,7 +3008,7 @@
 			name:      "apex_trimmed_share_private",
 			roots:     []string{"highest.apex.meta_lic"},
 			ctx: context{
-				conditions:  []string{"reciprocal", "restricted", "proprietary"},
+				conditions:  compliance.ImpliesShared.Union(compliance.ImpliesPrivate).AsList(),
 				stripPrefix: "testdata/proprietary/",
 			},
 			expectedOut: []getMatcher{
@@ -4132,67 +3021,48 @@
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"proprietary"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
 					"proprietary"),
 				matchResolution(
 					"bin/bin2.meta_lic",
 					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
+					"restricted",
 					"proprietary"),
 				matchResolution(
 					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
-					"lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"bin/bin2.meta_lic",
-					"lib/libb.so.meta_lic",
 					"lib/libb.so.meta_lic",
 					"restricted"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
+					"restricted",
 					"proprietary"),
 				matchResolution(
 					"highest.apex.meta_lic",
-					"bin/bin2.meta_lic",
-					"lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
 					"highest.apex.meta_lic",
-					"highest.apex.meta_lic",
-					"lib/libb.so.meta_lic",
 					"restricted"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"proprietary"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
 					"restricted"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
 					"proprietary"),
 				matchResolution(
 					"lib/liba.so.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"proprietary"),
 				matchResolution(
 					"lib/libb.so.meta_lic",
 					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
 					"restricted"),
 			},
 		},
@@ -4207,96 +3077,61 @@
 				matchTarget("lib/libc.a.meta_lic", "by_exception_only", "proprietary"),
 				matchTarget("bin/bin2.meta_lic", "by_exception_only", "proprietary"),
 				matchTarget("lib/libb.so.meta_lic", "restricted"),
-				matchTarget("lib/libd.so.meta_lic", "notice"),
 				matchTarget("highest.apex.meta_lic", "notice"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"bin/bin1.meta_lic",
-					"bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"lib/liba.so.meta_lic",
-					"lib/liba.so.meta_lic",
 					"by_exception_only",
 					"proprietary"),
 				matchResolution(
 					"bin/bin1.meta_lic",
 					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
 					"by_exception_only",
 					"proprietary"),
 				matchResolution(
 					"bin/bin2.meta_lic",
 					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
+					"restricted",
 					"by_exception_only",
 					"proprietary"),
 				matchResolution(
 					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
-					"lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"bin/bin2.meta_lic",
-					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"bin/bin2.meta_lic",
-					"lib/libd.so.meta_lic",
 					"lib/libb.so.meta_lic",
 					"restricted"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"bin/bin1.meta_lic",
-					"bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"bin/bin2.meta_lic",
-					"bin/bin2.meta_lic",
+					"restricted",
 					"by_exception_only",
 					"proprietary"),
 				matchResolution(
 					"highest.apex.meta_lic",
-					"bin/bin2.meta_lic",
-					"lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
 					"highest.apex.meta_lic",
-					"highest.apex.meta_lic",
-					"highest.apex.meta_lic",
+					"restricted",
 					"notice"),
 				matchResolution(
 					"highest.apex.meta_lic",
-					"highest.apex.meta_lic",
-					"lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"highest.apex.meta_lic",
-					"lib/liba.so.meta_lic",
 					"lib/liba.so.meta_lic",
 					"by_exception_only",
 					"proprietary"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
 					"restricted"),
 				matchResolution(
 					"highest.apex.meta_lic",
 					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
 					"by_exception_only",
 					"proprietary"),
 				matchResolution(
-					"highest.apex.meta_lic",
-					"lib/libd.so.meta_lic",
-					"lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"lib/liba.so.meta_lic",
 					"lib/liba.so.meta_lic",
 					"lib/liba.so.meta_lic",
 					"by_exception_only",
@@ -4304,24 +3139,7 @@
 				matchResolution(
 					"lib/libb.so.meta_lic",
 					"lib/libb.so.meta_lic",
-					"lib/libb.so.meta_lic",
 					"restricted"),
-				matchResolution(
-					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
-					"lib/libc.a.meta_lic",
-					"by_exception_only",
-					"proprietary"),
-				matchResolution(
-					"lib/libd.so.meta_lic",
-					"lib/libd.so.meta_lic",
-					"lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"lib/libd.so.meta_lic",
-					"lib/libd.so.meta_lic",
-					"lib/libd.so.meta_lic",
-					"notice"),
 			},
 		},
 		{
@@ -4334,96 +3152,61 @@
 				matchTarget("testdata/proprietary/lib/libc.a.meta_lic"),
 				matchTarget("testdata/proprietary/bin/bin2.meta_lic"),
 				matchTarget("testdata/proprietary/lib/libb.so.meta_lic"),
-				matchTarget("testdata/proprietary/lib/libd.so.meta_lic"),
 				matchTarget("testdata/proprietary/container.zip.meta_lic"),
 				matchResolution(
 					"testdata/proprietary/bin/bin1.meta_lic",
 					"testdata/proprietary/bin/bin1.meta_lic",
-					"testdata/proprietary/bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/proprietary/bin/bin1.meta_lic",
 					"testdata/proprietary/lib/liba.so.meta_lic",
-					"testdata/proprietary/lib/liba.so.meta_lic",
 					"by_exception_only",
 					"proprietary"),
 				matchResolution(
 					"testdata/proprietary/bin/bin1.meta_lic",
 					"testdata/proprietary/lib/libc.a.meta_lic",
-					"testdata/proprietary/lib/libc.a.meta_lic",
 					"by_exception_only",
 					"proprietary"),
 				matchResolution(
 					"testdata/proprietary/bin/bin2.meta_lic",
 					"testdata/proprietary/bin/bin2.meta_lic",
-					"testdata/proprietary/bin/bin2.meta_lic",
+					"restricted",
 					"by_exception_only",
 					"proprietary"),
 				matchResolution(
 					"testdata/proprietary/bin/bin2.meta_lic",
-					"testdata/proprietary/bin/bin2.meta_lic",
-					"testdata/proprietary/lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/proprietary/bin/bin2.meta_lic",
-					"testdata/proprietary/lib/libb.so.meta_lic",
-					"testdata/proprietary/lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/proprietary/bin/bin2.meta_lic",
-					"testdata/proprietary/lib/libd.so.meta_lic",
 					"testdata/proprietary/lib/libb.so.meta_lic",
 					"restricted"),
 				matchResolution(
 					"testdata/proprietary/container.zip.meta_lic",
 					"testdata/proprietary/bin/bin1.meta_lic",
-					"testdata/proprietary/bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/proprietary/container.zip.meta_lic",
 					"testdata/proprietary/bin/bin2.meta_lic",
-					"testdata/proprietary/bin/bin2.meta_lic",
+					"restricted",
 					"by_exception_only",
 					"proprietary"),
 				matchResolution(
 					"testdata/proprietary/container.zip.meta_lic",
-					"testdata/proprietary/bin/bin2.meta_lic",
-					"testdata/proprietary/lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
 					"testdata/proprietary/container.zip.meta_lic",
-					"testdata/proprietary/container.zip.meta_lic",
-					"testdata/proprietary/container.zip.meta_lic",
+					"restricted",
 					"notice"),
 				matchResolution(
 					"testdata/proprietary/container.zip.meta_lic",
-					"testdata/proprietary/container.zip.meta_lic",
-					"testdata/proprietary/lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/proprietary/container.zip.meta_lic",
-					"testdata/proprietary/lib/liba.so.meta_lic",
 					"testdata/proprietary/lib/liba.so.meta_lic",
 					"by_exception_only",
 					"proprietary"),
 				matchResolution(
 					"testdata/proprietary/container.zip.meta_lic",
 					"testdata/proprietary/lib/libb.so.meta_lic",
-					"testdata/proprietary/lib/libb.so.meta_lic",
 					"restricted"),
 				matchResolution(
 					"testdata/proprietary/container.zip.meta_lic",
 					"testdata/proprietary/lib/libc.a.meta_lic",
-					"testdata/proprietary/lib/libc.a.meta_lic",
 					"by_exception_only",
 					"proprietary"),
 				matchResolution(
-					"testdata/proprietary/container.zip.meta_lic",
-					"testdata/proprietary/lib/libd.so.meta_lic",
-					"testdata/proprietary/lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/proprietary/lib/liba.so.meta_lic",
 					"testdata/proprietary/lib/liba.so.meta_lic",
 					"testdata/proprietary/lib/liba.so.meta_lic",
 					"by_exception_only",
@@ -4431,24 +3214,7 @@
 				matchResolution(
 					"testdata/proprietary/lib/libb.so.meta_lic",
 					"testdata/proprietary/lib/libb.so.meta_lic",
-					"testdata/proprietary/lib/libb.so.meta_lic",
 					"restricted"),
-				matchResolution(
-					"testdata/proprietary/lib/libc.a.meta_lic",
-					"testdata/proprietary/lib/libc.a.meta_lic",
-					"testdata/proprietary/lib/libc.a.meta_lic",
-					"by_exception_only",
-					"proprietary"),
-				matchResolution(
-					"testdata/proprietary/lib/libd.so.meta_lic",
-					"testdata/proprietary/lib/libd.so.meta_lic",
-					"testdata/proprietary/lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/proprietary/lib/libd.so.meta_lic",
-					"testdata/proprietary/lib/libd.so.meta_lic",
-					"testdata/proprietary/lib/libd.so.meta_lic",
-					"notice"),
 			},
 		},
 		{
@@ -4458,55 +3224,17 @@
 			expectedOut: []getMatcher{
 				matchTarget("testdata/proprietary/application.meta_lic"),
 				matchTarget("testdata/proprietary/lib/liba.so.meta_lic"),
-				matchTarget("testdata/proprietary/lib/libb.so.meta_lic"),
-				matchTarget("testdata/proprietary/bin/bin3.meta_lic"),
 				matchResolution(
 					"testdata/proprietary/application.meta_lic",
 					"testdata/proprietary/application.meta_lic",
-					"testdata/proprietary/application.meta_lic",
-					"notice"),
-				matchResolution(
-					"testdata/proprietary/application.meta_lic",
-					"testdata/proprietary/application.meta_lic",
-					"testdata/proprietary/lib/libb.so.meta_lic",
+					"notice",
 					"restricted"),
 				matchResolution(
 					"testdata/proprietary/application.meta_lic",
 					"testdata/proprietary/lib/liba.so.meta_lic",
-					"testdata/proprietary/lib/liba.so.meta_lic",
+					"restricted",
 					"by_exception_only",
 					"proprietary"),
-				matchResolution(
-					"testdata/proprietary/application.meta_lic",
-					"testdata/proprietary/lib/liba.so.meta_lic",
-					"testdata/proprietary/lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/proprietary/application.meta_lic",
-					"testdata/proprietary/lib/libb.so.meta_lic",
-					"testdata/proprietary/lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/proprietary/bin/bin3.meta_lic",
-					"testdata/proprietary/bin/bin3.meta_lic",
-					"testdata/proprietary/bin/bin3.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/proprietary/lib/liba.so.meta_lic",
-					"testdata/proprietary/lib/liba.so.meta_lic",
-					"testdata/proprietary/lib/liba.so.meta_lic",
-					"by_exception_only",
-					"proprietary"),
-				matchResolution(
-					"testdata/proprietary/lib/liba.so.meta_lic",
-					"testdata/proprietary/lib/liba.so.meta_lic",
-					"testdata/proprietary/lib/libb.so.meta_lic",
-					"restricted"),
-				matchResolution(
-					"testdata/proprietary/lib/libb.so.meta_lic",
-					"testdata/proprietary/lib/libb.so.meta_lic",
-					"testdata/proprietary/lib/libb.so.meta_lic",
-					"restricted"),
 			},
 		},
 		{
@@ -4520,30 +3248,15 @@
 				matchResolution(
 					"testdata/proprietary/bin/bin1.meta_lic",
 					"testdata/proprietary/bin/bin1.meta_lic",
-					"testdata/proprietary/bin/bin1.meta_lic",
 					"notice"),
 				matchResolution(
 					"testdata/proprietary/bin/bin1.meta_lic",
 					"testdata/proprietary/lib/liba.so.meta_lic",
-					"testdata/proprietary/lib/liba.so.meta_lic",
 					"by_exception_only",
 					"proprietary"),
 				matchResolution(
 					"testdata/proprietary/bin/bin1.meta_lic",
 					"testdata/proprietary/lib/libc.a.meta_lic",
-					"testdata/proprietary/lib/libc.a.meta_lic",
-					"by_exception_only",
-					"proprietary"),
-				matchResolution(
-					"testdata/proprietary/lib/liba.so.meta_lic",
-					"testdata/proprietary/lib/liba.so.meta_lic",
-					"testdata/proprietary/lib/liba.so.meta_lic",
-					"by_exception_only",
-					"proprietary"),
-				matchResolution(
-					"testdata/proprietary/lib/libc.a.meta_lic",
-					"testdata/proprietary/lib/libc.a.meta_lic",
-					"testdata/proprietary/lib/libc.a.meta_lic",
 					"by_exception_only",
 					"proprietary"),
 			},
@@ -4557,7 +3270,6 @@
 				matchResolution(
 					"testdata/proprietary/lib/libd.so.meta_lic",
 					"testdata/proprietary/lib/libd.so.meta_lic",
-					"testdata/proprietary/lib/libd.so.meta_lic",
 					"notice"),
 			},
 		},
@@ -4566,13 +3278,6 @@
 		t.Run(tt.condition+" "+tt.name, func(t *testing.T) {
 			ctx := &testContext{0, make(map[string]string)}
 
-			expectedOut := &bytes.Buffer{}
-			for _, eo := range tt.expectedOut {
-				m := eo(ctx)
-				expectedOut.WriteString(m.matchString(ctx))
-				expectedOut.WriteString("\n")
-			}
-
 			stdout := &bytes.Buffer{}
 			stderr := &bytes.Buffer{}
 
@@ -4581,8 +3286,7 @@
 				rootFiles = append(rootFiles, "testdata/"+tt.condition+"/"+r)
 			}
 			tt.ctx.graphViz = true
-			err := dumpResolutions(&tt.ctx, stdout, stderr, rootFiles...)
-
+			lg, err := dumpResolutions(&tt.ctx, stdout, stderr, rootFiles...)
 			if err != nil {
 				t.Fatalf("dumpresolutions: error = %v, stderr = %v", err, stderr)
 				return
@@ -4590,6 +3294,14 @@
 			if stderr.Len() > 0 {
 				t.Errorf("dumpresolutions: gotStderr = %v, want none", stderr)
 			}
+
+			expectedOut := &bytes.Buffer{}
+			for _, eo := range tt.expectedOut {
+				m := eo(ctx)
+				expectedOut.WriteString(m.matchString(ctx, lg))
+				expectedOut.WriteString("\n")
+			}
+
 			outList := strings.Split(stdout.String(), "\n")
 			outLine := 0
 			if outList[outLine] != "strict digraph {" {
diff --git a/tools/compliance/cmd/listshare.go b/tools/compliance/cmd/listshare.go
index bba2308..5c58dc4 100644
--- a/tools/compliance/cmd/listshare.go
+++ b/tools/compliance/cmd/listshare.go
@@ -22,6 +22,7 @@
 	"os"
 	"path/filepath"
 	"sort"
+	"strings"
 )
 
 func init() {
@@ -83,17 +84,17 @@
 	shareSource := compliance.ResolveSourceSharing(licenseGraph)
 
 	// Group the resolutions by project.
-	presolution := make(map[string]*compliance.LicenseConditionSet)
+	presolution := make(map[string]compliance.LicenseConditionSet)
 	for _, target := range shareSource.AttachesTo() {
 		rl := shareSource.Resolutions(target)
 		sort.Sort(rl)
 		for _, r := range rl {
 			for _, p := range r.ActsOn().Projects() {
 				if _, ok := presolution[p]; !ok {
-					presolution[p] = r.Resolves().Copy()
+					presolution[p] = r.Resolves()
 					continue
 				}
-				presolution[p].AddSet(r.Resolves())
+				presolution[p] = presolution[p].Union(r.Resolves())
 			}
 		}
 	}
@@ -107,17 +108,11 @@
 
 	// Output the sorted projects and the source-sharing license conditions that each project resolves.
 	for _, p := range projects {
-		fmt.Fprintf(stdout, "%s", p)
-
-		// Sort the conditions for repeatability/stability.
-		conditions := presolution[p].AsList()
-		sort.Sort(conditions)
-
-		// Output the sorted origin:condition pairs.
-		for _, lc := range conditions {
-			fmt.Fprintf(stdout, ",%s:%s", lc.Origin().Name(), lc.Name())
+		if presolution[p].IsEmpty() {
+			fmt.Fprintf(stdout, "%s\n", p)
+		} else {
+			fmt.Fprintf(stdout, "%s,%s\n", p, strings.Join(presolution[p].Names(), ","))
 		}
-		fmt.Fprintf(stdout, "\n")
 	}
 
 	return nil
diff --git a/tools/compliance/cmd/listshare_test.go b/tools/compliance/cmd/listshare_test.go
index b4847e3..71a0be6 100644
--- a/tools/compliance/cmd/listshare_test.go
+++ b/tools/compliance/cmd/listshare_test.go
@@ -98,12 +98,12 @@
 			expectedOut: []projectShare{
 				{
 					project:    "device/library",
-					conditions: []string{"lib/liba.so.meta_lic:reciprocal"},
+					conditions: []string{"reciprocal"},
 				},
 				{
 					project: "static/library",
 					conditions: []string{
-						"lib/libc.a.meta_lic:reciprocal",
+						"reciprocal",
 					},
 				},
 			},
@@ -115,12 +115,12 @@
 			expectedOut: []projectShare{
 				{
 					project:    "device/library",
-					conditions: []string{"lib/liba.so.meta_lic:reciprocal"},
+					conditions: []string{"reciprocal"},
 				},
 				{
 					project: "static/library",
 					conditions: []string{
-						"lib/libc.a.meta_lic:reciprocal",
+						"reciprocal",
 					},
 				},
 			},
@@ -132,7 +132,7 @@
 			expectedOut: []projectShare{
 				{
 					project:    "device/library",
-					conditions: []string{"lib/liba.so.meta_lic:reciprocal"},
+					conditions: []string{"reciprocal"},
 				},
 			},
 		},
@@ -144,13 +144,13 @@
 				{
 					project: "device/library",
 					conditions: []string{
-						"lib/liba.so.meta_lic:reciprocal",
+						"reciprocal",
 					},
 				},
 				{
 					project: "static/library",
 					conditions: []string{
-						"lib/libc.a.meta_lic:reciprocal",
+						"reciprocal",
 					},
 				},
 			},
@@ -168,34 +168,34 @@
 			expectedOut: []projectShare{
 				{
 					project:    "base/library",
-					conditions: []string{"lib/libb.so.meta_lic:restricted"},
+					conditions: []string{"restricted"},
 				},
 				{
 					project:    "device/library",
-					conditions: []string{"lib/liba.so.meta_lic:restricted"},
+					conditions: []string{"restricted_allows_dynamic_linking"},
 				},
 				{
 					project:    "dynamic/binary",
-					conditions: []string{"lib/libb.so.meta_lic:restricted"},
+					conditions: []string{"restricted"},
 				},
 				{
 					project: "highest/apex",
 					conditions: []string{
-						"lib/liba.so.meta_lic:restricted",
-						"lib/libb.so.meta_lic:restricted",
+						"restricted",
+						"restricted_allows_dynamic_linking",
 					},
 				},
 				{
 					project: "static/binary",
 					conditions: []string{
-						"lib/liba.so.meta_lic:restricted",
+						"restricted_allows_dynamic_linking",
 					},
 				},
 				{
 					project: "static/library",
 					conditions: []string{
-						"lib/liba.so.meta_lic:restricted",
-						"lib/libc.a.meta_lic:reciprocal",
+						"reciprocal",
+						"restricted_allows_dynamic_linking",
 					},
 				},
 			},
@@ -207,34 +207,34 @@
 			expectedOut: []projectShare{
 				{
 					project:    "base/library",
-					conditions: []string{"lib/libb.so.meta_lic:restricted"},
+					conditions: []string{"restricted"},
 				},
 				{
 					project: "container/zip",
 					conditions: []string{
-						"lib/liba.so.meta_lic:restricted",
-						"lib/libb.so.meta_lic:restricted",
+						"restricted",
+						"restricted_allows_dynamic_linking",
 					},
 				},
 				{
 					project:    "device/library",
-					conditions: []string{"lib/liba.so.meta_lic:restricted"},
+					conditions: []string{"restricted_allows_dynamic_linking"},
 				},
 				{
 					project:    "dynamic/binary",
-					conditions: []string{"lib/libb.so.meta_lic:restricted"},
+					conditions: []string{"restricted"},
 				},
 				{
 					project: "static/binary",
 					conditions: []string{
-						"lib/liba.so.meta_lic:restricted",
+						"restricted_allows_dynamic_linking",
 					},
 				},
 				{
 					project: "static/library",
 					conditions: []string{
-						"lib/liba.so.meta_lic:restricted",
-						"lib/libc.a.meta_lic:reciprocal",
+						"reciprocal",
+						"restricted_allows_dynamic_linking",
 					},
 				},
 			},
@@ -247,15 +247,15 @@
 				{
 					project: "device/library",
 					conditions: []string{
-						"lib/liba.so.meta_lic:restricted",
-						"lib/libb.so.meta_lic:restricted",
+						"restricted",
+						"restricted_allows_dynamic_linking",
 					},
 				},
 				{
 					project: "distributable/application",
 					conditions: []string{
-						"lib/liba.so.meta_lic:restricted",
-						"lib/libb.so.meta_lic:restricted",
+						"restricted",
+						"restricted_allows_dynamic_linking",
 					},
 				},
 			},
@@ -268,20 +268,20 @@
 				{
 					project: "device/library",
 					conditions: []string{
-						"lib/liba.so.meta_lic:restricted",
+						"restricted_allows_dynamic_linking",
 					},
 				},
 				{
 					project: "static/binary",
 					conditions: []string{
-						"lib/liba.so.meta_lic:restricted",
+						"restricted_allows_dynamic_linking",
 					},
 				},
 				{
 					project: "static/library",
 					conditions: []string{
-						"lib/liba.so.meta_lic:restricted",
-						"lib/libc.a.meta_lic:reciprocal",
+						"reciprocal",
+						"restricted_allows_dynamic_linking",
 					},
 				},
 			},
@@ -299,15 +299,15 @@
 			expectedOut: []projectShare{
 				{
 					project:    "base/library",
-					conditions: []string{"lib/libb.so.meta_lic:restricted"},
+					conditions: []string{"restricted"},
 				},
 				{
 					project:    "dynamic/binary",
-					conditions: []string{"lib/libb.so.meta_lic:restricted"},
+					conditions: []string{"restricted"},
 				},
 				{
 					project:    "highest/apex",
-					conditions: []string{"lib/libb.so.meta_lic:restricted"},
+					conditions: []string{"restricted"},
 				},
 			},
 		},
@@ -318,15 +318,15 @@
 			expectedOut: []projectShare{
 				{
 					project:    "base/library",
-					conditions: []string{"lib/libb.so.meta_lic:restricted"},
+					conditions: []string{"restricted"},
 				},
 				{
 					project:    "container/zip",
-					conditions: []string{"lib/libb.so.meta_lic:restricted"},
+					conditions: []string{"restricted"},
 				},
 				{
 					project:    "dynamic/binary",
-					conditions: []string{"lib/libb.so.meta_lic:restricted"},
+					conditions: []string{"restricted"},
 				},
 			},
 		},
@@ -337,11 +337,11 @@
 			expectedOut: []projectShare{
 				{
 					project:    "device/library",
-					conditions: []string{"lib/libb.so.meta_lic:restricted"},
+					conditions: []string{"restricted"},
 				},
 				{
 					project:    "distributable/application",
-					conditions: []string{"lib/libb.so.meta_lic:restricted"},
+					conditions: []string{"restricted"},
 				},
 			},
 		},
@@ -357,6 +357,98 @@
 			roots:       []string{"lib/libd.so.meta_lic"},
 			expectedOut: []projectShare{},
 		},
+		{
+			condition: "regressgpl1",
+			name:      "container",
+			roots:     []string{"container.zip.meta_lic"},
+			expectedOut: []projectShare{
+				{
+					project:    "bin/threelibraries",
+					conditions: []string{"restricted"},
+				},
+				{
+					project:    "container/zip",
+					conditions: []string{"restricted"},
+				},
+			},
+		},
+		{
+			condition: "regressgpl1",
+			name:      "containerplus",
+			roots:     []string{"container.zip.meta_lic", "lib/libapache.so.meta_lic", "lib/libc++.so.meta_lic"},
+			expectedOut: []projectShare{
+				{
+					project:    "bin/threelibraries",
+					conditions: []string{"restricted"},
+				},
+				{
+					project:    "container/zip",
+					conditions: []string{"restricted"},
+				},
+				{
+					project:    "lib/apache",
+					conditions: []string{"restricted"},
+				},
+				{
+					project:    "lib/c++",
+					conditions: []string{"restricted"},
+				},
+			},
+		},
+		{
+			condition: "regressgpl2",
+			name:      "container",
+			roots:     []string{"container.zip.meta_lic"},
+			expectedOut: []projectShare{
+				{
+					project:    "bin/threelibraries",
+					conditions: []string{"restricted"},
+				},
+				{
+					project:    "container/zip",
+					conditions: []string{"restricted"},
+				},
+				{
+					project:    "lib/apache",
+					conditions: []string{"restricted"},
+				},
+				{
+					project:    "lib/c++",
+					conditions: []string{"restricted"},
+				},
+				{
+					project:    "lib/gpl",
+					conditions: []string{"restricted"},
+				},
+			},
+		},
+		{
+			condition: "regressgpl2",
+			name:      "containerplus",
+			roots:     []string{"container.zip.meta_lic", "lib/libapache.so.meta_lic", "lib/libc++.so.meta_lic"},
+			expectedOut: []projectShare{
+				{
+					project:    "bin/threelibraries",
+					conditions: []string{"restricted"},
+				},
+				{
+					project:    "container/zip",
+					conditions: []string{"restricted"},
+				},
+				{
+					project:    "lib/apache",
+					conditions: []string{"restricted"},
+				},
+				{
+					project:    "lib/c++",
+					conditions: []string{"restricted"},
+				},
+				{
+					project:    "lib/gpl",
+					conditions: []string{"restricted"},
+				},
+			},
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.condition+" "+tt.name, func(t *testing.T) {
@@ -365,9 +457,6 @@
 				expectedOut.WriteString(p.project)
 				for _, lc := range p.conditions {
 					expectedOut.WriteString(",")
-					expectedOut.WriteString("testdata/")
-					expectedOut.WriteString(tt.condition)
-					expectedOut.WriteString("/")
 					expectedOut.WriteString(lc)
 				}
 				expectedOut.WriteString("\n")
diff --git a/tools/compliance/cmd/testdata/README.md b/tools/compliance/cmd/testdata/README.md
index 9872c04..07564c2 100644
--- a/tools/compliance/cmd/testdata/README.md
+++ b/tools/compliance/cmd/testdata/README.md
@@ -1,9 +1,12 @@
 ## Test data
 
-Each directory under testdata/ defines a similar build graph.
+Each non-regression directory under testdata/ defines a similar build graph.
 All have the same structure, but different versions of the graph have different
 license metadata.
 
+The regression* directories can have whatever structure is required for the
+specific test case.
+
 ### Testdata build graph structure:
 
 The structure is meant to simulate some common scenarios:
@@ -70,6 +73,7 @@
 
 #### a pure aggregation `container.zip` that merely bundles files together
 
+```dot
 strict digraph {
 	rankdir=LR;
 	bin1 [label="bin/bin1.meta_lic"];
@@ -89,6 +93,7 @@
 	container -> libb [label="static"];
 	{rank=same; container}
 }
+```
 
 #### an apex file (more like an apk file) with some binaries and libraries
 
diff --git a/tools/compliance/cmd/testdata/regressgpl1/README.md b/tools/compliance/cmd/testdata/regressgpl1/README.md
new file mode 100644
index 0000000..6ab872d
--- /dev/null
+++ b/tools/compliance/cmd/testdata/regressgpl1/README.md
@@ -0,0 +1,32 @@
+## Shipped versus non-shipped libraries with restricted license
+
+### Testdata build graph structure:
+
+A restricted licensed library sandwiched between a notice library and a notice
+binary. The source-code for the libraries only needs to be shared if shipped
+alongside the container with the binaries.
+
+```dot
+strict digraph {
+	rankdir=LR;
+	bin1 [label="bin/bin1.meta_lic\nnotice"];
+	bin2 [label="bin/bin2.meta_lic\nnotice"];
+	bin3 [label="bin/bin3.meta_lic\nnotice"];
+	container [label="container.zip.meta_lic\nnotice"];
+	libapache [label="lib/libapache.so.meta_lic\nnotice"];
+	libcxx [label="lib/libc++.so.meta_lic\nnotice"];
+	libgpl [label="lib/libgpl.so.meta_lic\nrestricted"];
+	container -> bin1[label="static"];
+	container -> bin2 [label="static"];
+	container -> bin3 [label="static"];
+	bin1 -> libcxx [label="dynamic"];
+	bin2 -> libapache [label="dynamic"];
+	bin2 -> libcxx [label="dynamic"];
+	bin3 -> libapache [label="dynamic"];
+	bin3 -> libcxx [label="dynamic"];
+	bin3 -> libgpl [label="dynamic"];
+	libapache -> libcxx [label="dynamic"];
+	libgpl -> libcxx [label="dynamic"];
+	{rank=same; container}
+}
+```
diff --git a/tools/compliance/cmd/testdata/regressgpl1/bin/bin1.meta_lic b/tools/compliance/cmd/testdata/regressgpl1/bin/bin1.meta_lic
new file mode 100644
index 0000000..4afd240
--- /dev/null
+++ b/tools/compliance/cmd/testdata/regressgpl1/bin/bin1.meta_lic
@@ -0,0 +1,14 @@
+package_name:  "Android"
+module_classes: "EXECUTABLES"
+projects:  "bin/onelibrary"
+license_kinds:  "SPDX-license-identifier-Apache-2.0"
+license_conditions:  "notice"
+license_texts:  "build/soong/licenses/LICENSE"
+is_container:  false
+built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin1"
+installed:  "out/target/product/fictional/system/bin/bin1"
+sources:  "out/target/product/fictional/system/lib/libc++.so"
+deps:  {
+  file:  "testdata/regressgpl1/lib/libc++.so.meta_lic"
+  annotations:  "dynamic"
+}
diff --git a/tools/compliance/cmd/testdata/regressgpl1/bin/bin2.meta_lic b/tools/compliance/cmd/testdata/regressgpl1/bin/bin2.meta_lic
new file mode 100644
index 0000000..4e56fa3
--- /dev/null
+++ b/tools/compliance/cmd/testdata/regressgpl1/bin/bin2.meta_lic
@@ -0,0 +1,19 @@
+package_name:  "Android"
+module_classes: "EXECUTABLES"
+projects:  "bin/twolibraries"
+license_kinds:  "SPDX-license-identifier-Apache-2.0"
+license_conditions:  "notice"
+license_texts:  "build/soong/licenses/LICENSE"
+is_container:  false
+built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin2"
+installed:  "out/target/product/fictional/system/bin/bin2"
+sources:  "out/target/product/fictional/system/lib/libc++.so"
+sources:  "out/target/product/fictional/system/lib/libapache.so"
+deps:  {
+  file:  "testdata/regressgpl1/lib/libc++.so.meta_lic"
+  annotations:  "dynamic"
+}
+deps:  {
+  file:  "testdata/regressgpl1/lib/libapache.so.meta_lic"
+  annotations:  "dynamic"
+}
diff --git a/tools/compliance/cmd/testdata/regressgpl1/bin/bin3.meta_lic b/tools/compliance/cmd/testdata/regressgpl1/bin/bin3.meta_lic
new file mode 100644
index 0000000..16290d3
--- /dev/null
+++ b/tools/compliance/cmd/testdata/regressgpl1/bin/bin3.meta_lic
@@ -0,0 +1,23 @@
+package_name:  "Compiler"
+module_classes: "EXECUTABLES"
+projects:  "bin/threelibraries"
+license_kinds:  "SPDX-license-identifier-NCSA"
+license_conditions:  "notice"
+is_container:  false
+built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin3"
+installed:  "out/target/product/fictional/system/bin/bin3"
+sources:  "out/target/product/fictional/system/lib/libc++.so"
+sources:  "out/target/product/fictional/system/lib/libapache.so"
+sources:  "out/target/product/fictional/system/lib/libgpl.so"
+deps:  {
+  file:  "testdata/regressgpl1/lib/libc++.so.meta_lic"
+  annotations:  "dynamic"
+}
+deps:  {
+  file:  "testdata/regressgpl1/lib/libapache.so.meta_lic"
+  annotations:  "dynamic"
+}
+deps:  {
+  file:  "testdata/regressgpl1/lib/libgpl.so.meta_lic"
+  annotations:  "dynamic"
+}
diff --git a/tools/compliance/cmd/testdata/regressgpl1/container.zip.meta_lic b/tools/compliance/cmd/testdata/regressgpl1/container.zip.meta_lic
new file mode 100644
index 0000000..295bcdb
--- /dev/null
+++ b/tools/compliance/cmd/testdata/regressgpl1/container.zip.meta_lic
@@ -0,0 +1,32 @@
+package_name:  "Android"
+projects:  "container/zip"
+license_kinds:  "SPDX-license-identifier-Apache-2.0"
+license_conditions:  "notice"
+license_texts:  "build/soong/licenses/LICENSE"
+is_container:  true
+built:  "out/target/product/fictional/obj/ETC/container_intermediates/container.zip"
+installed:  "out/target/product/fictional/data/container.zip"
+install_map {
+  from_path:  "out/target/product/fictional/system/lib/"
+  container_path:  ""
+}
+install_map {
+  from_path:  "out/target/product/fictional/system/bin/"
+  container_path:  ""
+}
+sources:  "out/target/product/fictional/system/bin/bin1"
+sources:  "out/target/product/fictional/system/bin/bin2"
+sources:  "out/target/product/fictional/system/bin/bin3"
+sources:  "out/target/product/fictional/system/lib/libapache.so"
+deps:  {
+  file:  "testdata/regressgpl1/bin/bin1.meta_lic"
+  annotations:  "static"
+}
+deps:  {
+  file:  "testdata/regressgpl1/bin/bin2.meta_lic"
+  annotations:  "static"
+}
+deps:  {
+  file:  "testdata/regressgpl1/bin/bin3.meta_lic"
+  annotations:  "static"
+}
diff --git a/tools/compliance/cmd/testdata/regressgpl1/lib/libapache.so.meta_lic b/tools/compliance/cmd/testdata/regressgpl1/lib/libapache.so.meta_lic
new file mode 100644
index 0000000..9184501
--- /dev/null
+++ b/tools/compliance/cmd/testdata/regressgpl1/lib/libapache.so.meta_lic
@@ -0,0 +1,14 @@
+package_name:  "Android"
+projects:  "lib/apache"
+license_kinds:  "SPDX-license-identifier-Apache-2.0"
+license_conditions:  "notice"
+license_texts:  "build/soong/licenses/LICENSE"
+is_container:  false
+built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libapache.so"
+built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libapache.a"
+installed:  "out/target/product/fictional/system/lib/libapache.so"
+sources:  "out/target/product/fictional/system/lib/libc++.so"
+deps:  {
+  file:  "testdata/regressgpl1/lib/libc++.so.meta_lic"
+  annotations:  "dynamic"
+}
diff --git a/tools/compliance/cmd/testdata/regressgpl1/lib/libc++.so.meta_lic b/tools/compliance/cmd/testdata/regressgpl1/lib/libc++.so.meta_lic
new file mode 100644
index 0000000..b789377
--- /dev/null
+++ b/tools/compliance/cmd/testdata/regressgpl1/lib/libc++.so.meta_lic
@@ -0,0 +1,8 @@
+package_name:  "Device"
+projects:  "lib/c++"
+license_kinds:  "SPDX-license-identifier-BSD"
+license_conditions:  "notice"
+is_container:  false
+built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc++.so"
+built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc++.a"
+installed:  "out/target/product/fictional/system/lib/libc++.so"
diff --git a/tools/compliance/cmd/testdata/regressgpl1/lib/libgpl.so.meta_lic b/tools/compliance/cmd/testdata/regressgpl1/lib/libgpl.so.meta_lic
new file mode 100644
index 0000000..a3afa2b
--- /dev/null
+++ b/tools/compliance/cmd/testdata/regressgpl1/lib/libgpl.so.meta_lic
@@ -0,0 +1,12 @@
+package_name:  "External"
+projects:  "lib/gpl"
+license_kinds:  "SPDX-license-identifier-GPL-2.0"
+license_conditions:  "restricted"
+is_container:  false
+built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libgpl.so"
+installed:  "out/target/product/fictional/system/lib/libgpl.so"
+sources:  "out/target/product/fictional/system/lib/libc++.so"
+deps:  {
+  file:  "testdata/regressgpl1/lib/libc++.so.meta_lic"
+  annotations:  "dynamic"
+}
diff --git a/tools/compliance/cmd/testdata/regressgpl2/README.md b/tools/compliance/cmd/testdata/regressgpl2/README.md
new file mode 100644
index 0000000..9da0f4d
--- /dev/null
+++ b/tools/compliance/cmd/testdata/regressgpl2/README.md
@@ -0,0 +1,35 @@
+## Libraries shipped inside container with restricted license
+
+### Testdata build graph structure:
+
+A restricted licensed library sandwiched between a notice library and a notice
+binary. The source-code for the libraries needs to be shared when shipped as
+part of the container with the binaries.
+
+```dot
+strict digraph {
+	rankdir=LR;
+	bin1 [label="bin/bin1.meta_lic\nnotice"];
+	bin2 [label="bin/bin2.meta_lic\nnotice"];
+	bin3 [label="bin/bin3.meta_lic\nnotice"];
+	container [label="container.zip.meta_lic\nnotice"];
+	libapache [label="lib/libapache.so.meta_lic\nnotice"];
+	libcxx [label="lib/libc++.so.meta_lic\nnotice"];
+	libgpl [label="lib/libgpl.so.meta_lic\nrestricted"];
+	container -> bin1[label="static"];
+	container -> bin2 [label="static"];
+	container -> bin3 [label="static"];
+	container -> libapache [label="static"];
+	container -> libcxx [label="static"];
+	container -> libgpl [label="static"];
+	bin1 -> libcxx [label="dynamic"];
+	bin2 -> libapache [label="dynamic"];
+	bin2 -> libcxx [label="dynamic"];
+	bin3 -> libapache [label="dynamic"];
+	bin3 -> libcxx [label="dynamic"];
+	bin3 -> libgpl [label="dynamic"];
+	libapache -> libcxx [label="dynamic"];
+	libgpl -> libcxx [label="dynamic"];
+	{rank=same; container}
+}
+```
diff --git a/tools/compliance/cmd/testdata/regressgpl2/bin/bin1.meta_lic b/tools/compliance/cmd/testdata/regressgpl2/bin/bin1.meta_lic
new file mode 100644
index 0000000..839fd9c
--- /dev/null
+++ b/tools/compliance/cmd/testdata/regressgpl2/bin/bin1.meta_lic
@@ -0,0 +1,14 @@
+package_name:  "Android"
+module_classes: "EXECUTABLES"
+projects:  "bin/onelibrary"
+license_kinds:  "SPDX-license-identifier-Apache-2.0"
+license_conditions:  "notice"
+license_texts:  "build/soong/licenses/LICENSE"
+is_container:  false
+built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin1"
+installed:  "out/target/product/fictional/system/bin/bin1"
+sources:  "out/target/product/fictional/system/lib/libc++.so"
+deps:  {
+  file:  "testdata/regressgpl2/lib/libc++.so.meta_lic"
+  annotations:  "dynamic"
+}
diff --git a/tools/compliance/cmd/testdata/regressgpl2/bin/bin2.meta_lic b/tools/compliance/cmd/testdata/regressgpl2/bin/bin2.meta_lic
new file mode 100644
index 0000000..96baaae
--- /dev/null
+++ b/tools/compliance/cmd/testdata/regressgpl2/bin/bin2.meta_lic
@@ -0,0 +1,19 @@
+package_name:  "Android"
+module_classes: "EXECUTABLES"
+projects:  "bin/twolibraries"
+license_kinds:  "SPDX-license-identifier-Apache-2.0"
+license_conditions:  "notice"
+license_texts:  "build/soong/licenses/LICENSE"
+is_container:  false
+built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin2"
+installed:  "out/target/product/fictional/system/bin/bin2"
+sources:  "out/target/product/fictional/system/lib/libc++.so"
+sources:  "out/target/product/fictional/system/lib/libapache.so"
+deps:  {
+  file:  "testdata/regressgpl2/lib/libc++.so.meta_lic"
+  annotations:  "dynamic"
+}
+deps:  {
+  file:  "testdata/regressgpl2/lib/libapache.so.meta_lic"
+  annotations:  "dynamic"
+}
diff --git a/tools/compliance/cmd/testdata/regressgpl2/bin/bin3.meta_lic b/tools/compliance/cmd/testdata/regressgpl2/bin/bin3.meta_lic
new file mode 100644
index 0000000..3cd8602
--- /dev/null
+++ b/tools/compliance/cmd/testdata/regressgpl2/bin/bin3.meta_lic
@@ -0,0 +1,23 @@
+package_name:  "Compiler"
+module_classes: "EXECUTABLES"
+projects:  "bin/threelibraries"
+license_kinds:  "SPDX-license-identifier-NCSA"
+license_conditions:  "notice"
+is_container:  false
+built:  "out/target/product/fictional/obj/EXECUTABLES/bin_intermediates/bin3"
+installed:  "out/target/product/fictional/system/bin/bin3"
+sources:  "out/target/product/fictional/system/lib/libc++.so"
+sources:  "out/target/product/fictional/system/lib/libapache.so"
+sources:  "out/target/product/fictional/system/lib/libgpl.so"
+deps:  {
+  file:  "testdata/regressgpl2/lib/libc++.so.meta_lic"
+  annotations:  "dynamic"
+}
+deps:  {
+  file:  "testdata/regressgpl2/lib/libapache.so.meta_lic"
+  annotations:  "dynamic"
+}
+deps:  {
+  file:  "testdata/regressgpl2/lib/libgpl.so.meta_lic"
+  annotations:  "dynamic"
+}
diff --git a/tools/compliance/cmd/testdata/regressgpl2/container.zip.meta_lic b/tools/compliance/cmd/testdata/regressgpl2/container.zip.meta_lic
new file mode 100644
index 0000000..71b68cd
--- /dev/null
+++ b/tools/compliance/cmd/testdata/regressgpl2/container.zip.meta_lic
@@ -0,0 +1,44 @@
+package_name:  "Android"
+projects:  "container/zip"
+license_kinds:  "SPDX-license-identifier-Apache-2.0"
+license_conditions:  "notice"
+license_texts:  "build/soong/licenses/LICENSE"
+is_container:  true
+built:  "out/target/product/fictional/obj/ETC/container_intermediates/container.zip"
+installed:  "out/target/product/fictional/data/container.zip"
+install_map {
+  from_path:  "out/target/product/fictional/system/lib/"
+  container_path:  ""
+}
+install_map {
+  from_path:  "out/target/product/fictional/system/bin/"
+  container_path:  ""
+}
+sources:  "out/target/product/fictional/system/bin/bin1"
+sources:  "out/target/product/fictional/system/bin/bin2"
+sources:  "out/target/product/fictional/system/bin/bin3"
+sources:  "out/target/product/fictional/system/lib/libapache.so"
+deps:  {
+  file:  "testdata/regressgpl2/bin/bin1.meta_lic"
+  annotations:  "static"
+}
+deps:  {
+  file:  "testdata/regressgpl2/bin/bin2.meta_lic"
+  annotations:  "static"
+}
+deps:  {
+  file:  "testdata/regressgpl2/bin/bin3.meta_lic"
+  annotations:  "static"
+}
+deps:  {
+  file:  "testdata/regressgpl2/lib/libapache.so.meta_lic"
+  annotations:  "static"
+}
+deps:  {
+  file:  "testdata/regressgpl2/lib/libc++.so.meta_lic"
+  annotations:  "static"
+}
+deps:  {
+  file:  "testdata/regressgpl2/lib/libgpl.so.meta_lic"
+  annotations:  "static"
+}
diff --git a/tools/compliance/cmd/testdata/regressgpl2/lib/libapache.so.meta_lic b/tools/compliance/cmd/testdata/regressgpl2/lib/libapache.so.meta_lic
new file mode 100644
index 0000000..ae47340
--- /dev/null
+++ b/tools/compliance/cmd/testdata/regressgpl2/lib/libapache.so.meta_lic
@@ -0,0 +1,14 @@
+package_name:  "Android"
+projects:  "lib/apache"
+license_kinds:  "SPDX-license-identifier-Apache-2.0"
+license_conditions:  "notice"
+license_texts:  "build/soong/licenses/LICENSE"
+is_container:  false
+built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libapache.so"
+built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libapache.a"
+installed:  "out/target/product/fictional/system/lib/libapache.so"
+sources:  "out/target/product/fictional/system/lib/libc++.so"
+deps:  {
+  file:  "testdata/regressgpl2/lib/libc++.so.meta_lic"
+  annotations:  "dynamic"
+}
diff --git a/tools/compliance/cmd/testdata/regressgpl2/lib/libc++.so.meta_lic b/tools/compliance/cmd/testdata/regressgpl2/lib/libc++.so.meta_lic
new file mode 100644
index 0000000..b789377
--- /dev/null
+++ b/tools/compliance/cmd/testdata/regressgpl2/lib/libc++.so.meta_lic
@@ -0,0 +1,8 @@
+package_name:  "Device"
+projects:  "lib/c++"
+license_kinds:  "SPDX-license-identifier-BSD"
+license_conditions:  "notice"
+is_container:  false
+built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc++.so"
+built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libc++.a"
+installed:  "out/target/product/fictional/system/lib/libc++.so"
diff --git a/tools/compliance/cmd/testdata/regressgpl2/lib/libgpl.so.meta_lic b/tools/compliance/cmd/testdata/regressgpl2/lib/libgpl.so.meta_lic
new file mode 100644
index 0000000..4e78697
--- /dev/null
+++ b/tools/compliance/cmd/testdata/regressgpl2/lib/libgpl.so.meta_lic
@@ -0,0 +1,12 @@
+package_name:  "External"
+projects:  "lib/gpl"
+license_kinds:  "SPDX-license-identifier-GPL-2.0"
+license_conditions:  "restricted"
+is_container:  false
+built:  "out/target/product/fictional/obj/SHARED_LIBRARIES/lib_intermediates/libgpl.so"
+installed:  "out/target/product/fictional/system/lib/libgpl.so"
+sources:  "out/target/product/fictional/system/lib/libc++.so"
+deps:  {
+  file:  "testdata/regressgpl2/lib/libc++.so.meta_lic"
+  annotations:  "dynamic"
+}
diff --git a/tools/compliance/condition.go b/tools/compliance/condition.go
index b5c8cec..26b91ca 100644
--- a/tools/compliance/condition.go
+++ b/tools/compliance/condition.go
@@ -16,150 +16,87 @@
 
 import (
 	"fmt"
-	"strings"
 )
 
-// LicenseCondition describes an individual license condition or requirement
-// originating at a specific target node. (immutable)
-//
-// e.g. A module licensed under GPL terms would originate a `restricted` condition.
-type LicenseCondition struct {
-	name   string
-	origin *TargetNode
-}
+// LicenseCondition identifies a recognized license condition by setting the
+// corresponding bit.
+type LicenseCondition uint16
 
-// Name returns the name of the condition. e.g. "restricted" or "notice"
+// LicenseConditionMask is a bitmask for the recognized license conditions.
+const LicenseConditionMask = LicenseCondition(0x3ff)
+
+const (
+	// UnencumberedCondition identifies public domain or public domain-
+	// like license that disclaims copyright.
+	UnencumberedCondition = LicenseCondition(0x0001)
+	// PermissiveCondition identifies a license without notice or other
+	// significant requirements.
+	PermissiveCondition = LicenseCondition(0x0002)
+	// NoticeCondition identifies a typical open-source license with only
+	// notice or attribution requirements.
+	NoticeCondition = LicenseCondition(0x0004)
+	// ReciprocalCondition identifies a license with requirement to share
+	// the module's source only.
+	ReciprocalCondition = LicenseCondition(0x0008)
+	// RestrictedCondition identifies a license with requirement to share
+	// all source code linked to the module's source.
+	RestrictedCondition = LicenseCondition(0x0010)
+	// RestrictedClasspathExceptionCondition identifies RestrictedCondition
+	// waived for dynamic linking from independent modules.
+	RestrictedClasspathExceptionCondition = LicenseCondition(0x0020)
+	// WeaklyRestrictedCondition identifies a RestrictedCondition waived
+	// for dynamic linking.
+	WeaklyRestrictedCondition = LicenseCondition(0x0040)
+	// ProprietaryCondition identifies a license with source privacy
+	// requirements.
+	ProprietaryCondition = LicenseCondition(0x0080)
+	// ByExceptionOnly identifies a license where policy requires product
+	// counsel review prior to use.
+	ByExceptionOnlyCondition = LicenseCondition(0x0100)
+	// NotAllowedCondition identifies a license with onerous conditions
+	// where policy prohibits use.
+	NotAllowedCondition = LicenseCondition(0x0200)
+)
+
+var (
+	// RecognizedConditionNames maps condition strings to LicenseCondition.
+	RecognizedConditionNames = map[string]LicenseCondition{
+		"unencumbered": UnencumberedCondition,
+		"permissive": PermissiveCondition,
+		"notice": NoticeCondition,
+		"reciprocal": ReciprocalCondition,
+		"restricted": RestrictedCondition,
+		"restricted_with_classpath_exception": RestrictedClasspathExceptionCondition,
+		"restricted_allows_dynamic_linking": WeaklyRestrictedCondition,
+		"proprietary": ProprietaryCondition,
+		"by_exception_only": ByExceptionOnlyCondition,
+		"not_allowed": NotAllowedCondition,
+	}
+)
+
+// Name returns the condition string corresponding to the LicenseCondition.
 func (lc LicenseCondition) Name() string {
-	return lc.name
-}
-
-// Origin identifies the TargetNode where the condition originates.
-func (lc LicenseCondition) Origin() *TargetNode {
-	return lc.origin
-}
-
-// asString returns a string representation of a license condition:
-// origin+separator+condition.
-func (lc LicenseCondition) asString(separator string) string {
-	return lc.origin.name + separator + lc.name
-}
-
-// ConditionList implements introspection methods to arrays of LicenseCondition.
-type ConditionList []LicenseCondition
-
-
-// ConditionList orders arrays of LicenseCondition by Origin and Name.
-
-// Len returns the length of the list.
-func (l ConditionList) Len() int      { return len(l) }
-
-// Swap rearranges 2 elements in the list so each occupies the other's former position.
-func (l ConditionList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
-
-// Less returns true when the `i`th element is lexicographically less than tht `j`th element.
-func (l ConditionList) Less(i, j int) bool {
-	if l[i].origin.name == l[j].origin.name {
-		return l[i].name < l[j].name
+	switch lc {
+	case UnencumberedCondition:
+		return "unencumbered"
+	case PermissiveCondition:
+		return "permissive"
+	case NoticeCondition:
+		return "notice"
+	case ReciprocalCondition:
+		return "reciprocal"
+	case RestrictedCondition:
+		return "restricted"
+	case RestrictedClasspathExceptionCondition:
+		return "restricted_with_classpath_exception"
+	case WeaklyRestrictedCondition:
+		return "restricted_allows_dynamic_linking"
+	case ProprietaryCondition:
+		return "proprietary"
+	case ByExceptionOnlyCondition:
+		return "by_exception_only"
+	case NotAllowedCondition:
+		return "not_allowed"
 	}
-	return l[i].origin.name < l[j].origin.name
-}
-
-// String returns a string representation of the set.
-func (cl ConditionList) String() string {
-	var sb strings.Builder
-	fmt.Fprintf(&sb, "[")
-	sep := ""
-	for _, lc := range cl {
-		fmt.Fprintf(&sb, "%s%s:%s", sep, lc.origin.name, lc.name)
-		sep = ", "
-	}
-	fmt.Fprintf(&sb, "]")
-	return sb.String()
-}
-
-// Names returns the list of the conditions' names.
-func (cl ConditionList) Names() []string {
-	result := make([]string, 0, len(cl))
-	for _, lc := range cl {
-		result = append(result, lc.name)
-	}
-	return result
-}
-
-// HasByName returns true if the list contains any condition matching `name`.
-func (cl ConditionList) HasByName(name ConditionNames) bool {
-	for _, lc := range cl {
-		if name.Contains(lc.name) {
-			return true
-		}
-	}
-	return false
-}
-
-// ByName returns the sublist of conditions that match `name`.
-func (cl ConditionList) ByName(name ConditionNames) ConditionList {
-	result := make(ConditionList, 0, cl.CountByName(name))
-	for _, lc := range cl {
-		if name.Contains(lc.name) {
-			result = append(result, lc)
-		}
-	}
-	return result
-}
-
-// CountByName returns the size of the sublist of conditions that match `name`.
-func (cl ConditionList) CountByName(name ConditionNames) int {
-	size := 0
-	for _, lc := range cl {
-		if name.Contains(lc.name) {
-			size++
-		}
-	}
-	return size
-}
-
-// HasByOrigin returns true if the list contains any condition originating at `origin`.
-func (cl ConditionList) HasByOrigin(origin *TargetNode) bool {
-	for _, lc := range cl {
-		if lc.origin.name == origin.name {
-			return true
-		}
-	}
-	return false
-}
-
-// ByOrigin returns the sublist of conditions that originate at `origin`.
-func (cl ConditionList) ByOrigin(origin *TargetNode) ConditionList {
-	result := make(ConditionList, 0, cl.CountByOrigin(origin))
-	for _, lc := range cl {
-		if lc.origin.name == origin.name {
-			result = append(result, lc)
-		}
-	}
-	return result
-}
-
-// CountByOrigin returns the size of the sublist of conditions that originate at `origin`.
-func (cl ConditionList) CountByOrigin(origin *TargetNode) int {
-	size := 0
-	for _, lc := range cl {
-		if lc.origin.name == origin.name {
-			size++
-		}
-	}
-	return size
-}
-
-// ConditionNames implements the Contains predicate for slices of condition
-// name strings.
-type ConditionNames []string
-
-// Contains returns true if the name matches one of the ConditionNames.
-func (cn ConditionNames) Contains(name string) bool {
-	for _, cname := range cn {
-		if cname == name {
-			return true
-		}
-	}
-	return false
+	panic(fmt.Errorf("unrecognized license condition: %04x", lc))
 }
diff --git a/tools/compliance/condition_test.go b/tools/compliance/condition_test.go
index 0507469..778ce4a 100644
--- a/tools/compliance/condition_test.go
+++ b/tools/compliance/condition_test.go
@@ -15,204 +15,53 @@
 package compliance
 
 import (
-	"sort"
-	"strings"
 	"testing"
 )
 
-func TestConditionNames(t *testing.T) {
-	impliesShare := ConditionNames([]string{"restricted", "reciprocal"})
+func TestConditionSetHas(t *testing.T) {
+	impliesShare := ImpliesShared
 
-	if impliesShare.Contains("notice") {
-		t.Errorf("impliesShare.Contains(\"notice\") got true, want false")
+	t.Logf("testing with imliesShare=%04x", impliesShare)
+
+	if impliesShare.HasAny(NoticeCondition) {
+		t.Errorf("impliesShare.HasAny(\"notice\"=%04x) got true, want false", NoticeCondition)
 	}
 
-	if !impliesShare.Contains("restricted") {
-		t.Errorf("impliesShare.Contains(\"restricted\") got false, want true")
+	if !impliesShare.HasAny(RestrictedCondition) {
+		t.Errorf("impliesShare.HasAny(\"restricted\"=%04x) got false, want true", RestrictedCondition)
 	}
 
-	if !impliesShare.Contains("reciprocal") {
-		t.Errorf("impliesShare.Contains(\"reciprocal\") got false, want true")
+	if !impliesShare.HasAny(ReciprocalCondition) {
+		t.Errorf("impliesShare.HasAny(\"reciprocal\"=%04x) got false, want true", ReciprocalCondition)
 	}
 
-	if impliesShare.Contains("") {
-		t.Errorf("impliesShare.Contains(\"\") got true, want false")
+	if impliesShare.HasAny(LicenseCondition(0x0000)) {
+		t.Errorf("impliesShare.HasAny(nil=%04x) got true, want false", LicenseCondition(0x0000))
 	}
 }
 
-func TestConditionList(t *testing.T) {
-	tests := []struct {
-		name       string
-		conditions map[string][]string
-		byName     map[string][]string
-		byOrigin   map[string][]string
-	}{
-		{
-			name: "noticeonly",
-			conditions: map[string][]string{
-				"notice": []string{"bin1", "lib1"},
-			},
-			byName: map[string][]string{
-				"notice":     []string{"bin1", "lib1"},
-				"restricted": []string{},
-			},
-			byOrigin: map[string][]string{
-				"bin1": []string{"notice"},
-				"lib1": []string{"notice"},
-				"bin2": []string{},
-				"lib2": []string{},
-			},
-		},
-		{
-			name:       "empty",
-			conditions: map[string][]string{},
-			byName: map[string][]string{
-				"notice":     []string{},
-				"restricted": []string{},
-			},
-			byOrigin: map[string][]string{
-				"bin1": []string{},
-				"lib1": []string{},
-				"bin2": []string{},
-				"lib2": []string{},
-			},
-		},
-		{
-			name: "everything",
-			conditions: map[string][]string{
-				"notice":            []string{"bin1", "bin2", "lib1", "lib2"},
-				"reciprocal":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"restricted":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"by_exception_only": []string{"bin1", "bin2", "lib1", "lib2"},
-			},
-			byName: map[string][]string{
-				"permissive":        []string{},
-				"notice":            []string{"bin1", "bin2", "lib1", "lib2"},
-				"reciprocal":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"restricted":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"by_exception_only": []string{"bin1", "bin2", "lib1", "lib2"},
-			},
-			byOrigin: map[string][]string{
-				"bin1":  []string{"notice", "reciprocal", "restricted", "by_exception_only"},
-				"bin2":  []string{"notice", "reciprocal", "restricted", "by_exception_only"},
-				"lib1":  []string{"notice", "reciprocal", "restricted", "by_exception_only"},
-				"lib2":  []string{"notice", "reciprocal", "restricted", "by_exception_only"},
-				"other": []string{},
-			},
-		},
-		{
-			name: "allbutoneeach",
-			conditions: map[string][]string{
-				"notice":            []string{"bin2", "lib1", "lib2"},
-				"reciprocal":        []string{"bin1", "lib1", "lib2"},
-				"restricted":        []string{"bin1", "bin2", "lib2"},
-				"by_exception_only": []string{"bin1", "bin2", "lib1"},
-			},
-			byName: map[string][]string{
-				"permissive":        []string{},
-				"notice":            []string{"bin2", "lib1", "lib2"},
-				"reciprocal":        []string{"bin1", "lib1", "lib2"},
-				"restricted":        []string{"bin1", "bin2", "lib2"},
-				"by_exception_only": []string{"bin1", "bin2", "lib1"},
-			},
-			byOrigin: map[string][]string{
-				"bin1":  []string{"reciprocal", "restricted", "by_exception_only"},
-				"bin2":  []string{"notice", "restricted", "by_exception_only"},
-				"lib1":  []string{"notice", "reciprocal", "by_exception_only"},
-				"lib2":  []string{"notice", "reciprocal", "restricted"},
-				"other": []string{},
-			},
-		},
-		{
-			name: "oneeach",
-			conditions: map[string][]string{
-				"notice":            []string{"bin1"},
-				"reciprocal":        []string{"bin2"},
-				"restricted":        []string{"lib1"},
-				"by_exception_only": []string{"lib2"},
-			},
-			byName: map[string][]string{
-				"permissive":        []string{},
-				"notice":            []string{"bin1"},
-				"reciprocal":        []string{"bin2"},
-				"restricted":        []string{"lib1"},
-				"by_exception_only": []string{"lib2"},
-			},
-			byOrigin: map[string][]string{
-				"bin1":  []string{"notice"},
-				"bin2":  []string{"reciprocal"},
-				"lib1":  []string{"restricted"},
-				"lib2":  []string{"by_exception_only"},
-				"other": []string{},
-			},
-		},
+func TestConditionName(t *testing.T) {
+	for expected, condition := range RecognizedConditionNames {
+		actual := condition.Name()
+		if expected != actual {
+			t.Errorf("unexpected name for condition %04x: got %s, want %s", condition, actual, expected)
+		}
 	}
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			lg := newLicenseGraph()
-			cl := toConditionList(lg, tt.conditions)
-			for names, expected := range tt.byName {
-				name := ConditionNames(strings.Split(names, ":"))
-				if cl.HasByName(name) {
-					if len(expected) == 0 {
-						t.Errorf("unexpected ConditionList.HasByName(%q): got true, want false", name)
-					}
-				} else {
-					if len(expected) != 0 {
-						t.Errorf("unexpected ConditionList.HasByName(%q): got false, want true", name)
-					}
-				}
-				if len(expected) != cl.CountByName(name) {
-					t.Errorf("unexpected ConditionList.CountByName(%q): got %d, want %d", name, cl.CountByName(name), len(expected))
-				}
-				byName := cl.ByName(name)
-				if len(expected) != len(byName) {
-					t.Errorf("unexpected ConditionList.ByName(%q): got %v, want %v", name, byName, expected)
-				} else {
-					sort.Strings(expected)
-					actual := make([]string, 0, len(byName))
-					for _, lc := range byName {
-						actual = append(actual, lc.Origin().Name())
-					}
-					sort.Strings(actual)
-					for i := 0; i < len(expected); i++ {
-						if expected[i] != actual[i] {
-							t.Errorf("unexpected ConditionList.ByName(%q) index %d in %v: got %s, want %s", name, i, actual, actual[i], expected[i])
-						}
-					}
-				}
+}
+
+func TestConditionName_InvalidCondition(t *testing.T) {
+	panicked := false
+	var lc LicenseCondition
+	func() {
+		defer func() {
+			if err := recover(); err != nil {
+				panicked = true
 			}
-			for origin, expected := range tt.byOrigin {
-				onode := newTestNode(lg, origin)
-				if cl.HasByOrigin(onode) {
-					if len(expected) == 0 {
-						t.Errorf("unexpected ConditionList.HasByOrigin(%q): got true, want false", origin)
-					}
-				} else {
-					if len(expected) != 0 {
-						t.Errorf("unexpected ConditionList.HasByOrigin(%q): got false, want true", origin)
-					}
-				}
-				if len(expected) != cl.CountByOrigin(onode) {
-					t.Errorf("unexpected ConditionList.CountByOrigin(%q): got %d, want %d", origin, cl.CountByOrigin(onode), len(expected))
-				}
-				byOrigin := cl.ByOrigin(onode)
-				if len(expected) != len(byOrigin) {
-					t.Errorf("unexpected ConditionList.ByOrigin(%q): got %v, want %v", origin, byOrigin, expected)
-				} else {
-					sort.Strings(expected)
-					actual := make([]string, 0, len(byOrigin))
-					for _, lc := range byOrigin {
-						actual = append(actual, lc.Name())
-					}
-					sort.Strings(actual)
-					for i := 0; i < len(expected); i++ {
-						if expected[i] != actual[i] {
-							t.Errorf("unexpected ConditionList.ByOrigin(%q) index %d in %v: got %s, want %s", origin, i, actual, actual[i], expected[i])
-						}
-					}
-				}
-			}
-		})
+		}()
+		name := lc.Name()
+		t.Errorf("invalid condition unexpected name: got %s, wanted panic", name)
+	}()
+	if !panicked {
+		t.Errorf("no expected panic for %04x.Name(): got no panic, wanted panic", lc)
 	}
 }
diff --git a/tools/compliance/conditionset.go b/tools/compliance/conditionset.go
index 1ad15ca..7a12ddc 100644
--- a/tools/compliance/conditionset.go
+++ b/tools/compliance/conditionset.go
@@ -16,263 +16,174 @@
 
 import (
 	"fmt"
+	"strings"
 )
 
-// NewLicenseConditionSet creates a new instance or variable of *LicenseConditionSet.
-func NewLicenseConditionSet(conditions ...LicenseCondition) *LicenseConditionSet {
-	cs := newLicenseConditionSet()
-	cs.Add(conditions...)
+// LicenseConditionSet identifies sets of license conditions.
+type LicenseConditionSet LicenseCondition
+
+// AllLicenseConditions is the set of all recognized license conditions.
+const AllLicenseConditions = LicenseConditionSet(LicenseConditionMask)
+
+// NewLicenseConditionSet returns a set containing exactly the elements of
+// `conditions`.
+func NewLicenseConditionSet(conditions ...LicenseCondition) LicenseConditionSet {
+	cs := LicenseConditionSet(0x00)
+	for _, lc := range conditions {
+		cs |= LicenseConditionSet(lc)
+	}
 	return cs
 }
 
-// LicenseConditionSet describes a mutable set of immutable license conditions.
-type LicenseConditionSet struct {
-	// conditions describes the set of license conditions i.e. (condition name, origin target) pairs
-	// by mapping condition name -> origin target -> true.
-	conditions map[string]map[*TargetNode]bool
-}
-
-// Add makes all `conditions` members of the set if they were not previously.
-func (cs *LicenseConditionSet) Add(conditions ...LicenseCondition) {
-	if len(conditions) == 0 {
-		return
-	}
+// Plus returns a new set containing all of the elements of `cs` and all of the
+// `conditions`.
+func (cs LicenseConditionSet) Plus(conditions ...LicenseCondition) LicenseConditionSet {
+	result := cs
 	for _, lc := range conditions {
-		if _, ok := cs.conditions[lc.name]; !ok {
-			cs.conditions[lc.name] = make(map[*TargetNode]bool)
-		}
-		cs.conditions[lc.name][lc.origin] = true
+		result |= LicenseConditionSet(lc)
 	}
+	return result
 }
 
-// AddSet makes all elements of `conditions` members of the set if they were not previously.
-func (cs *LicenseConditionSet) AddSet(other *LicenseConditionSet) {
-	if len(other.conditions) == 0 {
-		return
+// Union returns a new set containing all of the elements of `cs` and all of the
+// elements of the `other` sets.
+func (cs LicenseConditionSet) Union(other ...LicenseConditionSet) LicenseConditionSet {
+	result := cs
+	for _, ls := range other {
+		result |= ls
 	}
-	for name, origins := range other.conditions {
-		if len(origins) == 0 {
-			continue
-		}
-		if _, ok := cs.conditions[name]; !ok {
-			cs.conditions[name] = make(map[*TargetNode]bool)
-		}
-		for origin := range origins {
-			cs.conditions[name][origin] = other.conditions[name][origin]
-		}
-	}
+	return result
 }
 
-// ByName returns a list of the conditions in the set matching `names`.
-func (cs *LicenseConditionSet) ByName(names ...ConditionNames) *LicenseConditionSet {
-	other := newLicenseConditionSet()
-	for _, cn := range names {
-		for _, name := range cn {
-			if origins, ok := cs.conditions[name]; ok {
-				other.conditions[name] = make(map[*TargetNode]bool)
-				for origin := range origins {
-					other.conditions[name][origin] = true
-				}
-			}
-		}
+// MatchingAny returns the subset of `cs` equal to any of the `conditions`.
+func (cs LicenseConditionSet) MatchingAny(conditions ...LicenseCondition) LicenseConditionSet {
+	result := LicenseConditionSet(0x00)
+	for _, lc := range conditions {
+		result |= cs & LicenseConditionSet(lc)
 	}
-	return other
+	return result
 }
 
-// HasAnyByName returns true if the set contains any conditions matching `names` originating at any target.
-func (cs *LicenseConditionSet) HasAnyByName(names ...ConditionNames) bool {
-	for _, cn := range names {
-		for _, name := range cn {
-			if origins, ok := cs.conditions[name]; ok {
-				if len(origins) > 0 {
-					return true
-				}
-			}
-		}
+// MatchingAnySet returns the subset of `cs` that are members of any of the
+// `other` sets.
+func (cs LicenseConditionSet) MatchingAnySet(other ...LicenseConditionSet) LicenseConditionSet {
+	result := LicenseConditionSet(0x00)
+	for _, ls := range other {
+		result |= cs & ls
 	}
-	return false
+	return result
 }
 
-// CountByName returns the number of conditions matching `names` originating at any target.
-func (cs *LicenseConditionSet) CountByName(names ...ConditionNames) int {
-	size := 0
-	for _, cn := range names {
-		for _, name := range cn {
-			if origins, ok := cs.conditions[name]; ok {
-				size += len(origins)
-			}
-		}
-	}
-	return size
-}
-
-// ByOrigin returns all of the conditions that originate at `origin` regardless of name.
-func (cs *LicenseConditionSet) ByOrigin(origin *TargetNode) *LicenseConditionSet {
-	other := newLicenseConditionSet()
-	for name, origins := range cs.conditions {
-		if _, ok := origins[origin]; ok {
-			other.conditions[name] = make(map[*TargetNode]bool)
-			other.conditions[name][origin] = true
-		}
-	}
-	return other
-}
-
-// HasAnyByOrigin returns true if the set contains any conditions originating at `origin` regardless of condition name.
-func (cs *LicenseConditionSet) HasAnyByOrigin(origin *TargetNode) bool {
-	for _, origins := range cs.conditions {
-		if _, ok := origins[origin]; ok {
+// HasAny returns true when `cs` contains at least one of the `conditions`.
+func (cs LicenseConditionSet) HasAny(conditions ...LicenseCondition) bool {
+	for _, lc := range conditions {
+		if 0x0000 != (cs & LicenseConditionSet(lc)) {
 			return true
 		}
 	}
 	return false
 }
 
-// CountByOrigin returns the number of conditions originating at `origin` regardless of condition name.
-func (cs *LicenseConditionSet) CountByOrigin(origin *TargetNode) int {
-	size := 0
-	for _, origins := range cs.conditions {
-		if _, ok := origins[origin]; ok {
-			size++
-		}
-	}
-	return size
-}
-
-// AsList returns a list of all the conditions in the set.
-func (cs *LicenseConditionSet) AsList() ConditionList {
-	result := make(ConditionList, 0, cs.Count())
-	for name, origins := range cs.conditions {
-		for origin := range origins {
-			result = append(result, LicenseCondition{name, origin})
-		}
-	}
-	return result
-}
-
-// Names returns a list of the names of the conditions in the set.
-func (cs *LicenseConditionSet) Names() []string {
-	result := make([]string, 0, len(cs.conditions))
-	for name := range cs.conditions {
-		result = append(result, name)
-	}
-	return result
-}
-
-// Count returns the number of conditions in the set.
-func (cs *LicenseConditionSet) Count() int {
-	size := 0
-	for _, origins := range cs.conditions {
-		size += len(origins)
-	}
-	return size
-}
-
-// Copy creates a new LicenseCondition variable with the same value.
-func (cs *LicenseConditionSet) Copy() *LicenseConditionSet {
-	other := newLicenseConditionSet()
-	for name := range cs.conditions {
-		other.conditions[name] = make(map[*TargetNode]bool)
-		for origin := range cs.conditions[name] {
-			other.conditions[name][origin] = cs.conditions[name][origin]
-		}
-	}
-	return other
-}
-
-// HasCondition returns true if the set contains any condition matching both `names` and `origin`.
-func (cs *LicenseConditionSet) HasCondition(names ConditionNames, origin *TargetNode) bool {
-	for _, name := range names {
-		if origins, ok := cs.conditions[name]; ok {
-			_, isPresent := origins[origin]
-			if isPresent {
-				return true
-			}
+// MatchesAnySet returns true when `cs` has a non-empty intersection with at
+// least one of the `other` condition sets.
+func (cs LicenseConditionSet) MatchesAnySet(other ...LicenseConditionSet) bool {
+	for _, ls := range other {
+		if 0x0000 != (cs & ls) {
+			return true
 		}
 	}
 	return false
 }
 
-// IsEmpty returns true when the set of conditions contains zero elements.
-func (cs *LicenseConditionSet) IsEmpty() bool {
-	for _, origins := range cs.conditions {
-		if 0 < len(origins) {
+// HasAll returns true when `cs` contains every one of the `conditions`.
+func (cs LicenseConditionSet) HasAll(conditions ...LicenseCondition) bool {
+	for _, lc := range conditions {
+		if 0x0000 == (cs & LicenseConditionSet(lc)) {
 			return false
 		}
 	}
 	return true
 }
 
-// RemoveAllByName changes the set to delete all conditions matching `names`.
-func (cs *LicenseConditionSet) RemoveAllByName(names ...ConditionNames) {
-	for _, cn := range names {
-		for _, name := range cn {
-			delete(cs.conditions, name)
+// MatchesEverySet returns true when `cs` has a non-empty intersection with
+// each of the `other` condition sets.
+func (cs LicenseConditionSet) MatchesEverySet(other ...LicenseConditionSet) bool {
+	for _, ls := range other {
+		if 0x0000 == (cs & ls) {
+			return false
 		}
 	}
+	return true
 }
 
-// Remove changes the set to delete `conditions`.
-func (cs *LicenseConditionSet) Remove(conditions ...LicenseCondition) {
+// Intersection returns the subset of `cs` that are members of every `other`
+// set.
+func (cs LicenseConditionSet) Intersection(other ...LicenseConditionSet) LicenseConditionSet {
+	result := cs
+	for _, ls := range other {
+		result &= ls
+	}
+	return result
+}
+
+// Minus returns the subset of `cs` that are not equaal to any `conditions`.
+func (cs LicenseConditionSet) Minus(conditions ...LicenseCondition) LicenseConditionSet {
+	result := cs
 	for _, lc := range conditions {
-		if _, isPresent := cs.conditions[lc.name]; !isPresent {
-			panic(fmt.Errorf("attempt to remove non-existent condition: %q", lc.asString(":")))
-		}
-		if _, isPresent := cs.conditions[lc.name][lc.origin]; !isPresent {
-			panic(fmt.Errorf("attempt to remove non-existent origin: %q", lc.asString(":")))
-		}
-		delete(cs.conditions[lc.name], lc.origin)
+		result &^= LicenseConditionSet(lc)
 	}
+	return result
 }
 
-// removeSet changes the set to delete all conditions also present in `other`.
-func (cs *LicenseConditionSet) RemoveSet(other *LicenseConditionSet) {
-	for name, origins := range other.conditions {
-		if _, isPresent := cs.conditions[name]; !isPresent {
-			continue
-		}
-		for origin := range origins {
-			delete(cs.conditions[name], origin)
+// Difference returns the subset of `cs` that are not members of any `other`
+// set.
+func (cs LicenseConditionSet) Difference(other ...LicenseConditionSet) LicenseConditionSet {
+	result := cs
+	for _, ls := range other {
+		result &^= ls
+	}
+	return result
+}
+
+// Len returns the number of license conditions in the set.
+func (cs LicenseConditionSet) Len() int {
+	size := 0
+	for lc := LicenseConditionSet(0x01); 0x00 != (AllLicenseConditions & lc); lc <<= 1 {
+		if 0x00 != (cs & lc) {
+			size++
 		}
 	}
+	return size
 }
 
-// compliance-only LicenseConditionSet methods
-
-// newLicenseConditionSet constructs a set of `conditions`.
-func newLicenseConditionSet() *LicenseConditionSet {
-	return &LicenseConditionSet{make(map[string]map[*TargetNode]bool)}
-}
-
-// add changes the set to include each element of `conditions` originating at `origin`.
-func (cs *LicenseConditionSet) add(origin *TargetNode, conditions ...string) {
-	for _, name := range conditions {
-		if _, ok := cs.conditions[name]; !ok {
-			cs.conditions[name] = make(map[*TargetNode]bool)
-		}
-		cs.conditions[name][origin] = true
-	}
-}
-
-// asStringList returns the conditions in the set as `separator`-separated (origin, condition-name) pair strings.
-func (cs *LicenseConditionSet) asStringList(separator string) []string {
-	result := make([]string, 0, cs.Count())
-	for name, origins := range cs.conditions {
-		for origin := range origins {
-			result = append(result, origin.name+separator+name)
+// AsList returns an array of the license conditions in the set.
+func (cs LicenseConditionSet) AsList() []LicenseCondition {
+	result := make([]LicenseCondition, 0, cs.Len())
+	for lc := LicenseConditionSet(0x01); 0x00 != (AllLicenseConditions & lc); lc <<= 1 {
+		if 0x00 != (cs & lc) {
+			result = append(result, LicenseCondition(lc))
 		}
 	}
 	return result
 }
 
-// conditionNamesArray implements a `contains` predicate for arrays of ConditionNames
-type conditionNamesArray []ConditionNames
-
-func (cn conditionNamesArray) contains(name string) bool {
-	for _, names := range cn {
-		if names.Contains(name) {
-			return true
+// Names returns an array of the names of the license conditions in the set.
+func (cs LicenseConditionSet) Names() []string {
+	result := make([]string, 0, cs.Len())
+	for lc := LicenseConditionSet(0x01); 0x00 != (AllLicenseConditions & lc); lc <<= 1 {
+		if 0x00 != (cs & lc) {
+			result = append(result, LicenseCondition(lc).Name())
 		}
 	}
-	return false
+	return result
+}
+
+// IsEmpty returns true when the set contains no license conditions.
+func (cs LicenseConditionSet) IsEmpty() bool {
+	return 0x00 == (cs & AllLicenseConditions)
+}
+
+// String returns a human-readable string representation of the set.
+func (cs LicenseConditionSet) String() string {
+	return fmt.Sprintf("{%s}", strings.Join(cs.Names(), "|"))
 }
diff --git a/tools/compliance/conditionset_test.go b/tools/compliance/conditionset_test.go
index eac0680..c7306e7 100644
--- a/tools/compliance/conditionset_test.go
+++ b/tools/compliance/conditionset_test.go
@@ -15,576 +15,643 @@
 package compliance
 
 import (
-	"sort"
 	"strings"
 	"testing"
 )
 
-type byName map[string][]string
-
-func (bn byName) checkPublic(ls *LicenseConditionSet, t *testing.T) {
-	for names, expected := range bn {
-		name := ConditionNames(strings.Split(names, ":"))
-		if ls.HasAnyByName(name) {
-			if len(expected) == 0 {
-				t.Errorf("unexpected LicenseConditionSet.HasAnyByName(%q): got true, want false", name)
-			}
-		} else {
-			if len(expected) != 0 {
-				t.Errorf("unexpected LicenseConditionSet.HasAnyByName(%q): got false, want true", name)
-			}
-		}
-		if len(expected) != ls.CountByName(name) {
-			t.Errorf("unexpected LicenseConditionSet.CountByName(%q): got %d, want %d", name, ls.CountByName(name), len(expected))
-		}
-		byName := ls.ByName(name).AsList()
-		if len(expected) != len(byName) {
-			t.Errorf("unexpected LicenseConditionSet.ByName(%q): got %v, want %v", name, byName, expected)
-		} else {
-			sort.Strings(expected)
-			actual := make([]string, 0, len(byName))
-			for _, lc := range byName {
-				actual = append(actual, lc.Origin().Name())
-			}
-			sort.Strings(actual)
-			for i := 0; i < len(expected); i++ {
-				if expected[i] != actual[i] {
-					t.Errorf("unexpected LicenseConditionSet.ByName(%q) index %d in %v: got %s, want %s", name, i, actual, actual[i], expected[i])
-				}
-			}
-		}
-	}
-}
-
-type byOrigin map[string][]string
-
-func (bo byOrigin) checkPublic(lg *LicenseGraph, ls *LicenseConditionSet, t *testing.T) {
-	expectedCount := 0
-	for origin, expected := range bo {
-		expectedCount += len(expected)
-		onode := newTestNode(lg, origin)
-		if ls.HasAnyByOrigin(onode) {
-			if len(expected) == 0 {
-				t.Errorf("unexpected LicenseConditionSet.HasAnyByOrigin(%q): got true, want false", origin)
-			}
-		} else {
-			if len(expected) != 0 {
-				t.Errorf("unexpected LicenseConditionSet.HasAnyByOrigin(%q): got false, want true", origin)
-			}
-		}
-		if len(expected) != ls.CountByOrigin(onode) {
-			t.Errorf("unexpected LicenseConditionSet.CountByOrigin(%q): got %d, want %d", origin, ls.CountByOrigin(onode), len(expected))
-		}
-		byOrigin := ls.ByOrigin(onode).AsList()
-		if len(expected) != len(byOrigin) {
-			t.Errorf("unexpected LicenseConditionSet.ByOrigin(%q): got %v, want %v", origin, byOrigin, expected)
-		} else {
-			sort.Strings(expected)
-			actual := make([]string, 0, len(byOrigin))
-			for _, lc := range byOrigin {
-				actual = append(actual, lc.Name())
-			}
-			sort.Strings(actual)
-			for i := 0; i < len(expected); i++ {
-				if expected[i] != actual[i] {
-					t.Errorf("unexpected LicenseConditionSet.ByOrigin(%q) index %d in %v: got %s, want %s", origin, i, actual, actual[i], expected[i])
-				}
-			}
-		}
-	}
-	if expectedCount != ls.Count() {
-		t.Errorf("unexpected LicenseConditionSet.Count(): got %d, want %d", ls.Count(), expectedCount)
-	}
-	if ls.IsEmpty() {
-		if expectedCount != 0 {
-			t.Errorf("unexpected LicenseConditionSet.IsEmpty(): got true, want false")
-		}
-	} else {
-		if expectedCount == 0 {
-			t.Errorf("unexpected LicenseConditionSet.IsEmpty(): got false, want true")
-		}
-	}
-}
-
 func TestConditionSet(t *testing.T) {
 	tests := []struct {
-		name       string
-		conditions map[string][]string
-		add        map[string][]string
-		byName     map[string][]string
-		byOrigin   map[string][]string
+		name        string
+		conditions  []string
+		plus        *[]string
+		minus       *[]string
+		matchingAny map[string][]string
+		expected    []string
 	}{
 		{
 			name:       "empty",
-			conditions: map[string][]string{},
-			add:        map[string][]string{},
-			byName: map[string][]string{
+			conditions: []string{},
+			plus:       &[]string{},
+			matchingAny: map[string][]string{
 				"notice":     []string{},
 				"restricted": []string{},
+				"restricted|reciprocal": []string{},
 			},
-			byOrigin: map[string][]string{
-				"bin1": []string{},
-				"lib1": []string{},
-				"bin2": []string{},
-				"lib2": []string{},
+			expected:   []string{},
+		},
+		{
+			name:       "emptyminusnothing",
+			conditions: []string{},
+			minus:      &[]string{},
+			matchingAny: map[string][]string{
+				"notice":     []string{},
+				"restricted": []string{},
+				"restricted|reciprocal": []string{},
 			},
+			expected:   []string{},
+		},
+		{
+			name:       "emptyminusnotice",
+			conditions: []string{},
+			minus:      &[]string{"notice"},
+			matchingAny: map[string][]string{
+				"notice":     []string{},
+				"restricted": []string{},
+				"restricted|reciprocal": []string{},
+			},
+			expected:   []string{},
 		},
 		{
 			name: "noticeonly",
-			conditions: map[string][]string{
-				"notice": []string{"bin1", "lib1"},
-			},
-			byName: map[string][]string{
-				"notice":     []string{"bin1", "lib1"},
+			conditions: []string{"notice"},
+			matchingAny: map[string][]string{
+				"notice":     []string{"notice"},
+				"notice|proprietary":     []string{"notice"},
 				"restricted": []string{},
 			},
-			byOrigin: map[string][]string{
-				"bin1": []string{"notice"},
-				"lib1": []string{"notice"},
-				"bin2": []string{},
-				"lib2": []string{},
-			},
+			expected: []string{"notice"},
 		},
 		{
-			name: "noticeonlyadded",
-			conditions: map[string][]string{
-				"notice": []string{"bin1", "lib1"},
-			},
-			add: map[string][]string{
-				"notice": []string{"bin1", "bin2"},
-			},
-			byName: map[string][]string{
-				"notice":     []string{"bin1", "bin2", "lib1"},
+			name: "allnoticeonly",
+			conditions: []string{"notice"},
+			plus: &[]string{"notice"},
+			matchingAny: map[string][]string{
+				"notice":     []string{"notice"},
+				"notice|proprietary":     []string{"notice"},
 				"restricted": []string{},
 			},
-			byOrigin: map[string][]string{
-				"bin1": []string{"notice"},
-				"lib1": []string{"notice"},
-				"bin2": []string{"notice"},
-				"lib2": []string{},
+			expected: []string{"notice"},
+		},
+		{
+			name: "emptyplusnotice",
+			conditions: []string{},
+			plus: &[]string{"notice"},
+			matchingAny: map[string][]string{
+				"notice":     []string{"notice"},
+				"notice|proprietary":     []string{"notice"},
+				"restricted": []string{},
 			},
+			expected: []string{"notice"},
 		},
 		{
 			name: "everything",
-			conditions: map[string][]string{
-				"notice":            []string{"bin1", "bin2", "lib1", "lib2"},
-				"reciprocal":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"restricted":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"by_exception_only": []string{"bin1", "bin2", "lib1", "lib2"},
+			conditions: []string{"unencumbered", "permissive", "notice", "reciprocal", "restricted", "proprietary"},
+			plus: &[]string{"restricted_with_classpath_exception", "restricted_allows_dynamic_linking", "by_exception_only", "not_allowed"},
+			matchingAny: map[string][]string{
+				"unencumbered": []string{"unencumbered"},
+				"permissive":       []string{"permissive"},
+				"notice":     []string{"notice"},
+				"reciprocal":     []string{"reciprocal"},
+				"restricted":     []string{"restricted"},
+				"restricted_with_classpath_exception":     []string{"restricted_with_classpath_exception"},
+				"restricted_allows_dynamic_linking":     []string{"restricted_allows_dynamic_linking"},
+				"proprietary":     []string{"proprietary"},
+				"by_exception_only":     []string{"by_exception_only"},
+				"not_allowed":     []string{"not_allowed"},
+				"notice|proprietary":     []string{"notice", "proprietary"},
 			},
-			add: map[string][]string{
-				"notice":            []string{"bin1", "bin2", "lib1", "lib2"},
-				"reciprocal":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"restricted":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"by_exception_only": []string{"bin1", "bin2", "lib1", "lib2"},
-			},
-			byName: map[string][]string{
-				"permissive":        []string{},
-				"notice":            []string{"bin1", "bin2", "lib1", "lib2"},
-				"reciprocal":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"restricted":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"by_exception_only": []string{"bin1", "bin2", "lib1", "lib2"},
-			},
-			byOrigin: map[string][]string{
-				"bin1":  []string{"notice", "reciprocal", "restricted", "by_exception_only"},
-				"bin2":  []string{"notice", "reciprocal", "restricted", "by_exception_only"},
-				"lib1":  []string{"notice", "reciprocal", "restricted", "by_exception_only"},
-				"lib2":  []string{"notice", "reciprocal", "restricted", "by_exception_only"},
-				"other": []string{},
+			expected: []string{
+				"unencumbered",
+				"permissive",
+				"notice",
+				"reciprocal",
+				"restricted",
+				"restricted_with_classpath_exception",
+				"restricted_allows_dynamic_linking",
+				"proprietary",
+				"by_exception_only",
+				"not_allowed",
 			},
 		},
 		{
-			name: "allbutoneeach",
-			conditions: map[string][]string{
-				"notice":            []string{"bin2", "lib1", "lib2"},
-				"reciprocal":        []string{"bin1", "lib1", "lib2"},
-				"restricted":        []string{"bin1", "bin2", "lib2"},
-				"by_exception_only": []string{"bin1", "bin2", "lib1"},
+			name: "everythingplusminusnothing",
+			conditions: []string{
+				"unencumbered",
+				"permissive",
+				"notice",
+				"reciprocal",
+				"restricted",
+				"restricted_with_classpath_exception",
+				"restricted_allows_dynamic_linking",
+				"proprietary",
+				"by_exception_only",
+				"not_allowed",
 			},
-			byName: map[string][]string{
-				"permissive":        []string{},
-				"notice":            []string{"bin2", "lib1", "lib2"},
-				"reciprocal":        []string{"bin1", "lib1", "lib2"},
-				"restricted":        []string{"bin1", "bin2", "lib2"},
-				"by_exception_only": []string{"bin1", "bin2", "lib1"},
+			plus: &[]string{},
+			minus: &[]string{},
+			matchingAny: map[string][]string{
+				"unencumbered|permissive|notice": []string{"unencumbered", "permissive", "notice"},
+				"restricted|reciprocal":     []string{"reciprocal", "restricted"},
+				"proprietary|by_exception_only":     []string{"proprietary", "by_exception_only"},
+				"not_allowed":     []string{"not_allowed"},
 			},
-			byOrigin: map[string][]string{
-				"bin1":  []string{"reciprocal", "restricted", "by_exception_only"},
-				"bin2":  []string{"notice", "restricted", "by_exception_only"},
-				"lib1":  []string{"notice", "reciprocal", "by_exception_only"},
-				"lib2":  []string{"notice", "reciprocal", "restricted"},
-				"other": []string{},
+			expected: []string{
+				"unencumbered",
+				"permissive",
+				"notice",
+				"reciprocal",
+				"restricted",
+				"restricted_with_classpath_exception",
+				"restricted_allows_dynamic_linking",
+				"proprietary",
+				"by_exception_only",
+				"not_allowed",
 			},
 		},
 		{
-			name: "allbutoneeachadded",
-			conditions: map[string][]string{
-				"notice":            []string{"bin2", "lib1", "lib2"},
-				"reciprocal":        []string{"bin1", "lib1", "lib2"},
-				"restricted":        []string{"bin1", "bin2", "lib2"},
-				"by_exception_only": []string{"bin1", "bin2", "lib1"},
+			name: "allbutone",
+			conditions: []string{"unencumbered", "permissive", "notice", "reciprocal", "restricted", "proprietary"},
+			plus: &[]string{"restricted_allows_dynamic_linking", "by_exception_only", "not_allowed"},
+			matchingAny: map[string][]string{
+				"unencumbered": []string{"unencumbered"},
+				"permissive":       []string{"permissive"},
+				"notice":     []string{"notice"},
+				"reciprocal":     []string{"reciprocal"},
+				"restricted":     []string{"restricted"},
+				"restricted_with_classpath_exception":     []string{},
+				"restricted_allows_dynamic_linking":     []string{"restricted_allows_dynamic_linking"},
+				"proprietary":     []string{"proprietary"},
+				"by_exception_only":     []string{"by_exception_only"},
+				"not_allowed":     []string{"not_allowed"},
+				"notice|proprietary":     []string{"notice", "proprietary"},
 			},
-			add: map[string][]string{
-				"notice":            []string{"bin2", "lib1", "lib2"},
-				"reciprocal":        []string{"bin1", "lib1", "lib2"},
-				"restricted":        []string{"bin1", "bin2", "lib2"},
-				"by_exception_only": []string{"bin1", "bin2", "lib1"},
-			},
-			byName: map[string][]string{
-				"permissive":        []string{},
-				"notice":            []string{"bin2", "lib1", "lib2"},
-				"reciprocal":        []string{"bin1", "lib1", "lib2"},
-				"restricted":        []string{"bin1", "bin2", "lib2"},
-				"by_exception_only": []string{"bin1", "bin2", "lib1"},
-			},
-			byOrigin: map[string][]string{
-				"bin1":  []string{"reciprocal", "restricted", "by_exception_only"},
-				"bin2":  []string{"notice", "restricted", "by_exception_only"},
-				"lib1":  []string{"notice", "reciprocal", "by_exception_only"},
-				"lib2":  []string{"notice", "reciprocal", "restricted"},
-				"other": []string{},
+			expected: []string{
+				"unencumbered",
+				"permissive",
+				"notice",
+				"reciprocal",
+				"restricted",
+				"restricted_allows_dynamic_linking",
+				"proprietary",
+				"by_exception_only",
+				"not_allowed",
 			},
 		},
 		{
-			name: "allbutoneeachfilled",
-			conditions: map[string][]string{
-				"notice":            []string{"bin2", "lib1", "lib2"},
-				"reciprocal":        []string{"bin1", "lib1", "lib2"},
-				"restricted":        []string{"bin1", "bin2", "lib2"},
-				"by_exception_only": []string{"bin1", "bin2", "lib1"},
+			name: "everythingminusone",
+			conditions: []string{
+				"unencumbered",
+				"permissive",
+				"notice",
+				"reciprocal",
+				"restricted",
+				"restricted_with_classpath_exception",
+				"restricted_allows_dynamic_linking",
+				"proprietary",
+				"by_exception_only",
+				"not_allowed",
 			},
-			add: map[string][]string{
-				"notice":            []string{"bin1", "bin2", "lib1"},
-				"reciprocal":        []string{"bin1", "bin2", "lib2"},
-				"restricted":        []string{"bin1", "lib1", "lib2"},
-				"by_exception_only": []string{"bin2", "lib1", "lib2"},
+			minus: &[]string{"restricted_allows_dynamic_linking"},
+			matchingAny: map[string][]string{
+				"unencumbered": []string{"unencumbered"},
+				"permissive":       []string{"permissive"},
+				"notice":     []string{"notice"},
+				"reciprocal":     []string{"reciprocal"},
+				"restricted":     []string{"restricted"},
+				"restricted_with_classpath_exception":     []string{"restricted_with_classpath_exception"},
+				"restricted_allows_dynamic_linking":     []string{},
+				"proprietary":     []string{"proprietary"},
+				"by_exception_only":     []string{"by_exception_only"},
+				"not_allowed":     []string{"not_allowed"},
+				"restricted|proprietary":     []string{"restricted", "proprietary"},
 			},
-			byName: map[string][]string{
-				"permissive":        []string{},
-				"notice":            []string{"bin1", "bin2", "lib1", "lib2"},
-				"reciprocal":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"restricted":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"by_exception_only": []string{"bin1", "bin2", "lib1", "lib2"},
-			},
-			byOrigin: map[string][]string{
-				"bin1":  []string{"notice", "reciprocal", "restricted", "by_exception_only"},
-				"bin2":  []string{"notice", "reciprocal", "restricted", "by_exception_only"},
-				"lib1":  []string{"notice", "reciprocal", "restricted", "by_exception_only"},
-				"lib2":  []string{"notice", "reciprocal", "restricted", "by_exception_only"},
-				"other": []string{},
+			expected: []string{
+				"unencumbered",
+				"permissive",
+				"notice",
+				"reciprocal",
+				"restricted",
+				"restricted_with_classpath_exception",
+				"proprietary",
+				"by_exception_only",
+				"not_allowed",
 			},
 		},
 		{
-			name: "oneeach",
-			conditions: map[string][]string{
-				"notice":            []string{"bin1"},
-				"reciprocal":        []string{"bin2"},
-				"restricted":        []string{"lib1"},
-				"by_exception_only": []string{"lib2"},
+			name: "everythingminuseverything",
+			conditions: []string{
+				"unencumbered",
+				"permissive",
+				"notice",
+				"reciprocal",
+				"restricted",
+				"restricted_with_classpath_exception",
+				"restricted_allows_dynamic_linking",
+				"proprietary",
+				"by_exception_only",
+				"not_allowed",
 			},
-			byName: map[string][]string{
-				"permissive":        []string{},
-				"notice":            []string{"bin1"},
-				"reciprocal":        []string{"bin2"},
-				"restricted":        []string{"lib1"},
-				"by_exception_only": []string{"lib2"},
+			minus: &[]string{
+				"unencumbered",
+				"permissive",
+				"notice",
+				"reciprocal",
+				"restricted",
+				"restricted_with_classpath_exception",
+				"restricted_allows_dynamic_linking",
+				"proprietary",
+				"by_exception_only",
+				"not_allowed",
 			},
-			byOrigin: map[string][]string{
-				"bin1":  []string{"notice"},
-				"bin2":  []string{"reciprocal"},
-				"lib1":  []string{"restricted"},
-				"lib2":  []string{"by_exception_only"},
-				"other": []string{},
+			matchingAny: map[string][]string{
+				"unencumbered": []string{},
+				"permissive":       []string{},
+				"notice":     []string{},
+				"reciprocal":     []string{},
+				"restricted":     []string{},
+				"restricted_with_classpath_exception":     []string{},
+				"restricted_allows_dynamic_linking":     []string{},
+				"proprietary":     []string{},
+				"by_exception_only":     []string{},
+				"not_allowed":     []string{},
+				"restricted|proprietary":     []string{},
 			},
+			expected: []string{},
 		},
 		{
-			name: "oneeachoverlap",
-			conditions: map[string][]string{
-				"notice":            []string{"bin1"},
-				"reciprocal":        []string{"bin2"},
-				"restricted":        []string{"lib1"},
-				"by_exception_only": []string{"lib2"},
+			name: "restrictedplus",
+			conditions: []string{"restricted", "restricted_with_classpath_exception", "restricted_allows_dynamic_linking"},
+			plus: &[]string{"permissive", "notice", "restricted", "proprietary"},
+			matchingAny: map[string][]string{
+				"unencumbered":     []string{},
+				"permissive":     []string{"permissive"},
+				"notice":     []string{"notice"},
+				"restricted":     []string{"restricted"},
+				"restricted_with_classpath_exception":     []string{"restricted_with_classpath_exception"},
+				"restricted_allows_dynamic_linking":     []string{"restricted_allows_dynamic_linking"},
+				"proprietary":     []string{"proprietary"},
+				"restricted|proprietary":     []string{"restricted", "proprietary"},
+				"by_exception_only": []string{},
+				"proprietary|by_exception_only":     []string{"proprietary"},
 			},
-			add: map[string][]string{
-				"notice":            []string{"lib2"},
-				"reciprocal":        []string{"lib1"},
-				"restricted":        []string{"bin2"},
-				"by_exception_only": []string{"bin1"},
-			},
-			byName: map[string][]string{
-				"permissive":        []string{},
-				"notice":            []string{"bin1", "lib2"},
-				"reciprocal":        []string{"bin2", "lib1"},
-				"restricted":        []string{"bin2", "lib1"},
-				"by_exception_only": []string{"bin1", "lib2"},
-			},
-			byOrigin: map[string][]string{
-				"bin1":  []string{"by_exception_only", "notice"},
-				"bin2":  []string{"reciprocal", "restricted"},
-				"lib1":  []string{"reciprocal", "restricted"},
-				"lib2":  []string{"by_exception_only", "notice"},
-				"other": []string{},
-			},
-		},
-		{
-			name: "oneeachadded",
-			conditions: map[string][]string{
-				"notice":            []string{"bin1"},
-				"reciprocal":        []string{"bin2"},
-				"restricted":        []string{"lib1"},
-				"by_exception_only": []string{"lib2"},
-			},
-			add: map[string][]string{
-				"notice":            []string{"bin1"},
-				"reciprocal":        []string{"bin2"},
-				"restricted":        []string{"lib1"},
-				"by_exception_only": []string{"lib2"},
-			},
-			byName: map[string][]string{
-				"permissive":        []string{},
-				"notice":            []string{"bin1"},
-				"reciprocal":        []string{"bin2"},
-				"restricted":        []string{"lib1"},
-				"by_exception_only": []string{"lib2"},
-			},
-			byOrigin: map[string][]string{
-				"bin1":  []string{"notice"},
-				"bin2":  []string{"reciprocal"},
-				"lib1":  []string{"restricted"},
-				"lib2":  []string{"by_exception_only"},
-				"other": []string{},
-			},
+			expected: []string{"permissive", "notice", "restricted", "restricted_with_classpath_exception", "restricted_allows_dynamic_linking", "proprietary"},
 		},
 	}
 	for _, tt := range tests {
-		testPublicInterface := func(lg *LicenseGraph, cs *LicenseConditionSet, t *testing.T) {
-			byName(tt.byName).checkPublic(cs, t)
-			byOrigin(tt.byOrigin).checkPublic(lg, cs, t)
+		toConditions := func(names []string) []LicenseCondition {
+			result := make([]LicenseCondition, 0, len(names))
+			for _, name := range names {
+				result = append(result, RecognizedConditionNames[name])
+			}
+			return result
 		}
-		t.Run(tt.name+"_public_interface", func(t *testing.T) {
-			lg := newLicenseGraph()
-			cs := NewLicenseConditionSet(toConditionList(lg, tt.conditions)...)
-			if tt.add != nil {
-				cs.Add(toConditionList(lg, tt.add)...)
+		populate := func() LicenseConditionSet {
+			testSet := NewLicenseConditionSet(toConditions(tt.conditions)...)
+			if tt.plus != nil {
+				testSet = testSet.Plus(toConditions(*tt.plus)...)
 			}
-			testPublicInterface(lg, cs, t)
-		})
-
-		t.Run("Copy() of "+tt.name+"_public_interface", func(t *testing.T) {
-			lg := newLicenseGraph()
-			cs := NewLicenseConditionSet(toConditionList(lg, tt.conditions)...)
-			if tt.add != nil {
-				cs.Add(toConditionList(lg, tt.add)...)
+			if tt.minus != nil {
+				testSet = testSet.Minus(toConditions(*tt.minus)...)
 			}
-			testPublicInterface(lg, cs.Copy(), t)
-		})
+			return testSet
+		}
+		populateSet := func() LicenseConditionSet {
+			testSet := NewLicenseConditionSet(toConditions(tt.conditions)...)
+			if tt.plus != nil {
+				testSet = testSet.Union(NewLicenseConditionSet(toConditions(*tt.plus)...))
+			}
+			if tt.minus != nil {
+				testSet = testSet.Difference(NewLicenseConditionSet(toConditions(*tt.minus)...))
+			}
+			return testSet
+		}
+		populatePlusSet := func() LicenseConditionSet {
+			testSet := NewLicenseConditionSet(toConditions(tt.conditions)...)
+			if tt.plus != nil {
+				testSet = testSet.Union(NewLicenseConditionSet(toConditions(*tt.plus)...))
+			}
+			if tt.minus != nil {
+				testSet = testSet.Minus(toConditions(*tt.minus)...)
+			}
+			return testSet
+		}
+		populateMinusSet := func() LicenseConditionSet {
+			testSet := NewLicenseConditionSet(toConditions(tt.conditions)...)
+			if tt.plus != nil {
+				testSet = testSet.Plus(toConditions(*tt.plus)...)
+			}
+			if tt.minus != nil {
+				testSet = testSet.Difference(NewLicenseConditionSet(toConditions(*tt.minus)...))
+			}
+			return testSet
+		}
+		checkMatching := func(cs LicenseConditionSet, t *testing.T) {
+			for data, expectedNames := range tt.matchingAny {
+				expectedConditions := toConditions(expectedNames)
+				expected := NewLicenseConditionSet(expectedConditions...)
+				actual := cs.MatchingAny(toConditions(strings.Split(data, "|"))...)
+				actualNames := actual.Names()
 
-		testPrivateInterface := func(lg *LicenseGraph, cs *LicenseConditionSet, t *testing.T) {
-			slist := make([]string, 0, cs.Count())
-			for origin, expected := range tt.byOrigin {
-				for _, name := range expected {
-					slist = append(slist, origin+";"+name)
+				t.Logf("MatchingAny(%s): actual set %04x %s", data, actual, actual.String())
+				t.Logf("MatchingAny(%s): expected set %04x %s", data, expected, expected.String())
+
+				if actual != expected {
+					t.Errorf("MatchingAny(%s): got %04x, want %04x", data, actual, expected)
+					continue
+				}
+				if len(actualNames) != len(expectedNames) {
+					t.Errorf("len(MatchinAny(%s).Names()): got %d, want %d",
+						data, len(actualNames), len(expectedNames))
+				} else {
+					for i := 0; i < len(actualNames); i++ {
+						if actualNames[i] != expectedNames[i] {
+							t.Errorf("MatchingAny(%s).Names()[%d]: got %s, want %s",
+								data, i, actualNames[i], expectedNames[i])
+							break
+						}
+					}
+				}
+				actualConditions := actual.AsList()
+				if len(actualConditions) != len(expectedConditions) {
+					t.Errorf("len(MatchingAny(%d).AsList()):  got %d, want %d",
+						data, len(actualNames), len(expectedNames))
+				} else {
+					for i := 0; i < len(actualNames); i++ {
+						if actualNames[i] != expectedNames[i] {
+							t.Errorf("MatchingAny(%s).AsList()[%d]: got %s, want %s",
+								data, i, actualNames[i], expectedNames[i])
+							break
+						}
+					}
 				}
 			}
-			actualSlist := cs.asStringList(";")
-			if len(slist) != len(actualSlist) {
-				t.Errorf("unexpected LicenseConditionSet.asStringList(\";\"): got %v, want %v", actualSlist, slist)
+		}
+		checkMatchingSet := func(cs LicenseConditionSet, t *testing.T) {
+			for data, expectedNames := range tt.matchingAny {
+				expected := NewLicenseConditionSet(toConditions(expectedNames)...)
+				actual := cs.MatchingAnySet(NewLicenseConditionSet(toConditions(strings.Split(data, "|"))...))
+				actualNames := actual.Names()
+
+				t.Logf("MatchingAnySet(%s): actual set %04x %s", data, actual, actual.String())
+				t.Logf("MatchingAnySet(%s): expected set %04x %s", data, expected, expected.String())
+
+				if actual != expected {
+					t.Errorf("MatchingAnySet(%s): got %04x, want %04x", data, actual, expected)
+					continue
+				}
+				if len(actualNames) != len(expectedNames) {
+					t.Errorf("len(MatchingAnySet(%s).Names()): got %d, want %d",
+						data, len(actualNames), len(expectedNames))
+				} else {
+					for i := 0; i < len(actualNames); i++ {
+						if actualNames[i] != expectedNames[i] {
+							t.Errorf("MatchingAnySet(%s).Names()[%d]: got %s, want %s",
+								data, i, actualNames[i], expectedNames[i])
+							break
+						}
+					}
+				}
+				expectedConditions := toConditions(expectedNames)
+				actualConditions := actual.AsList()
+				if len(actualConditions) != len(expectedConditions) {
+					t.Errorf("len(MatchingAnySet(%s).AsList()): got %d, want %d",
+						data, len(actualNames), len(expectedNames))
+				} else {
+					for i := 0; i < len(actualNames); i++ {
+						if actualNames[i] != expectedNames[i] {
+							t.Errorf("MatchingAnySet(%s).AsList()[%d]: got %s, want %s",
+								data, i, actualNames[i], expectedNames[i])
+							break
+						}
+					}
+				}
+			}
+		}
+
+		checkExpected := func(actual LicenseConditionSet, t *testing.T) bool {
+			t.Logf("checkExpected{%s}", strings.Join(tt.expected, ", "))
+
+			expectedConditions := toConditions(tt.expected)
+			expected := NewLicenseConditionSet(expectedConditions...)
+
+			actualNames := actual.Names()
+
+			t.Logf("actual license condition set: %04x %s", actual, actual.String())
+			t.Logf("expected license condition set: %04x %s", expected, expected.String())
+
+			if actual != expected {
+				t.Errorf("checkExpected: got %04x, want %04x", actual, expected)
+				return false
+			}
+
+			if len(actualNames) != len(tt.expected) {
+				t.Errorf("len(actual.Names()): got %d, want %d", len(actualNames), len(tt.expected))
 			} else {
-				sort.Strings(slist)
-				sort.Strings(actualSlist)
-				for i := 0; i < len(slist); i++ {
-					if slist[i] != actualSlist[i] {
-						t.Errorf("unexpected LicenseConditionSet.asStringList(\";\") index %d in %v: got %s, want %s", i, actualSlist, actualSlist[i], slist[i])
+				for i := 0; i < len(actualNames); i++ {
+					if actualNames[i] != tt.expected[i] {
+						t.Errorf("actual.Names()[%d]: got %s, want %s", i, actualNames[i], tt.expected[i])
+						break
 					}
 				}
 			}
+
+			actualConditions := actual.AsList()
+			if len(actualConditions) != len(expectedConditions) {
+				t.Errorf("len(actual.AsList()): got %d, want %d", len(actualConditions), len(expectedConditions))
+			} else {
+				for i := 0; i < len(actualConditions); i++ {
+					if actualConditions[i] != expectedConditions[i] {
+						t.Errorf("actual.AsList()[%d]: got %s, want %s",
+							i, actualConditions[i], expectedConditions[i])
+						break
+					}
+				}
+			}
+
+			if len(tt.expected) == 0 {
+				if !actual.IsEmpty() {
+					t.Errorf("actual.IsEmpty(): got false, want true")
+				}
+				if actual.HasAny(expectedConditions...) {
+					t.Errorf("actual.HasAny(): got true, want false")
+				}
+			} else {
+				if actual.IsEmpty() {
+					t.Errorf("actual.IsEmpty(): got true, want false")
+				}
+				if !actual.HasAny(expectedConditions...) {
+					t.Errorf("actual.HasAny(all expected): got false, want true")
+				}
+			}
+			if !actual.HasAll(expectedConditions...) {
+				t.Errorf("actual.Hasll(all expected): want true, got false")
+			}
+			for _, expectedCondition := range expectedConditions {
+				if !actual.HasAny(expectedCondition) {
+					t.Errorf("actual.HasAny(%q): got false, want true", expectedCondition.Name())
+				}
+				if !actual.HasAll(expectedCondition) {
+					t.Errorf("actual.HasAll(%q): got false, want true", expectedCondition.Name())
+				}
+			}
+
+			notExpected := (AllLicenseConditions &^ expected)
+			notExpectedList := notExpected.AsList()
+			t.Logf("not expected license condition set: %04x %s", notExpected, notExpected.String())
+
+			if len(tt.expected) == 0 {
+				if actual.HasAny(append(expectedConditions, notExpectedList...)...) {
+					t.Errorf("actual.HasAny(all conditions): want false, got true")
+				}
+			} else {
+				if !actual.HasAny(append(expectedConditions, notExpectedList...)...) {
+					t.Errorf("actual.HasAny(all conditions): want true, got false")
+				}
+			}
+			if len(notExpectedList) == 0 {
+				if !actual.HasAll(append(expectedConditions, notExpectedList...)...) {
+					t.Errorf("actual.HasAll(all conditions): want true, got false")
+				}
+			} else {
+				if actual.HasAll(append(expectedConditions, notExpectedList...)...) {
+					t.Errorf("actual.HasAll(all conditions): want false, got true")
+				}
+			}
+			for _, unexpectedCondition := range notExpectedList {
+				if actual.HasAny(unexpectedCondition) {
+					t.Errorf("actual.HasAny(%q): got true, want false", unexpectedCondition.Name())
+				}
+				if actual.HasAll(unexpectedCondition) {
+					t.Errorf("actual.HasAll(%q): got true, want false", unexpectedCondition.Name())
+				}
+			}
+			return true
 		}
 
-		t.Run(tt.name+"_private_list_interface", func(t *testing.T) {
-			lg := newLicenseGraph()
-			cs := newLicenseConditionSet()
-			for name, origins := range tt.conditions {
-				for _, origin := range origins {
-					cs.add(newTestNode(lg, origin), name)
-				}
-			}
-			if tt.add != nil {
-				cs.Add(toConditionList(lg, tt.add)...)
-			}
-			testPrivateInterface(lg, cs, t)
-		})
+		checkExpectedSet := func(actual LicenseConditionSet, t *testing.T) bool {
+			t.Logf("checkExpectedSet{%s}", strings.Join(tt.expected, ", "))
 
-		t.Run(tt.name+"_private_set_interface", func(t *testing.T) {
-			lg := newLicenseGraph()
-			cs := newLicenseConditionSet()
-			for name, origins := range tt.conditions {
-				for _, origin := range origins {
-					cs.add(newTestNode(lg, origin), name)
-				}
+			expectedConditions := toConditions(tt.expected)
+			expected := NewLicenseConditionSet(expectedConditions...)
+
+			actualNames := actual.Names()
+
+			t.Logf("actual license condition set: %04x %s", actual, actual.String())
+			t.Logf("expected license condition set: %04x %s", expected, expected.String())
+
+			if actual != expected {
+				t.Errorf("checkExpectedSet: got %04x, want %04x", actual, expected)
+				return false
 			}
-			if tt.add != nil {
-				other := newLicenseConditionSet()
-				for name, origins := range tt.add {
-					for _, origin := range origins {
-						other.add(newTestNode(lg, origin), name)
+
+			if len(actualNames) != len(tt.expected) {
+				t.Errorf("len(actual.Names()): got %d, want %d", len(actualNames), len(tt.expected))
+			} else {
+				for i := 0; i < len(actualNames); i++ {
+					if actualNames[i] != tt.expected[i] {
+						t.Errorf("actual.Names()[%d]: got %s, want %s", i, actualNames[i], tt.expected[i])
+						break
 					}
 				}
-				cs.AddSet(other)
 			}
-			testPrivateInterface(lg, cs, t)
-		})
-	}
-}
 
-func TestConditionSet_Removals(t *testing.T) {
-	tests := []struct {
-		name         string
-		conditions   map[string][]string
-		removeByName []ConditionNames
-		removeSet    map[string][]string
-		byName       map[string][]string
-		byOrigin     map[string][]string
-	}{
-		{
-			name:         "emptybyname",
-			conditions:   map[string][]string{},
-			removeByName: []ConditionNames{{"reciprocal", "restricted"}},
-			byName: map[string][]string{
-				"notice":     []string{},
-				"restricted": []string{},
-			},
-			byOrigin: map[string][]string{
-				"bin1": []string{},
-				"lib1": []string{},
-				"bin2": []string{},
-				"lib2": []string{},
-			},
-		},
-		{
-			name:       "emptybyset",
-			conditions: map[string][]string{},
-			removeSet: map[string][]string{
-				"notice":     []string{"bin1", "bin2"},
-				"restricted": []string{"lib1", "lib2"},
-			},
-			byName: map[string][]string{
-				"notice":     []string{},
-				"restricted": []string{},
-			},
-			byOrigin: map[string][]string{
-				"bin1": []string{},
-				"lib1": []string{},
-				"bin2": []string{},
-				"lib2": []string{},
-			},
-		},
-		{
-			name: "everythingremovenone",
-			conditions: map[string][]string{
-				"notice":            []string{"bin1", "bin2", "lib1", "lib2"},
-				"reciprocal":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"restricted":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"by_exception_only": []string{"bin1", "bin2", "lib1", "lib2"},
-			},
-			removeByName: []ConditionNames{{"permissive", "unencumbered"}},
-			removeSet: map[string][]string{
-				"notice": []string{"apk1"},
-			},
-			byName: map[string][]string{
-				"permissive":        []string{},
-				"notice":            []string{"bin1", "bin2", "lib1", "lib2"},
-				"reciprocal":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"restricted":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"by_exception_only": []string{"bin1", "bin2", "lib1", "lib2"},
-			},
-			byOrigin: map[string][]string{
-				"bin1":  []string{"notice", "reciprocal", "restricted", "by_exception_only"},
-				"bin2":  []string{"notice", "reciprocal", "restricted", "by_exception_only"},
-				"lib1":  []string{"notice", "reciprocal", "restricted", "by_exception_only"},
-				"lib2":  []string{"notice", "reciprocal", "restricted", "by_exception_only"},
-				"other": []string{},
-			},
-		},
-		{
-			name: "everythingremovesome",
-			conditions: map[string][]string{
-				"notice":            []string{"bin1", "bin2", "lib1", "lib2"},
-				"reciprocal":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"restricted":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"by_exception_only": []string{"bin1", "bin2", "lib1", "lib2"},
-			},
-			removeByName: []ConditionNames{{"restricted", "by_exception_only"}},
-			removeSet: map[string][]string{
-				"notice": []string{"lib1"},
-			},
-			byName: map[string][]string{
-				"permissive":        []string{},
-				"notice":            []string{"bin1", "bin2", "lib2"},
-				"reciprocal":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"restricted":        []string{},
-				"by_exception_only": []string{},
-			},
-			byOrigin: map[string][]string{
-				"bin1":  []string{"notice", "reciprocal"},
-				"bin2":  []string{"notice", "reciprocal"},
-				"lib1":  []string{"reciprocal"},
-				"lib2":  []string{"notice", "reciprocal"},
-				"other": []string{},
-			},
-		},
-		{
-			name: "everythingremoveall",
-			conditions: map[string][]string{
-				"notice":            []string{"bin1", "bin2", "lib1", "lib2"},
-				"reciprocal":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"restricted":        []string{"bin1", "bin2", "lib1", "lib2"},
-				"by_exception_only": []string{"bin1", "bin2", "lib1", "lib2"},
-			},
-			removeByName: []ConditionNames{{"restricted", "by_exception_only"}},
-			removeSet: map[string][]string{
-				"notice":     []string{"bin1", "bin2", "lib1", "lib2"},
-				"reciprocal": []string{"bin1", "bin2", "lib1", "lib2"},
-				"restricted": []string{"bin1"},
-			},
-			byName: map[string][]string{
-				"permissive":        []string{},
-				"notice":            []string{},
-				"reciprocal":        []string{},
-				"restricted":        []string{},
-				"by_exception_only": []string{},
-			},
-			byOrigin: map[string][]string{
-				"bin1":  []string{},
-				"bin2":  []string{},
-				"lib1":  []string{},
-				"lib2":  []string{},
-				"other": []string{},
-			},
-		},
-	}
-	for _, tt := range tests {
+			actualConditions := actual.AsList()
+			if len(actualConditions) != len(expectedConditions) {
+				t.Errorf("len(actual.AsList()): got %d, want %d", len(actualConditions), len(expectedConditions))
+			} else {
+				for i := 0; i < len(actualConditions); i++ {
+					if actualConditions[i] != expectedConditions[i] {
+						t.Errorf("actual.AsList()[%d}: got %s, want %s",
+							i, actualConditions[i], expectedConditions[i])
+						break
+					}
+				}
+			}
+
+			if len(tt.expected) == 0 {
+				if !actual.IsEmpty() {
+					t.Errorf("actual.IsEmpty(): got false, want true")
+				}
+				if actual.MatchesAnySet(expected) {
+					t.Errorf("actual.MatchesAnySet({}): got true, want false")
+				}
+				if actual.MatchesEverySet(expected, expected) {
+					t.Errorf("actual.MatchesEverySet({}, {}): want false, got true")
+				}
+			} else {
+				if actual.IsEmpty() {
+					t.Errorf("actual.IsEmpty(): got true, want false")
+				}
+				if !actual.MatchesAnySet(expected) {
+					t.Errorf("actual.MatchesAnySet({all expected}): want true, got false")
+				}
+				if !actual.MatchesEverySet(expected, expected) {
+					t.Errorf("actual.MatchesEverySet({all expected}, {all expected}): want true, got false")
+				}
+			}
+
+			notExpected := (AllLicenseConditions &^ expected)
+			t.Logf("not expected license condition set: %04x %s", notExpected, notExpected.String())
+
+			if len(tt.expected) == 0 {
+				if actual.MatchesAnySet(expected, notExpected) {
+					t.Errorf("empty actual.MatchesAnySet({expected}, {not expected}): want false, got true")
+				}
+			} else {
+				if !actual.MatchesAnySet(expected, notExpected) {
+					t.Errorf("actual.MatchesAnySet({expected}, {not expected}): want true, got false")
+				}
+			}
+			if actual.MatchesAnySet(notExpected) {
+				t.Errorf("actual.MatchesAnySet({not expected}): want false, got true")
+			}
+			if actual.MatchesEverySet(notExpected) {
+				t.Errorf("actual.MatchesEverySet({not expected}): want false, got true")
+			}
+			if actual.MatchesEverySet(expected, notExpected) {
+				t.Errorf("actual.MatchesEverySet({expected}, {not expected}): want false, got true")
+			}
+
+			if !actual.Difference(expected).IsEmpty() {
+				t.Errorf("actual.Difference({expected}).IsEmpty(): want true, got false")
+			}
+			if expected != actual.Intersection(expected) {
+				t.Errorf("expected == actual.Intersection({expected}): want true, got false (%04x != %04x)", expected, actual.Intersection(expected))
+			}
+			if actual != actual.Intersection(expected) {
+				t.Errorf("actual == actual.Intersection({expected}): want true, got false (%04x != %04x)", actual, actual.Intersection(expected))
+			}
+			return true
+		}
+
 		t.Run(tt.name, func(t *testing.T) {
-			lg := newLicenseGraph()
-			cs := newLicenseConditionSet()
-			for name, origins := range tt.conditions {
-				for _, origin := range origins {
-					cs.add(newTestNode(lg, origin), name)
-				}
+			cs := populate()
+			if checkExpected(cs, t) {
+				checkMatching(cs, t)
 			}
-			if tt.removeByName != nil {
-				cs.RemoveAllByName(tt.removeByName...)
+			if checkExpectedSet(cs, t) {
+				checkMatchingSet(cs, t)
 			}
-			if tt.removeSet != nil {
-				other := newLicenseConditionSet()
-				for name, origins := range tt.removeSet {
-					for _, origin := range origins {
-						other.add(newTestNode(lg, origin), name)
-					}
-				}
-				cs.RemoveSet(other)
+		})
+
+		t.Run(tt.name+"_sets", func(t *testing.T) {
+			cs := populateSet()
+			if checkExpected(cs, t) {
+				checkMatching(cs, t)
 			}
-			byName(tt.byName).checkPublic(cs, t)
-			byOrigin(tt.byOrigin).checkPublic(lg, cs, t)
+			if checkExpectedSet(cs, t){
+				checkMatchingSet(cs, t)
+			}
+		})
+
+		t.Run(tt.name+"_plusset", func(t *testing.T) {
+			cs := populatePlusSet()
+			if checkExpected(cs, t) {
+				checkMatching(cs, t)
+			}
+			if checkExpectedSet(cs, t){
+				checkMatchingSet(cs, t)
+			}
+		})
+
+		t.Run(tt.name+"_minusset", func(t *testing.T) {
+			cs := populateMinusSet()
+			if checkExpected(cs, t) {
+				checkMatching(cs, t)
+			}
+			if checkExpectedSet(cs, t){
+				checkMatchingSet(cs, t)
+			}
 		})
 	}
 }
diff --git a/tools/compliance/graph.go b/tools/compliance/graph.go
index 9dcfa66..97fa657 100644
--- a/tools/compliance/graph.go
+++ b/tools/compliance/graph.go
@@ -52,30 +52,19 @@
 	// edges lists the directed edges in the graph from target to dependency. (guarded by mu)
 	//
 	// Alternatively, the graph is the set of `edges`.
-	edges []*dependencyEdge
+	edges TargetEdgeList
 
-	// targets identifies, indexes by name, and describes the entire set of target node files.
+	// targets identifies, indexes, and describes the entire set of target node files.
 	/// (guarded by mu)
 	targets map[string]*TargetNode
 
-	// index facilitates looking up edges from targets. (creation guarded by my)
-	//
-	// This is a forward index from target to dependencies. i.e. "top-down"
-	index map[string][]*dependencyEdge
+	// wgBU becomes non-nil when the bottom-up resolve begins and reaches 0
+	// (i.e. Wait() proceeds) when the bottom-up resolve completes. (guarded by mu)
+	wgBU *sync.WaitGroup
 
-	// rsBU caches the results of a full bottom-up resolve. (creation guarded by mu)
-	//
-	// A bottom-up resolve is a prerequisite for all of the top-down resolves so caching
-	// the result is a performance win.
-	rsBU *ResolutionSet
-
-	// rsTD caches the results of a full top-down resolve. (creation guarded by mu)
-	//
-	// A top-down resolve is a prerequisite for final resolutions.
-	// e.g. a shipped node inheriting a `restricted` condition from a parent through a
-	// dynamic dependency implies a notice dependency on the parent; even though, the
-	// distribution does not happen as a result of the dynamic dependency itself.
-	rsTD *ResolutionSet
+	// wgTD becomes non-nil when the top-down resolve begins and reaches 0 (i.e. Wait()
+	// proceeds) when the top-down resolve completes. (guarded by mu)
+	wgTD *sync.WaitGroup
 
 	// shippedNodes caches the results of a full walk of nodes identifying targets
 	// distributed either directly or as derivative works. (creation guarded by mu)
@@ -85,35 +74,18 @@
 	mu sync.Mutex
 }
 
-// TargetNode returns the target node identified by `name`.
-func (lg *LicenseGraph) TargetNode(name string) *TargetNode {
-	if _, ok := lg.targets[name]; !ok {
-		panic(fmt.Errorf("target node %q missing from graph", name))
-	}
-	return lg.targets[name]
-}
-
-// HasTargetNode returns true if a target node identified by `name` appears in
-// the graph.
-func (lg *LicenseGraph) HasTargetNode(name string) bool {
-	_, isPresent := lg.targets[name]
-	return isPresent
-}
-
 // Edges returns the list of edges in the graph. (unordered)
 func (lg *LicenseGraph) Edges() TargetEdgeList {
 	edges := make(TargetEdgeList, 0, len(lg.edges))
-	for _, e := range lg.edges {
-		edges = append(edges, TargetEdge{lg, e})
-	}
+	edges = append(edges, lg.edges...)
 	return edges
 }
 
 // Targets returns the list of target nodes in the graph. (unordered)
 func (lg *LicenseGraph) Targets() TargetNodeList {
 	targets := make(TargetNodeList, 0, len(lg.targets))
-	for target := range lg.targets {
-		targets = append(targets, lg.targets[target])
+	for _, target := range lg.targets {
+		targets = append(targets, target)
 	}
 	return targets
 }
@@ -124,33 +96,10 @@
 func newLicenseGraph() *LicenseGraph {
 	return &LicenseGraph{
 		rootFiles: []string{},
-		edges:     make([]*dependencyEdge, 0, 1000),
 		targets:   make(map[string]*TargetNode),
 	}
 }
 
-// indexForward guarantees the `index` map is populated to look up edges by
-// `target`.
-func (lg *LicenseGraph) indexForward() {
-	lg.mu.Lock()
-	defer func() {
-		lg.mu.Unlock()
-	}()
-
-	if lg.index != nil {
-		return
-	}
-
-	lg.index = make(map[string][]*dependencyEdge)
-	for _, e := range lg.edges {
-		if _, ok := lg.index[e.target]; ok {
-			lg.index[e.target] = append(lg.index[e.target], e)
-		} else {
-			lg.index[e.target] = []*dependencyEdge{e}
-		}
-	}
-}
-
 // TargetEdge describes a directed, annotated edge from a target to a
 // dependency. (immutable)
 //
@@ -159,25 +108,25 @@
 // i.e. `Target` depends on `Dependency` in the manner described by
 // `Annotations`.
 type TargetEdge struct {
-	// lg identifies the scope, i.e. license graph, in which the edge appears.
-	lg *LicenseGraph
+	// target and dependency identify the nodes connected by the edge.
+	target, dependency *TargetNode
 
-	// e identifies describes the target, dependency, and annotations of the edge.
-	e *dependencyEdge
+	// annotations identifies the set of compliance-relevant annotations describing the edge.
+	annotations TargetEdgeAnnotations
 }
 
 // Target identifies the target that depends on the dependency.
 //
 // Target needs Dependency to build.
-func (e TargetEdge) Target() *TargetNode {
-	return e.lg.targets[e.e.target]
+func (e *TargetEdge) Target() *TargetNode {
+	return e.target
 }
 
 // Dependency identifies the target depended on by the target.
 //
 // Dependency builds without Target, but Target needs Dependency to build.
-func (e TargetEdge) Dependency() *TargetNode {
-	return e.lg.targets[e.e.dependency]
+func (e *TargetEdge) Dependency() *TargetNode {
+	return e.dependency
 }
 
 // Annotations describes the type of edge by the set of annotations attached to
@@ -186,12 +135,17 @@
 // Only annotations prescribed by policy have any meaning for licensing, and
 // the meaning for licensing is likewise prescribed by policy. Other annotations
 // are preserved and ignored by policy.
-func (e TargetEdge) Annotations() TargetEdgeAnnotations {
-	return e.e.annotations
+func (e *TargetEdge) Annotations() TargetEdgeAnnotations {
+	return e.annotations
+}
+
+// String returns a human-readable string representation of the edge.
+func (e *TargetEdge) String() string {
+	return fmt.Sprintf("%s -[%s]> %s", e.target.name, strings.Join(e.annotations.AsList(), ", "), e.dependency.name)
 }
 
 // TargetEdgeList orders lists of edges by target then dependency then annotations.
-type TargetEdgeList []TargetEdge
+type TargetEdgeList []*TargetEdge
 
 // Len returns the count of the elmements in the list.
 func (l TargetEdgeList) Len() int      { return len(l) }
@@ -201,18 +155,63 @@
 
 // Less returns true when the `i`th element is lexicographically less than the `j`th.
 func (l TargetEdgeList) Less(i, j int) bool {
-	if l[i].e.target == l[j].e.target {
-		if l[i].e.dependency == l[j].e.dependency {
-			return l[i].e.annotations.Compare(l[j].e.annotations) < 0
-		}
-		return l[i].e.dependency < l[j].e.dependency
+	namei := l[i].target.name
+	namej := l[j].target.name
+	if namei == namej {
+		namei = l[i].dependency.name
+		namej = l[j].dependency.name
 	}
-	return l[i].e.target < l[j].e.target
+	if namei == namej {
+		return l[i].annotations.Compare(l[j].annotations) < 0
+	}
+	return namei < namej
+}
+
+// TargetEdgePathSegment describes a single arc in a TargetPath associating the
+// edge with a context `ctx` defined by whatever process is creating the path.
+type TargetEdgePathSegment struct {
+	edge *TargetEdge
+	ctx interface{}
+}
+
+// Target identifies the target that depends on the dependency.
+//
+// Target needs Dependency to build.
+func (s TargetEdgePathSegment) Target() *TargetNode {
+	return s.edge.target
+}
+
+// Dependency identifies the target depended on by the target.
+//
+// Dependency builds without Target, but Target needs Dependency to build.
+func (s TargetEdgePathSegment) Dependency() *TargetNode {
+	return s.edge.dependency
+}
+
+// Annotations describes the type of edge by the set of annotations attached to
+// it.
+//
+// Only annotations prescribed by policy have any meaning for licensing, and
+// the meaning for licensing is likewise prescribed by policy. Other annotations
+// are preserved and ignored by policy.
+func (s TargetEdgePathSegment) Annotations() TargetEdgeAnnotations {
+	return s.edge.annotations
+}
+
+// Context returns the context associated with the path segment. The type and
+// value of the context defined by the process creating the path.
+func (s TargetEdgePathSegment) Context() interface{} {
+	return s.ctx
+}
+
+// String returns a human-readable string representation of the edge.
+func (s TargetEdgePathSegment) String() string {
+	return fmt.Sprintf("%s -[%s]> %s", s.edge.target.name, strings.Join(s.edge.annotations.AsList(), ", "), s.edge.dependency.name)
 }
 
 // TargetEdgePath describes a sequence of edges starting at a root and ending
 // at some final dependency.
-type TargetEdgePath []TargetEdge
+type TargetEdgePath []TargetEdgePathSegment
 
 // NewTargetEdgePath creates a new, empty path with capacity `cap`.
 func NewTargetEdgePath(cap int) *TargetEdgePath {
@@ -222,15 +221,15 @@
 
 // Push appends a new edge to the list verifying that the target of the new
 // edge is the dependency of the prior.
-func (p *TargetEdgePath) Push(edge TargetEdge) {
+func (p *TargetEdgePath) Push(edge *TargetEdge, ctx interface{}) {
 	if len(*p) == 0 {
-		*p = append(*p, edge)
+		*p = append(*p, TargetEdgePathSegment{edge, ctx})
 		return
 	}
-	if (*p)[len(*p)-1].e.dependency != edge.e.target {
-		panic(fmt.Errorf("disjoint path %s does not end at %s", p.String(), edge.e.target))
+	if (*p)[len(*p)-1].edge.dependency != edge.target {
+		panic(fmt.Errorf("disjoint path %s does not end at %s", p.String(), edge.target.name))
 	}
-	*p = append(*p, edge)
+	*p = append(*p, TargetEdgePathSegment{edge, ctx})
 }
 
 // Pop shortens the path by 1 edge.
@@ -256,10 +255,11 @@
 	}
 	var sb strings.Builder
 	fmt.Fprintf(&sb, "[")
-	for _, e := range *p {
-		fmt.Fprintf(&sb, "%s -> ", e.e.target)
+	for _, s := range *p {
+		fmt.Fprintf(&sb, "%s -> ", s.edge.target.name)
 	}
-	fmt.Fprintf(&sb, "%s]", (*p)[len(*p)-1].e.dependency)
+	lastSegment := (*p)[len(*p)-1]
+	fmt.Fprintf(&sb, "%s]", lastSegment.edge.dependency.name)
 	return sb.String()
 }
 
@@ -279,6 +279,13 @@
 	return tn.name
 }
 
+// Dependencies returns the list of edges to dependencies of `tn`.
+func (tn *TargetNode) Dependencies() TargetEdgeList {
+	edges := make(TargetEdgeList, 0, len(tn.edges))
+	edges = append(edges, tn.edges...)
+	return edges
+}
+
 // PackageName returns the string that identifes the package for the target.
 func (tn *TargetNode) PackageName() string {
 	return tn.proto.GetPackageName()
@@ -323,10 +330,8 @@
 // is a matter of policy. (unordered)
 //
 // e.g. notice or proprietary
-func (tn *TargetNode) LicenseConditions() *LicenseConditionSet {
-	result := newLicenseConditionSet()
-	result.add(tn, tn.proto.LicenseConditions...)
-	return result
+func (tn *TargetNode) LicenseConditions() LicenseConditionSet {
+	return tn.licenseConditions
 }
 
 // LicenseTexts returns the paths to the files containing the license texts for
@@ -387,12 +392,12 @@
 // Annotations typically distinguish between static linkage versus dynamic
 // versus tools that are used at build time but are not linked in any way.
 type TargetEdgeAnnotations struct {
-	annotations map[string]bool
+	annotations map[string]struct{}
 }
 
 // newEdgeAnnotations creates a new instance of TargetEdgeAnnotations.
 func newEdgeAnnotations() TargetEdgeAnnotations {
-	return TargetEdgeAnnotations{make(map[string]bool)}
+	return TargetEdgeAnnotations{make(map[string]struct{})}
 }
 
 // HasAnnotation returns true if an annotation `ann` is in the set.
@@ -439,7 +444,7 @@
 
 // TargetNodeSet describes a set of distinct nodes in a license graph.
 type TargetNodeSet struct {
-	nodes map[*TargetNode]bool
+	nodes map[*TargetNode]struct{}
 }
 
 // Contains returns true when `target` is an element of the set.
@@ -466,6 +471,11 @@
 	return result
 }
 
+// String returns a human-readable string representation of the set.
+func (ts *TargetNodeSet) String() string {
+	return fmt.Sprintf("{%s}", strings.Join(ts.Names(), ", "))
+}
+
 // TargetNodeList orders a list of targets by name.
 type TargetNodeList []*TargetNode
 
diff --git a/tools/compliance/policy/policy.go b/tools/compliance/policy/policy.go
index 9dab05b..581912a 100644
--- a/tools/compliance/policy/policy.go
+++ b/tools/compliance/policy/policy.go
@@ -20,32 +20,43 @@
 )
 
 var (
+	// RecognizedAnnotations identifies the set of annotations that have
+	// meaning for compliance policy.
+	RecognizedAnnotations = map[string]string{
+		// used in readgraph.go to avoid creating 1000's of copies of the below 3 strings.
+		"static":    "static",
+		"dynamic":   "dynamic",
+		"toolchain": "toolchain",
+	}
+
 	// ImpliesUnencumbered lists the condition names representing an author attempt to disclaim copyright.
-	ImpliesUnencumbered = ConditionNames{"unencumbered"}
+	ImpliesUnencumbered = LicenseConditionSet(UnencumberedCondition)
 
 	// ImpliesPermissive lists the condition names representing copyrighted but "licensed without policy requirements".
-	ImpliesPermissive = ConditionNames{"permissive"}
+	ImpliesPermissive = LicenseConditionSet(PermissiveCondition)
 
 	// ImpliesNotice lists the condition names implying a notice or attribution policy.
-	ImpliesNotice = ConditionNames{"unencumbered", "permissive", "notice", "reciprocal", "restricted", "proprietary", "by_exception_only"}
+	ImpliesNotice = LicenseConditionSet(UnencumberedCondition | PermissiveCondition | NoticeCondition | ReciprocalCondition |
+			RestrictedCondition | RestrictedClasspathExceptionCondition | WeaklyRestrictedCondition |
+			ProprietaryCondition | ByExceptionOnlyCondition)
 
 	// ImpliesReciprocal lists the condition names implying a local source-sharing policy.
-	ImpliesReciprocal = ConditionNames{"reciprocal"}
+	ImpliesReciprocal = LicenseConditionSet(ReciprocalCondition)
 
 	// Restricted lists the condition names implying an infectious source-sharing policy.
-	ImpliesRestricted = ConditionNames{"restricted"}
+	ImpliesRestricted = LicenseConditionSet(RestrictedCondition | RestrictedClasspathExceptionCondition | WeaklyRestrictedCondition)
 
 	// ImpliesProprietary lists the condition names implying a confidentiality policy.
-	ImpliesProprietary = ConditionNames{"proprietary"}
+	ImpliesProprietary = LicenseConditionSet(ProprietaryCondition)
 
 	// ImpliesByExceptionOnly lists the condition names implying a policy for "license review and approval before use".
-	ImpliesByExceptionOnly = ConditionNames{"proprietary", "by_exception_only"}
+	ImpliesByExceptionOnly = LicenseConditionSet(ProprietaryCondition | ByExceptionOnlyCondition)
 
 	// ImpliesPrivate lists the condition names implying a source-code privacy policy.
-	ImpliesPrivate = ConditionNames{"proprietary"}
+	ImpliesPrivate = LicenseConditionSet(ProprietaryCondition)
 
 	// ImpliesShared lists the condition names implying a source-code sharing policy.
-	ImpliesShared = ConditionNames{"reciprocal", "restricted"}
+	ImpliesShared = LicenseConditionSet(ReciprocalCondition | RestrictedCondition | RestrictedClasspathExceptionCondition | WeaklyRestrictedCondition)
 )
 
 var (
@@ -55,100 +66,117 @@
 	ccBySa       = regexp.MustCompile(`^SPDX-license-identifier-CC-BY.*-SA.*`)
 )
 
-// Resolution happens in two passes:
+
+// LicenseConditionSetFromNames returns a set containing the recognized `names` and
+// silently ignoring or discarding the unrecognized `names`.
+func LicenseConditionSetFromNames(tn *TargetNode, names ...string) LicenseConditionSet {
+	cs := NewLicenseConditionSet()
+	for _, name := range names {
+		if name == "restricted" {
+			if 0 == len(tn.LicenseKinds()) {
+				cs = cs.Plus(RestrictedCondition)
+				continue
+			}
+			hasLgpl := false
+			hasClasspath := false
+			hasGeneric := false
+			for _, kind := range tn.LicenseKinds() {
+				if strings.HasSuffix(kind, "-with-classpath-exception") {
+					cs = cs.Plus(RestrictedClasspathExceptionCondition)
+					hasClasspath = true
+				} else if anyLgpl.MatchString(kind) {
+					cs = cs.Plus(WeaklyRestrictedCondition)
+					hasLgpl = true
+				} else if versionedGpl.MatchString(kind) {
+					cs = cs.Plus(RestrictedCondition)
+				} else if genericGpl.MatchString(kind) {
+					hasGeneric = true
+				} else if kind == "legacy_restricted" || ccBySa.MatchString(kind) {
+					cs = cs.Plus(RestrictedCondition)
+				} else {
+					cs = cs.Plus(RestrictedCondition)
+				}
+			}
+			if hasGeneric && !hasLgpl && !hasClasspath {
+				cs = cs.Plus(RestrictedCondition)
+			}
+			continue
+		}
+		if lc, ok := RecognizedConditionNames[name]; ok {
+			cs |= LicenseConditionSet(lc)
+		}
+	}
+	return cs
+}
+
+
+// Resolution happens in three phases:
 //
-// 1. A bottom-up traversal propagates license conditions up to targets from
-// dendencies as needed.
+// 1. A bottom-up traversal propagates (restricted) license conditions up to
+// targets from dendencies as needed.
 //
-// 2. For each condition of interest, a top-down traversal adjusts the attached
-// conditions pushing restricted down from targets into linked dependencies.
+// 2. For each condition of interest, a top-down traversal propagates
+// (restricted) conditions down from targets into linked dependencies.
 //
-// The behavior of the 2 passes gets controlled by the 2 functions below.
+// 3. Finally, a walk of the shipped target nodes attaches resolutions to the
+// ancestor nodes from the root down to and including the first non-container.
 //
-// The first function controls what happens during the bottom-up traversal. In
-// general conditions flow up through static links but not other dependencies;
-// except, restricted sometimes flows up through dynamic links.
+// e.g. If a disk image contains a binary bin1 that links a library liba, the
+// notice requirement for liba gets attached to the disk image and to bin1.
+// Because liba doesn't actually get shipped as a separate artifact, but only
+// as bits in bin1, it has no actions 'attached' to it. The actions attached
+// to the image and to bin1 'act on' liba by providing notice.
 //
-// In general, too, the originating target gets acted on to resolve the
-// condition (e.g. providing notice), but again restricted is special in that
-// it requires acting on (i.e. sharing source of) both the originating module
-// and the target using the module.
+// The behavior of the 3 phases gets controlled by the 3 functions below.
 //
-// The latter function controls what happens during the top-down traversal. In
-// general, only restricted conditions flow down at all, and only through
-// static links.
+// The first function controls what happens during the bottom-up propagation.
+// Restricted conditions propagate up all non-toolchain dependencies; except,
+// some do not propagate up dynamic links, which may depend on whether the
+// modules are independent.
+//
+// The second function controls what happens during the top-down propagation.
+// Restricted conditions propagate down as above with the added caveat that
+// inherited restricted conditions do not propagate from pure aggregates to
+// their dependencies.
+//
+// The final function controls which conditions apply/get attached to ancestors
+// depending on the types of dependencies involved. All conditions apply across
+// normal derivation dependencies. No conditions apply across toolchain
+// dependencies. Some restricted conditions apply across dynamic link
+// dependencies.
 //
 // Not all restricted licenses are create equal. Some have special rules or
 // exceptions. e.g. LGPL or "with classpath excption".
 
-// depActionsApplicableToTarget returns the actions which propagate up an
+
+// depConditionsPropagatingToTarget returns the conditions which propagate up an
 // edge from dependency to target.
 //
-// This function sets the policy for the bottom-up traversal and how conditions
+// This function sets the policy for the bottom-up propagation and how conditions
 // flow up the graph from dependencies to targets.
 //
 // If a pure aggregation is built into a derivative work that is not a pure
 // aggregation, per policy it ceases to be a pure aggregation in the context of
 // that derivative work. The `treatAsAggregate` parameter will be false for
 // non-aggregates and for aggregates in non-aggregate contexts.
-func depActionsApplicableToTarget(e TargetEdge, depActions actionSet, treatAsAggregate bool) actionSet {
-	result := make(actionSet)
+func depConditionsPropagatingToTarget(lg *LicenseGraph, e *TargetEdge, depConditions LicenseConditionSet, treatAsAggregate bool) LicenseConditionSet {
+	result := LicenseConditionSet(0x0000)
 	if edgeIsDerivation(e) {
-		result.addSet(depActions)
-		for _, cs := range depActions.byName(ImpliesRestricted) {
-			result.add(e.Target(), cs)
-		}
+		result |= depConditions & ImpliesRestricted
 		return result
 	}
 	if !edgeIsDynamicLink(e) {
 		return result
 	}
 
-	restricted := depActions.byName(ImpliesRestricted)
-	for actsOn, cs := range restricted {
-		for _, lc := range cs.AsList() {
-			hasGpl := false
-			hasLgpl := false
-			hasClasspath := false
-			hasGeneric := false
-			hasOther := false
-			for _, kind := range lc.origin.LicenseKinds() {
-				if strings.HasSuffix(kind, "-with-classpath-exception") {
-					hasClasspath = true
-				} else if anyLgpl.MatchString(kind) {
-					hasLgpl = true
-				} else if versionedGpl.MatchString(kind) {
-					hasGpl = true
-				} else if genericGpl.MatchString(kind) {
-					hasGeneric = true
-				} else if kind == "legacy_restricted" || ccBySa.MatchString(kind) {
-					hasOther = true
-				}
-			}
-			if hasOther || hasGpl {
-				result.addCondition(actsOn, lc)
-				result.addCondition(e.Target(), lc)
-				continue
-			}
-			if hasClasspath && !edgeNodesAreIndependentModules(e) {
-				result.addCondition(actsOn, lc)
-				result.addCondition(e.Target(), lc)
-				continue
-			}
-			if hasLgpl || hasClasspath {
-				continue
-			}
-			if !hasGeneric {
-				continue
-			}
-			result.addCondition(actsOn, lc)
-			result.addCondition(e.Target(), lc)
-		}
+	result |= depConditions & LicenseConditionSet(RestrictedCondition)
+	if 0 != (depConditions & LicenseConditionSet(RestrictedClasspathExceptionCondition)) && !edgeNodesAreIndependentModules(e) {
+		result |= LicenseConditionSet(RestrictedClasspathExceptionCondition)
 	}
 	return result
 }
 
-// targetConditionsApplicableToDep returns the conditions which propagate down
+// targetConditionsPropagatingToDep returns the conditions which propagate down
 // an edge from target to dependency.
 //
 // This function sets the policy for the top-down traversal and how conditions
@@ -158,81 +186,73 @@
 // aggregation, per policy it ceases to be a pure aggregation in the context of
 // that derivative work. The `treatAsAggregate` parameter will be false for
 // non-aggregates and for aggregates in non-aggregate contexts.
-func targetConditionsApplicableToDep(e TargetEdge, targetConditions *LicenseConditionSet, treatAsAggregate bool) *LicenseConditionSet {
-	result := targetConditions.Copy()
+func targetConditionsPropagatingToDep(lg *LicenseGraph, e *TargetEdge, targetConditions LicenseConditionSet, treatAsAggregate bool) LicenseConditionSet {
+	result := targetConditions
 
 	// reverse direction -- none of these apply to things depended-on, only to targets depending-on.
-	result.RemoveAllByName(ConditionNames{"unencumbered", "permissive", "notice", "reciprocal", "proprietary", "by_exception_only"})
+	result = result.Minus(UnencumberedCondition, PermissiveCondition, NoticeCondition, ReciprocalCondition, ProprietaryCondition, ByExceptionOnlyCondition)
 
 	if !edgeIsDerivation(e) && !edgeIsDynamicLink(e) {
 		// target is not a derivative work of dependency and is not linked to dependency
-		result.RemoveAllByName(ImpliesRestricted)
+		result = result.Difference(ImpliesRestricted)
 		return result
 	}
 	if treatAsAggregate {
 		// If the author of a pure aggregate licenses it restricted, apply restricted to immediate dependencies.
 		// Otherwise, restricted does not propagate back down to dependencies.
-		restricted := result.ByName(ImpliesRestricted).AsList()
-		for _, lc := range restricted {
-			if lc.origin.name != e.e.target {
-				result.Remove(lc)
-			}
+		if !LicenseConditionSetFromNames(e.target, e.target.proto.LicenseConditions...).MatchesAnySet(ImpliesRestricted) {
+			result = result.Difference(ImpliesRestricted)
 		}
 		return result
 	}
 	if edgeIsDerivation(e) {
 		return result
 	}
-	restricted := result.ByName(ImpliesRestricted).AsList()
-	for _, lc := range restricted {
-		hasGpl := false
-		hasLgpl := false
-		hasClasspath := false
-		hasGeneric := false
-		hasOther := false
-		for _, kind := range lc.origin.LicenseKinds() {
-			if strings.HasSuffix(kind, "-with-classpath-exception") {
-				hasClasspath = true
-			} else if anyLgpl.MatchString(kind) {
-				hasLgpl = true
-			} else if versionedGpl.MatchString(kind) {
-				hasGpl = true
-			} else if genericGpl.MatchString(kind) {
-				hasGeneric = true
-			} else if kind == "legacy_restricted" || ccBySa.MatchString(kind) {
-				hasOther = true
-			}
-		}
-		if hasOther || hasGpl {
-			continue
-		}
-		if hasClasspath && !edgeNodesAreIndependentModules(e) {
-			continue
-		}
-		if hasGeneric && !hasLgpl && !hasClasspath {
-			continue
-		}
-		result.Remove(lc)
+	result = result.Minus(WeaklyRestrictedCondition)
+	if edgeNodesAreIndependentModules(e) {
+		result = result.Minus(RestrictedClasspathExceptionCondition)
 	}
 	return result
 }
 
+// conditionsAttachingAcrossEdge returns the subset of conditions in `universe`
+// that apply across edge `e`.
+//
+// This function sets the policy for attaching actions to ancestor nodes in the
+// final resolution walk.
+func conditionsAttachingAcrossEdge(lg *LicenseGraph, e *TargetEdge, universe LicenseConditionSet) LicenseConditionSet {
+	result := universe
+	if edgeIsDerivation(e) {
+		return result
+	}
+	if !edgeIsDynamicLink(e) {
+		return NewLicenseConditionSet()
+	}
+
+	result &= LicenseConditionSet(RestrictedCondition | RestrictedClasspathExceptionCondition)
+	if 0 != (result & LicenseConditionSet(RestrictedClasspathExceptionCondition)) && edgeNodesAreIndependentModules(e) {
+		result &= LicenseConditionSet(RestrictedCondition)
+	}
+	return result
+}
+
+
 // edgeIsDynamicLink returns true for edges representing shared libraries
 // linked dynamically at runtime.
-func edgeIsDynamicLink(e TargetEdge) bool {
-	return e.e.annotations.HasAnnotation("dynamic")
+func edgeIsDynamicLink(e *TargetEdge) bool {
+	return e.annotations.HasAnnotation("dynamic")
 }
 
 // edgeIsDerivation returns true for edges where the target is a derivative
 // work of dependency.
-func edgeIsDerivation(e TargetEdge) bool {
-	isDynamic := e.e.annotations.HasAnnotation("dynamic")
-	isToolchain := e.e.annotations.HasAnnotation("toolchain")
+func edgeIsDerivation(e *TargetEdge) bool {
+	isDynamic := e.annotations.HasAnnotation("dynamic")
+	isToolchain := e.annotations.HasAnnotation("toolchain")
 	return !isDynamic && !isToolchain
 }
 
 // edgeNodesAreIndependentModules returns true for edges where the target and
 // dependency are independent modules.
-func edgeNodesAreIndependentModules(e TargetEdge) bool {
-	return e.Target().PackageName() != e.Dependency().PackageName()
+func edgeNodesAreIndependentModules(e *TargetEdge) bool {
+	return e.target.PackageName() != e.dependency.PackageName()
 }
diff --git a/tools/compliance/policy/policy_test.go b/tools/compliance/policy/policy_test.go
index aea307f..09e831c 100644
--- a/tools/compliance/policy/policy_test.go
+++ b/tools/compliance/policy/policy_test.go
@@ -34,21 +34,21 @@
 		{
 			name:                     "firstparty",
 			edge:                     annotated{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
-			expectedDepActions:       []string{"apacheLib.meta_lic:apacheLib.meta_lic:notice"},
+			expectedDepActions:       []string{},
 			expectedTargetConditions: []string{},
 		},
 		{
 			name:                     "notice",
 			edge:                     annotated{"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
-			expectedDepActions:       []string{"mitLib.meta_lic:mitLib.meta_lic:notice"},
+			expectedDepActions:       []string{},
 			expectedTargetConditions: []string{},
 		},
 		{
 			name: "fponlgpl",
 			edge: annotated{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
 			expectedDepActions: []string{
-				"apacheBin.meta_lic:lgplLib.meta_lic:restricted",
-				"lgplLib.meta_lic:lgplLib.meta_lic:restricted",
+				"apacheBin.meta_lic:lgplLib.meta_lic:restricted_allows_dynamic_linking",
+				"lgplLib.meta_lic:lgplLib.meta_lic:restricted_allows_dynamic_linking",
 			},
 			expectedTargetConditions: []string{},
 		},
@@ -86,8 +86,8 @@
 			name: "independentmodulestatic",
 			edge: annotated{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
 			expectedDepActions: []string{
-				"apacheBin.meta_lic:gplWithClasspathException.meta_lic:restricted",
-				"gplWithClasspathException.meta_lic:gplWithClasspathException.meta_lic:restricted",
+				"apacheBin.meta_lic:gplWithClasspathException.meta_lic:restricted_with_classpath_exception",
+				"gplWithClasspathException.meta_lic:gplWithClasspathException.meta_lic:restricted_with_classpath_exception",
 			},
 			expectedTargetConditions: []string{},
 		},
@@ -95,8 +95,8 @@
 			name: "dependentmodule",
 			edge: annotated{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
 			expectedDepActions: []string{
-				"dependentModule.meta_lic:gplWithClasspathException.meta_lic:restricted",
-				"gplWithClasspathException.meta_lic:gplWithClasspathException.meta_lic:restricted",
+				"dependentModule.meta_lic:gplWithClasspathException.meta_lic:restricted_with_classpath_exception",
+				"gplWithClasspathException.meta_lic:gplWithClasspathException.meta_lic:restricted_with_classpath_exception",
 			},
 			expectedTargetConditions: []string{},
 		},
@@ -104,8 +104,8 @@
 		{
 			name:                     "lgplonfp",
 			edge:                     annotated{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
-			expectedDepActions:       []string{"apacheLib.meta_lic:apacheLib.meta_lic:notice"},
-			expectedTargetConditions: []string{"lgplBin.meta_lic:restricted"},
+			expectedDepActions:       []string{},
+			expectedTargetConditions: []string{"lgplBin.meta_lic:restricted_allows_dynamic_linking"},
 		},
 		{
 			name:                     "lgplonfpdynamic",
@@ -116,14 +116,14 @@
 		{
 			name:                     "gplonfp",
 			edge:                     annotated{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
-			expectedDepActions:       []string{"apacheLib.meta_lic:apacheLib.meta_lic:notice"},
+			expectedDepActions:       []string{},
 			expectedTargetConditions: []string{"gplBin.meta_lic:restricted"},
 		},
 		{
 			name:                     "gplcontainer",
 			edge:                     annotated{"gplContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
 			treatAsAggregate:         true,
-			expectedDepActions:       []string{"apacheLib.meta_lic:apacheLib.meta_lic:notice"},
+			expectedDepActions:       []string{},
 			expectedTargetConditions: []string{"gplContainer.meta_lic:restricted"},
 		},
 		{
@@ -133,7 +133,6 @@
 			otherCondition:   "gplLib.meta_lic:restricted",
 			expectedDepActions: []string{
 				"apacheContainer.meta_lic:gplLib.meta_lic:restricted",
-				"apacheLib.meta_lic:apacheLib.meta_lic:notice",
 				"apacheLib.meta_lic:gplLib.meta_lic:restricted",
 				"gplLib.meta_lic:gplLib.meta_lic:restricted",
 			},
@@ -146,7 +145,6 @@
 			otherCondition:   "gplLib.meta_lic:restricted",
 			expectedDepActions: []string{
 				"apacheBin.meta_lic:gplLib.meta_lic:restricted",
-				"apacheLib.meta_lic:apacheLib.meta_lic:notice",
 				"apacheLib.meta_lic:gplLib.meta_lic:restricted",
 				"gplLib.meta_lic:gplLib.meta_lic:restricted",
 			},
@@ -167,14 +165,14 @@
 		{
 			name:                     "independentmodulereversestatic",
 			edge:                     annotated{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"static"}},
-			expectedDepActions:       []string{"apacheBin.meta_lic:apacheBin.meta_lic:notice"},
-			expectedTargetConditions: []string{"gplWithClasspathException.meta_lic:restricted"},
+			expectedDepActions:       []string{},
+			expectedTargetConditions: []string{"gplWithClasspathException.meta_lic:restricted_with_classpath_exception"},
 		},
 		{
 			name:                     "dependentmodulereverse",
 			edge:                     annotated{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
 			expectedDepActions:       []string{},
-			expectedTargetConditions: []string{"gplWithClasspathException.meta_lic:restricted"},
+			expectedTargetConditions: []string{"gplWithClasspathException.meta_lic:restricted_with_classpath_exception"},
 		},
 		{
 			name: "ponr",
@@ -188,31 +186,31 @@
 		{
 			name:                     "ronp",
 			edge:                     annotated{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
-			expectedDepActions:       []string{"proprietary.meta_lic:proprietary.meta_lic:proprietary"},
+			expectedDepActions:       []string{},
 			expectedTargetConditions: []string{"gplBin.meta_lic:restricted"},
 		},
 		{
 			name:                     "noticeonb_e_o",
 			edge:                     annotated{"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
-			expectedDepActions:       []string{"by_exception.meta_lic:by_exception.meta_lic:by_exception_only"},
+			expectedDepActions:       []string{},
 			expectedTargetConditions: []string{},
 		},
 		{
 			name:                     "b_e_oonnotice",
 			edge:                     annotated{"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
-			expectedDepActions:       []string{"mitLib.meta_lic:mitLib.meta_lic:notice"},
+			expectedDepActions:       []string{},
 			expectedTargetConditions: []string{},
 		},
 		{
 			name:                     "noticeonrecip",
 			edge:                     annotated{"mitBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
-			expectedDepActions:       []string{"mplLib.meta_lic:mplLib.meta_lic:reciprocal"},
+			expectedDepActions:       []string{},
 			expectedTargetConditions: []string{},
 		},
 		{
 			name:                     "reciponnotice",
 			edge:                     annotated{"mplBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
-			expectedDepActions:       []string{"mitLib.meta_lic:mitLib.meta_lic:notice"},
+			expectedDepActions:       []string{},
 			expectedTargetConditions: []string{},
 		},
 	}
@@ -231,69 +229,80 @@
 				t.Errorf("unexpected error reading graph: %w", err)
 				return
 			}
+			edge := lg.Edges()[0]
 			// simulate a condition inherited from another edge/dependency.
 			otherTarget := ""
 			otherCondition := ""
+			var otn *TargetNode
 			if len(tt.otherCondition) > 0 {
 				fields := strings.Split(tt.otherCondition, ":")
 				otherTarget = fields[0]
 				otherCondition = fields[1]
+				otn = &TargetNode{name: otherTarget}
 				// other target must exist in graph
-				lg.targets[otherTarget] = &TargetNode{name: otherTarget}
-				lg.targets[otherTarget].proto.LicenseConditions = append(lg.targets[otherTarget].proto.LicenseConditions, otherCondition)
+				lg.targets[otherTarget] = otn
+				otn.licenseConditions = LicenseConditionSet(RecognizedConditionNames[otherCondition])
+			}
+			targets := make(map[string]*TargetNode)
+			targets[edge.target.name] = edge.target
+			targets[edge.dependency.name] = edge.dependency
+			if otn != nil {
+				targets[otn.name] = otn
 			}
 			if tt.expectedDepActions != nil {
-				depActions := make(actionSet)
-				depActions[lg.targets[tt.edge.dep]] = lg.targets[tt.edge.dep].LicenseConditions()
-				if otherTarget != "" {
-					// simulate a sub-dependency's condition having already propagated up to dep and about to go to target
-					otherCs := lg.targets[otherTarget].LicenseConditions()
-					depActions[lg.targets[tt.edge.dep]].AddSet(otherCs)
-					depActions[lg.targets[otherTarget]] = otherCs
-				}
-				asActual := depActionsApplicableToTarget(lg.Edges()[0], depActions, tt.treatAsAggregate)
-				asExpected := make(actionSet)
-				for _, triple := range tt.expectedDepActions {
-					fields := strings.Split(triple, ":")
-					actsOn := lg.targets[fields[0]]
-					origin := lg.targets[fields[1]]
-					expectedConditions := newLicenseConditionSet()
-					expectedConditions.add(origin, fields[2:]...)
-					if _, ok := asExpected[actsOn]; ok {
-						asExpected[actsOn].AddSet(expectedConditions)
-					} else {
-						asExpected[actsOn] = expectedConditions
+				t.Run("depConditionsPropagatingToTarget", func(t *testing.T) {
+					depConditions := edge.dependency.LicenseConditions()
+					if otherTarget != "" {
+						// simulate a sub-dependency's condition having already propagated up to dep and about to go to target
+						otherCs := otn.LicenseConditions()
+						depConditions |= otherCs
 					}
-				}
-
-				checkSameActions(lg, asActual, asExpected, t)
+					t.Logf("calculate target actions for edge=%s, dep conditions=%04x, treatAsAggregate=%v", edge.String(), depConditions, tt.treatAsAggregate)
+					csActual := depConditionsPropagatingToTarget(lg, edge, depConditions, tt.treatAsAggregate)
+					t.Logf("calculated target conditions as %04x{%s}", csActual, strings.Join(csActual.Names(), ", "))
+					csExpected := NewLicenseConditionSet()
+					for _, triple := range tt.expectedDepActions {
+						fields := strings.Split(triple, ":")
+						expectedConditions := NewLicenseConditionSet()
+						for _, cname := range fields[2:] {
+							expectedConditions = expectedConditions.Plus(RecognizedConditionNames[cname])
+						}
+						csExpected |= expectedConditions
+					}
+					t.Logf("expected target conditions as %04x{%s}", csExpected, strings.Join(csExpected.Names(), ", "))
+					if csActual != csExpected {
+						t.Errorf("unexpected license conditions: got %04x, want %04x", csActual, csExpected)
+					}
+				})
 			}
 			if tt.expectedTargetConditions != nil {
-				targetConditions := lg.TargetNode(tt.edge.target).LicenseConditions()
-				if otherTarget != "" {
-					targetConditions.add(lg.targets[otherTarget], otherCondition)
-				}
-				cs := targetConditionsApplicableToDep(
-					lg.Edges()[0],
-					targetConditions,
-					tt.treatAsAggregate)
-				actual := make([]string, 0, cs.Count())
-				for _, lc := range cs.AsList() {
-					actual = append(actual, lc.asString(":"))
-				}
-				sort.Strings(actual)
-				sort.Strings(tt.expectedTargetConditions)
-				if len(actual) != len(tt.expectedTargetConditions) {
-					t.Errorf("unexpected number of target conditions: got %v with %d conditions, want %v with %d conditions",
-						actual, len(actual), tt.expectedTargetConditions, len(tt.expectedTargetConditions))
-				} else {
-					for i := 0; i < len(actual); i++ {
-						if actual[i] != tt.expectedTargetConditions[i] {
-							t.Errorf("unexpected target condition at element %d: got %q, want %q",
-								i, actual[i], tt.expectedTargetConditions[i])
+				t.Run("targetConditionsPropagatingToDep", func(t *testing.T) {
+					targetConditions := edge.target.LicenseConditions()
+					if otherTarget != "" {
+						targetConditions = targetConditions.Union(otn.licenseConditions)
+					}
+					t.Logf("calculate dep conditions for edge=%s, target conditions=%v, treatAsAggregate=%v", edge.String(), targetConditions.Names(), tt.treatAsAggregate)
+					cs := targetConditionsPropagatingToDep(lg, edge, targetConditions, tt.treatAsAggregate)
+					t.Logf("calculated dep conditions as %v", cs.Names())
+					actual := cs.Names()
+					sort.Strings(actual)
+					expected := make([]string, 0)
+					for _, expectedDepCondition := range tt.expectedTargetConditions {
+						expected = append(expected, strings.Split(expectedDepCondition, ":")[1])
+					}
+					sort.Strings(expected)
+					if len(actual) != len(expected) {
+						t.Errorf("unexpected number of target conditions: got %v with %d conditions, want %v with %d conditions",
+							actual, len(actual), expected, len(expected))
+					} else {
+						for i := 0; i < len(actual); i++ {
+							if actual[i] != expected[i] {
+								t.Errorf("unexpected target condition at element %d: got %q, want %q",
+									i, actual[i], expected[i])
+							}
 						}
 					}
-				}
+				})
 			}
 		})
 	}
diff --git a/tools/compliance/policy/resolve.go b/tools/compliance/policy/resolve.go
index 58547f8..336894a 100644
--- a/tools/compliance/policy/resolve.go
+++ b/tools/compliance/policy/resolve.go
@@ -14,228 +14,196 @@
 
 package compliance
 
+import (
+	"sync"
+)
+
 // ResolveBottomUpConditions performs a bottom-up walk of the LicenseGraph
 // propagating conditions up the graph as necessary according to the properties
 // of each edge and according to each license condition in question.
 //
-// Subsequent top-down walks of the graph will filter some resolutions and may
-// introduce new resolutions.
-//
 // e.g. if a "restricted" condition applies to a binary, it also applies to all
 // of the statically-linked libraries and the transitive closure of their static
 // dependencies; even if neither they nor the transitive closure of their
 // dependencies originate any "restricted" conditions. The bottom-up walk will
 // not resolve the library and its transitive closure, but the later top-down
 // walk will.
-func ResolveBottomUpConditions(lg *LicenseGraph) *ResolutionSet {
+func ResolveBottomUpConditions(lg *LicenseGraph) {
 
 	// short-cut if already walked and cached
 	lg.mu.Lock()
-	rs := lg.rsBU
+	wg := lg.wgBU
+
+	if wg != nil {
+		lg.mu.Unlock()
+		wg.Wait()
+		return
+	}
+	wg = &sync.WaitGroup{}
+	wg.Add(1)
+	lg.wgBU = wg
 	lg.mu.Unlock()
 
-	if rs != nil {
-		return rs
+	// amap identifes targets previously walked. (guarded by mu)
+	amap := make(map[*TargetNode]struct{})
+
+	// cmap identifies targets previously walked as pure aggregates. i.e. as containers
+	// (guarded by mu)
+	cmap := make(map[*TargetNode]struct{})
+	var mu sync.Mutex
+
+	var walk func(target *TargetNode, treatAsAggregate bool) LicenseConditionSet
+
+	walk = func(target *TargetNode, treatAsAggregate bool) LicenseConditionSet {
+		priorWalkResults := func() (LicenseConditionSet, bool) {
+			mu.Lock()
+			defer mu.Unlock()
+
+			if _, alreadyWalked := amap[target]; alreadyWalked {
+				if treatAsAggregate {
+					return target.resolution, true
+				}
+				if _, asAggregate := cmap[target]; !asAggregate {
+					return target.resolution, true
+				}
+				// previously walked in a pure aggregate context,
+				// needs to walk again in non-aggregate context
+				delete(cmap, target)
+			} else {
+				target.resolution |= target.licenseConditions
+				amap[target] = struct{}{}
+			}
+			if treatAsAggregate {
+				cmap[target] = struct{}{}
+			}
+			return target.resolution, false
+		}
+		cs, alreadyWalked := priorWalkResults()
+		if alreadyWalked {
+			return cs
+		}
+
+		c := make(chan LicenseConditionSet, len(target.edges))
+		// add all the conditions from all the dependencies
+		for _, edge := range target.edges {
+			go func(edge *TargetEdge) {
+				// walk dependency to get its conditions
+				cs := walk(edge.dependency, treatAsAggregate && edge.dependency.IsContainer())
+
+				// turn those into the conditions that apply to the target
+				cs = depConditionsPropagatingToTarget(lg, edge, cs, treatAsAggregate)
+
+				c <- cs
+			}(edge)
+		}
+		for i := 0; i < len(target.edges); i++ {
+			cs |= <-c
+		}
+		mu.Lock()
+		target.resolution |= cs
+		mu.Unlock()
+
+		// return conditions up the tree
+		return cs
 	}
 
-	// must be indexed for fast lookup
-	lg.indexForward()
-
-	rs = resolveBottomUp(lg, make(map[*TargetNode]actionSet) /* empty map; no prior resolves */)
-
-	// if not yet cached, save the result
-	lg.mu.Lock()
-	if lg.rsBU == nil {
-		lg.rsBU = rs
-	} else {
-		// if we end up with 2, release the later for garbage collection
-		rs = lg.rsBU
+	// walk each of the roots
+	for _, rname := range lg.rootFiles {
+		rnode := lg.targets[rname]
+		_ = walk(rnode, rnode.IsContainer())
 	}
-	lg.mu.Unlock()
 
-	return rs
+	wg.Done()
 }
 
 // ResolveTopDownCondtions performs a top-down walk of the LicenseGraph
-// resolving all reachable nodes for `condition`. Policy establishes the rules
-// for transforming and propagating resolutions down the graph.
+// propagating conditions from target to dependency.
 //
 // e.g. For current policy, none of the conditions propagate from target to
 // dependency except restricted. For restricted, the policy is to share the
 // source of any libraries linked to restricted code and to provide notice.
-func ResolveTopDownConditions(lg *LicenseGraph) *ResolutionSet {
+func ResolveTopDownConditions(lg *LicenseGraph) {
 
 	// short-cut if already walked and cached
 	lg.mu.Lock()
-	rs := lg.rsTD
-	lg.mu.Unlock()
+	wg := lg.wgTD
 
-	if rs != nil {
-		return rs
+	if wg != nil {
+		lg.mu.Unlock()
+		wg.Wait()
+		return
 	}
+	wg = &sync.WaitGroup{}
+	wg.Add(1)
+	lg.wgTD = wg
+	lg.mu.Unlock()
 
 	// start with the conditions propagated up the graph
-	rs = ResolveBottomUpConditions(lg)
+	ResolveBottomUpConditions(lg)
 
-	// rmap maps 'appliesTo' targets to their applicable conditions
-	//
-	// rmap is the resulting ResolutionSet
-	rmap := make(map[*TargetNode]actionSet)
+	// amap contains the set of targets already walked. (guarded by mu)
+	amap := make(map[*TargetNode]struct{})
 
 	// cmap contains the set of targets walked as pure aggregates. i.e. containers
-	cmap := make(map[*TargetNode]bool)
+	// (guarded by mu)
+	cmap := make(map[*TargetNode]struct{})
 
-	var walk func(fnode *TargetNode, cs *LicenseConditionSet, treatAsAggregate bool)
+	// mu guards concurrent access to cmap
+	var mu sync.Mutex
 
-	walk = func(fnode *TargetNode, cs *LicenseConditionSet, treatAsAggregate bool) {
-		if _, ok := rmap[fnode]; !ok {
-			rmap[fnode] = make(actionSet)
-		}
-		rmap[fnode].add(fnode, cs)
+	var walk func(fnode *TargetNode, cs LicenseConditionSet, treatAsAggregate bool)
+
+	walk = func(fnode *TargetNode, cs LicenseConditionSet, treatAsAggregate bool) {
+		defer wg.Done()
+		mu.Lock()
+		fnode.resolution |= fnode.licenseConditions
+		fnode.resolution |= cs
+		amap[fnode] = struct{}{}
 		if treatAsAggregate {
-			cmap[fnode] = true
+			cmap[fnode] = struct{}{}
 		}
-		// add conditions attached to `fnode`
-		cs = cs.Copy()
-		for _, fcs := range rs.resolutions[fnode] {
-			cs.AddSet(fcs)
-		}
+		cs = fnode.resolution
+		mu.Unlock()
 		// for each dependency
-		for _, edge := range lg.index[fnode.name] {
-			e := TargetEdge{lg, edge}
-			// dcs holds the dpendency conditions inherited from the target
-			dcs := targetConditionsApplicableToDep(e, cs, treatAsAggregate)
-			if dcs.IsEmpty() && !treatAsAggregate {
-				continue
-			}
-			dnode := lg.targets[edge.dependency]
-			if as, alreadyWalked := rmap[dnode]; alreadyWalked {
-				diff := dcs.Copy()
-				diff.RemoveSet(as.conditions())
-				if diff.IsEmpty() {
-					// no new conditions
+		for _, edge := range fnode.edges {
+			func(edge *TargetEdge) {
+				// dcs holds the dpendency conditions inherited from the target
+				dcs := targetConditionsPropagatingToDep(lg, edge, cs, treatAsAggregate)
+				dnode := edge.dependency
+				mu.Lock()
+				defer mu.Unlock()
+				depcs := dnode.resolution
+				_, alreadyWalked := amap[dnode]
+				if !dcs.IsEmpty() && alreadyWalked {
+					if dcs.Difference(depcs).IsEmpty() {
+						// no new conditions
 
-					// pure aggregates never need walking a 2nd time with same conditions
-					if treatAsAggregate {
-						continue
+						// pure aggregates never need walking a 2nd time with same conditions
+						if treatAsAggregate {
+							return
+						}
+						// non-aggregates don't need walking as non-aggregate a 2nd time
+						if _, asAggregate := cmap[dnode]; !asAggregate {
+							return
+						}
+						// previously walked as pure aggregate; need to re-walk as non-aggregate
+						delete(cmap, dnode)
 					}
-					// non-aggregates don't need walking as non-aggregate a 2nd time
-					if _, asAggregate := cmap[dnode]; !asAggregate {
-						continue
-					}
-					// previously walked as pure aggregate; need to re-walk as non-aggregate
-					delete(cmap, dnode)
 				}
-			}
-			// add the conditions to the dependency
-			walk(dnode, dcs, treatAsAggregate && lg.targets[edge.dependency].IsContainer())
+				// add the conditions to the dependency
+				wg.Add(1)
+				go walk(dnode, dcs, treatAsAggregate && dnode.IsContainer())
+			}(edge)
 		}
 	}
 
 	// walk each of the roots
-	for _, r := range lg.rootFiles {
-		rnode := lg.targets[r]
-		as, ok := rs.resolutions[rnode]
-		if !ok {
-			// no conditions in root or transitive closure of dependencies
-			continue
-		}
-		if as.isEmpty() {
-			continue
-		}
-
+	for _, rname := range lg.rootFiles {
+		rnode := lg.targets[rname]
+		wg.Add(1)
 		// add the conditions to the root and its transitive closure
-		walk(rnode, newLicenseConditionSet(), lg.targets[r].IsContainer())
+		go walk(rnode, NewLicenseConditionSet(), rnode.IsContainer())
 	}
-
-	// back-fill any bottom-up conditions on targets missed by top-down walk
-	for attachesTo, as := range rs.resolutions {
-		if _, ok := rmap[attachesTo]; !ok {
-			rmap[attachesTo] = as.copy()
-		} else {
-			rmap[attachesTo].addSet(as)
-		}
-	}
-
-	// propagate any new conditions back up the graph
-	rs = resolveBottomUp(lg, rmap)
-
-	// if not yet cached, save the result
-	lg.mu.Lock()
-	if lg.rsTD == nil {
-		lg.rsTD = rs
-	} else {
-		// if we end up with 2, release the later for garbage collection
-		rs = lg.rsTD
-	}
-	lg.mu.Unlock()
-
-	return rs
-}
-
-// resolveBottomUp implements a bottom-up resolve propagating conditions both
-// from the graph, and from a `priors` map of resolutions.
-func resolveBottomUp(lg *LicenseGraph, priors map[*TargetNode]actionSet) *ResolutionSet {
-	rs := newResolutionSet()
-
-	// cmap contains an entry for every target that was previously walked as a pure aggregate only.
-	cmap := make(map[string]bool)
-
-	var walk func(f string, treatAsAggregate bool) actionSet
-
-	walk = func(f string, treatAsAggregate bool) actionSet {
-		target := lg.targets[f]
-		result := make(actionSet)
-		result[target] = newLicenseConditionSet()
-		result[target].add(target, target.proto.LicenseConditions...)
-		if pas, ok := priors[target]; ok {
-			result.addSet(pas)
-		}
-		if preresolved, ok := rs.resolutions[target]; ok {
-			if treatAsAggregate {
-				result.addSet(preresolved)
-				return result
-			}
-			if _, asAggregate := cmap[f]; !asAggregate {
-				result.addSet(preresolved)
-				return result
-			}
-			// previously walked in a pure aggregate context,
-			// needs to walk again in non-aggregate context
-			delete(cmap, f)
-		}
-		if treatAsAggregate {
-			cmap[f] = true
-		}
-
-		// add all the conditions from all the dependencies
-		for _, edge := range lg.index[f] {
-			// walk dependency to get its conditions
-			as := walk(edge.dependency, treatAsAggregate && lg.targets[edge.dependency].IsContainer())
-
-			// turn those into the conditions that apply to the target
-			as = depActionsApplicableToTarget(TargetEdge{lg, edge}, as, treatAsAggregate)
-
-			// add them to the result
-			result.addSet(as)
-		}
-
-		// record these conditions as applicable to the target
-		rs.addConditions(target, result)
-		if len(priors) == 0 {
-			// on the first bottom-up resolve, parents have their own sharing and notice needs
-			// on the later resolve, if priors is empty, there will be nothing new to add
-			rs.addSelf(target, result.byName(ImpliesRestricted))
-		}
-
-		// return this up the tree
-		return result
-	}
-
-	// walk each of the roots
-	for _, r := range lg.rootFiles {
-		_ = walk(r, lg.targets[r].IsContainer())
-	}
-
-	return rs
+	wg.Done()
+	wg.Wait()
 }
diff --git a/tools/compliance/policy/resolve_test.go b/tools/compliance/policy/resolve_test.go
index 4c99d35..09dd7dd 100644
--- a/tools/compliance/policy/resolve_test.go
+++ b/tools/compliance/policy/resolve_test.go
@@ -16,15 +16,16 @@
 
 import (
 	"bytes"
+	"sort"
 	"testing"
 )
 
 func TestResolveBottomUpConditions(t *testing.T) {
 	tests := []struct {
-		name                string
-		roots               []string
-		edges               []annotated
-		expectedResolutions []res
+		name            string
+		roots           []string
+		edges           []annotated
+		expectedActions []tcond
 	}{
 		{
 			name:  "firstparty",
@@ -32,10 +33,9 @@
 			edges: []annotated{
 				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
 			},
-			expectedResolutions: []res{
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheBin.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
-				{"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+			expectedActions: []tcond{
+				{"apacheBin.meta_lic", "notice"},
+				{"apacheLib.meta_lic", "notice"},
 			},
 		},
 		{
@@ -44,9 +44,9 @@
 			edges: []annotated{
 				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"toolchain"}},
 			},
-			expectedResolutions: []res{
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+			expectedActions: []tcond{
+				{"apacheBin.meta_lic", "notice"},
+				{"apacheLib.meta_lic", "notice"},
 			},
 		},
 		{
@@ -56,13 +56,10 @@
 				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
 				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
 			},
-			expectedResolutions: []res{
-				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheBin.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
-				{"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+			expectedActions: []tcond{
+				{"apacheContainer.meta_lic", "notice"},
+				{"apacheBin.meta_lic", "notice"},
+				{"apacheLib.meta_lic", "notice"},
 			},
 		},
 		{
@@ -72,12 +69,10 @@
 				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
 				{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
 			},
-			expectedResolutions: []res{
-				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+			expectedActions: []tcond{
+				{"apacheContainer.meta_lic", "notice"},
+				{"apacheBin.meta_lic", "notice"},
+				{"apacheLib.meta_lic", "notice"},
 			},
 		},
 		{
@@ -86,9 +81,9 @@
 			edges: []annotated{
 				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
 			},
-			expectedResolutions: []res{
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+			expectedActions: []tcond{
+				{"apacheBin.meta_lic", "notice"},
+				{"apacheLib.meta_lic", "notice"},
 			},
 		},
 		{
@@ -98,11 +93,10 @@
 				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
 				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
 			},
-			expectedResolutions: []res{
-				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+			expectedActions: []tcond{
+				{"apacheContainer.meta_lic", "notice"},
+				{"apacheBin.meta_lic", "notice"},
+				{"apacheLib.meta_lic", "notice"},
 			},
 		},
 		{
@@ -112,11 +106,10 @@
 				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
 				{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
 			},
-			expectedResolutions: []res{
-				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+			expectedActions: []tcond{
+				{"apacheContainer.meta_lic", "notice"},
+				{"apacheBin.meta_lic", "notice"},
+				{"apacheLib.meta_lic", "notice"},
 			},
 		},
 		{
@@ -125,11 +118,9 @@
 			edges: []annotated{
 				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
 			},
-			expectedResolutions: []res{
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+			expectedActions: []tcond{
+				{"apacheBin.meta_lic", "notice|restricted"},
+				{"gplLib.meta_lic", "restricted"},
 			},
 		},
 		{
@@ -138,9 +129,9 @@
 			edges: []annotated{
 				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"toolchain"}},
 			},
-			expectedResolutions: []res{
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+			expectedActions: []tcond{
+				{"apacheBin.meta_lic", "notice"},
+				{"gplLib.meta_lic", "restricted"},
 			},
 		},
 		{
@@ -150,16 +141,10 @@
 				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
 				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
 			},
-			expectedResolutions: []res{
-				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+			expectedActions: []tcond{
+				{"apacheContainer.meta_lic", "notice|restricted"},
+				{"apacheBin.meta_lic", "notice|restricted"},
+				{"gplLib.meta_lic", "restricted"},
 			},
 		},
 		{
@@ -169,13 +154,10 @@
 				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
 				{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
 			},
-			expectedResolutions: []res{
-				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+			expectedActions: []tcond{
+				{"apacheContainer.meta_lic", "notice|restricted"},
+				{"apacheBin.meta_lic", "notice"},
+				{"gplLib.meta_lic", "restricted"},
 			},
 		},
 		{
@@ -184,11 +166,9 @@
 			edges: []annotated{
 				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
 			},
-			expectedResolutions: []res{
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+			expectedActions: []tcond{
+				{"apacheBin.meta_lic", "notice|restricted"},
+				{"gplLib.meta_lic", "restricted"},
 			},
 		},
 		{
@@ -198,16 +178,10 @@
 				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
 				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
 			},
-			expectedResolutions: []res{
-				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+			expectedActions: []tcond{
+				{"apacheContainer.meta_lic", "notice|restricted"},
+				{"apacheBin.meta_lic", "notice|restricted"},
+				{"gplLib.meta_lic", "restricted"},
 			},
 		},
 		{
@@ -217,13 +191,10 @@
 				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
 				{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
 			},
-			expectedResolutions: []res{
-				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+			expectedActions: []tcond{
+				{"apacheContainer.meta_lic", "notice|restricted"},
+				{"apacheBin.meta_lic", "notice"},
+				{"gplLib.meta_lic", "restricted"},
 			},
 		},
 		{
@@ -232,11 +203,9 @@
 			edges: []annotated{
 				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
 			},
-			expectedResolutions: []res{
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
-				{"apacheBin.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
-				{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+			expectedActions: []tcond{
+				{"apacheBin.meta_lic", "notice|restricted_allows_dynamic_linking"},
+				{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
 			},
 		},
 		{
@@ -245,9 +214,9 @@
 			edges: []annotated{
 				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"toolchain"}},
 			},
-			expectedResolutions: []res{
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+			expectedActions: []tcond{
+				{"apacheBin.meta_lic", "notice"},
+				{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
 			},
 		},
 		{
@@ -257,16 +226,10 @@
 				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
 				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
 			},
-			expectedResolutions: []res{
-				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "lgplLib.meta_lic", "restricted"},
-				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
-				{"apacheContainer.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
-				{"apacheBin.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
-				{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+			expectedActions: []tcond{
+				{"apacheContainer.meta_lic", "notice|restricted_allows_dynamic_linking"},
+				{"apacheBin.meta_lic", "notice|restricted_allows_dynamic_linking"},
+				{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
 			},
 		},
 		{
@@ -276,13 +239,10 @@
 				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
 				{"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"static"}},
 			},
-			expectedResolutions: []res{
-				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "lgplLib.meta_lic", "restricted"},
-				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+			expectedActions: []tcond{
+				{"apacheContainer.meta_lic", "notice|restricted_allows_dynamic_linking"},
+				{"apacheBin.meta_lic", "notice"},
+				{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
 			},
 		},
 		{
@@ -291,9 +251,9 @@
 			edges: []annotated{
 				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
 			},
-			expectedResolutions: []res{
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+			expectedActions: []tcond{
+				{"apacheBin.meta_lic", "notice"},
+				{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
 			},
 		},
 		{
@@ -303,11 +263,10 @@
 				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
 				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
 			},
-			expectedResolutions: []res{
-				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+			expectedActions: []tcond{
+				{"apacheContainer.meta_lic", "notice"},
+				{"apacheBin.meta_lic", "notice"},
+				{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
 			},
 		},
 		{
@@ -317,11 +276,10 @@
 				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
 				{"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
 			},
-			expectedResolutions: []res{
-				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+			expectedActions: []tcond{
+				{"apacheContainer.meta_lic", "notice"},
+				{"apacheBin.meta_lic", "notice"},
+				{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
 			},
 		},
 		{
@@ -330,11 +288,9 @@
 			edges: []annotated{
 				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
 			},
-			expectedResolutions: []res{
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
-				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
-				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+			expectedActions: []tcond{
+				{"apacheBin.meta_lic", "notice|restricted_with_classpath_exception"},
+				{"gplWithClasspathException.meta_lic", "restricted_with_classpath_exception"},
 			},
 		},
 		{
@@ -343,11 +299,9 @@
 			edges: []annotated{
 				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
 			},
-			expectedResolutions: []res{
-				{"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
-				{"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
-				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
-				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+			expectedActions: []tcond{
+				{"dependentModule.meta_lic", "notice|restricted_with_classpath_exception"},
+				{"gplWithClasspathException.meta_lic", "restricted_with_classpath_exception"},
 			},
 		},
 		{
@@ -356,9 +310,9 @@
 			edges: []annotated{
 				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
 			},
-			expectedResolutions: []res{
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+			expectedActions: []tcond{
+				{"apacheBin.meta_lic", "notice"},
+				{"gplWithClasspathException.meta_lic", "restricted_with_classpath_exception"},
 			},
 		},
 		{
@@ -367,11 +321,9 @@
 			edges: []annotated{
 				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
 			},
-			expectedResolutions: []res{
-				{"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
-				{"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
-				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
-				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+			expectedActions: []tcond{
+				{"dependentModule.meta_lic", "notice|restricted_with_classpath_exception"},
+				{"gplWithClasspathException.meta_lic", "restricted_with_classpath_exception"},
 			},
 		},
 	}
@@ -383,19 +335,37 @@
 				t.Errorf("unexpected test data error: got %w, want no error", err)
 				return
 			}
-			expectedRs := toResolutionSet(lg, tt.expectedResolutions)
-			actualRs := ResolveBottomUpConditions(lg)
-			checkSame(actualRs, expectedRs, t)
+
+			logGraph(lg, t)
+
+			ResolveBottomUpConditions(lg)
+			actual := asActionList(lg)
+			sort.Sort(actual)
+			t.Logf("actual: %s", actual.String())
+
+			expected := toActionList(lg, tt.expectedActions)
+			sort.Sort(expected)
+			t.Logf("expected: %s", expected.String())
+
+			if len(actual) != len(expected) {
+				t.Errorf("unexpected number of actions: got %d, want %d", len(actual), len(expected))
+				return
+			}
+			for i := 0; i < len(actual); i++ {
+				if actual[i] != expected[i] {
+					t.Errorf("unexpected action at index %d: got %s, want %s", i, actual[i].String(), expected[i].String())
+				}
+			}
 		})
 	}
 }
 
 func TestResolveTopDownConditions(t *testing.T) {
 	tests := []struct {
-		name                string
-		roots               []string
-		edges               []annotated
-		expectedResolutions []res
+		name            string
+		roots           []string
+		edges           []annotated
+		expectedActions []tcond
 	}{
 		{
 			name:  "firstparty",
@@ -403,10 +373,9 @@
 			edges: []annotated{
 				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
 			},
-			expectedResolutions: []res{
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheBin.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
-				{"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+			expectedActions: []tcond{
+				{"apacheBin.meta_lic", "notice"},
+				{"apacheLib.meta_lic", "notice"},
 			},
 		},
 		{
@@ -415,9 +384,9 @@
 			edges: []annotated{
 				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
 			},
-			expectedResolutions: []res{
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheLib.meta_lic", "apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+			expectedActions: []tcond{
+				{"apacheBin.meta_lic", "notice"},
+				{"apacheLib.meta_lic", "notice"},
 			},
 		},
 		{
@@ -427,15 +396,10 @@
 				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
 				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
 			},
-			expectedResolutions: []res{
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheBin.meta_lic", "mitLib.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
-				{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"mitLib.meta_lic", "mitLib.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+			expectedActions: []tcond{
+				{"apacheBin.meta_lic", "notice|restricted"},
+				{"mitLib.meta_lic", "notice|restricted"},
+				{"gplLib.meta_lic", "restricted"},
 			},
 		},
 		{
@@ -445,11 +409,10 @@
 				{"apacheBin.meta_lic", "gplBin.meta_lic", []string{"toolchain"}},
 				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
 			},
-			expectedResolutions: []res{
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
-				{"gplBin.meta_lic", "gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
-				{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+			expectedActions: []tcond{
+				{"apacheBin.meta_lic", "notice"},
+				{"mitLib.meta_lic", "notice"},
+				{"gplBin.meta_lic", "restricted"},
 			},
 		},
 		{
@@ -462,27 +425,13 @@
 				{"apacheBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
 				{"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
 			},
-			expectedResolutions: []res{
-				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheContainer.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "mplLib.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheContainer.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheBin.meta_lic", "mplLib.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheBin.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
-				{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"mitBin.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
-				{"mitBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
-				{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
-				{"mplLib.meta_lic", "mplLib.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"mplLib.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
+			expectedActions: []tcond{
+				{"apacheContainer.meta_lic", "notice|restricted"},
+				{"apacheBin.meta_lic", "notice|restricted"},
+				{"mitBin.meta_lic", "notice"},
+				{"gplLib.meta_lic", "restricted"},
+				{"mplLib.meta_lic", "reciprocal|restricted"},
+				{"mitLib.meta_lic", "notice"},
 			},
 		},
 		{
@@ -492,13 +441,10 @@
 				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
 				{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
 			},
-			expectedResolutions: []res{
-				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+			expectedActions: []tcond{
+				{"apacheContainer.meta_lic", "notice|restricted"},
+				{"apacheBin.meta_lic", "notice"},
+				{"gplLib.meta_lic", "restricted"},
 			},
 		},
 		{
@@ -508,14 +454,10 @@
 				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
 				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"dynamic"}},
 			},
-			expectedResolutions: []res{
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheBin.meta_lic", "mitLib.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"mitLib.meta_lic", "mitLib.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+			expectedActions: []tcond{
+				{"apacheBin.meta_lic", "notice|restricted"},
+				{"gplLib.meta_lic", "restricted"},
+				{"mitLib.meta_lic", "notice|restricted"},
 			},
 		},
 		{
@@ -528,23 +470,13 @@
 				{"apacheBin.meta_lic", "mplLib.meta_lic", []string{"dynamic"}},
 				{"mitBin.meta_lic", "mitLib.meta_lic", []string{"dynamic"}},
 			},
-			expectedResolutions: []res{
-				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheContainer.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "mplLib.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheBin.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheBin.meta_lic", "mplLib.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"mitBin.meta_lic", "mitBin.meta_lic", "mitBin.meta_lic", "notice"},
-				{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
-				{"mplLib.meta_lic", "mplLib.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"mplLib.meta_lic", "mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
+			expectedActions: []tcond{
+				{"apacheContainer.meta_lic", "notice|restricted"},
+				{"apacheBin.meta_lic", "notice|restricted"},
+				{"mitBin.meta_lic", "notice"},
+				{"gplLib.meta_lic", "restricted"},
+				{"mplLib.meta_lic", "reciprocal|restricted"},
+				{"mitLib.meta_lic", "notice"},
 			},
 		},
 		{
@@ -554,13 +486,10 @@
 				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
 				{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"dynamic"}},
 			},
-			expectedResolutions: []res{
-				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"gplLib.meta_lic", "gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+			expectedActions: []tcond{
+				{"apacheContainer.meta_lic", "notice|restricted"},
+				{"apacheBin.meta_lic", "notice"},
+				{"gplLib.meta_lic", "restricted"},
 			},
 		},
 		{
@@ -570,15 +499,10 @@
 				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
 				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
 			},
-			expectedResolutions: []res{
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
-				{"apacheBin.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
-				{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
-				{"apacheBin.meta_lic", "mitLib.meta_lic", "lgplLib.meta_lic", "restricted"},
-				{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
-				{"mitLib.meta_lic", "mitLib.meta_lic", "lgplLib.meta_lic", "restricted"},
-				{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+			expectedActions: []tcond{
+				{"apacheBin.meta_lic", "notice|restricted_allows_dynamic_linking"},
+				{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
+				{"mitLib.meta_lic", "notice|restricted_allows_dynamic_linking"},
 			},
 		},
 		{
@@ -588,11 +512,10 @@
 				{"apacheBin.meta_lic", "lgplBin.meta_lic", []string{"toolchain"}},
 				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
 			},
-			expectedResolutions: []res{
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
-				{"lgplBin.meta_lic", "lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
-				{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+			expectedActions: []tcond{
+				{"apacheBin.meta_lic", "notice"},
+				{"lgplBin.meta_lic", "restricted_allows_dynamic_linking"},
+				{"mitLib.meta_lic", "notice"},
 			},
 		},
 		{
@@ -603,22 +526,11 @@
 				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
 				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
 			},
-			expectedResolutions: []res{
-				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "lgplLib.meta_lic", "restricted"},
-				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
-				{"apacheContainer.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
-				{"apacheContainer.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "mitLib.meta_lic", "lgplLib.meta_lic", "restricted"},
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
-				{"apacheBin.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
-				{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
-				{"apacheBin.meta_lic", "mitLib.meta_lic", "lgplLib.meta_lic", "restricted"},
-				{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
-				{"mitLib.meta_lic", "mitLib.meta_lic", "lgplLib.meta_lic", "restricted"},
-				{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+			expectedActions: []tcond{
+				{"apacheContainer.meta_lic", "notice|restricted_allows_dynamic_linking"},
+				{"apacheBin.meta_lic", "notice|restricted_allows_dynamic_linking"},
+				{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
+				{"mitLib.meta_lic", "notice|restricted_allows_dynamic_linking"},
 			},
 		},
 		{
@@ -628,13 +540,10 @@
 				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
 				{"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"static"}},
 			},
-			expectedResolutions: []res{
-				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "lgplLib.meta_lic", "restricted"},
-				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+			expectedActions: []tcond{
+				{"apacheContainer.meta_lic", "notice|restricted_allows_dynamic_linking"},
+				{"apacheBin.meta_lic", "notice"},
+				{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
 			},
 		},
 		{
@@ -644,11 +553,10 @@
 				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
 				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
 			},
-			expectedResolutions: []res{
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
-				{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
-				{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+			expectedActions: []tcond{
+				{"apacheBin.meta_lic", "notice"},
+				{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
+				{"mitLib.meta_lic", "notice"},
 			},
 		},
 		{
@@ -658,11 +566,10 @@
 				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
 				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
 			},
-			expectedResolutions: []res{
-				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+			expectedActions: []tcond{
+				{"apacheContainer.meta_lic", "notice"},
+				{"apacheBin.meta_lic", "notice"},
+				{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
 			},
 		},
 		{
@@ -672,11 +579,10 @@
 				{"apacheContainer.meta_lic", "apacheBin.meta_lic", []string{"static"}},
 				{"apacheContainer.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
 			},
-			expectedResolutions: []res{
-				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
-				{"apacheContainer.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"lgplLib.meta_lic", "lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+			expectedActions: []tcond{
+				{"apacheContainer.meta_lic", "notice"},
+				{"apacheBin.meta_lic", "notice"},
+				{"lgplLib.meta_lic", "restricted_allows_dynamic_linking"},
 			},
 		},
 		{
@@ -686,15 +592,10 @@
 				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
 				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
 			},
-			expectedResolutions: []res{
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
-				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
-				{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
-				{"apacheBin.meta_lic", "mitLib.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
-				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
-				{"mitLib.meta_lic", "mitLib.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
-				{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+			expectedActions: []tcond{
+				{"apacheBin.meta_lic", "notice|restricted_with_classpath_exception"},
+				{"gplWithClasspathException.meta_lic", "restricted_with_classpath_exception"},
+				{"mitLib.meta_lic", "notice|restricted_with_classpath_exception"},
 			},
 		},
 		{
@@ -704,15 +605,10 @@
 				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
 				{"dependentModule.meta_lic", "mitLib.meta_lic", []string{"static"}},
 			},
-			expectedResolutions: []res{
-				{"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
-				{"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
-				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
-				{"dependentModule.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
-				{"dependentModule.meta_lic", "mitLib.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
-				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
-				{"mitLib.meta_lic", "mitLib.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
-				{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+			expectedActions: []tcond{
+				{"dependentModule.meta_lic", "notice|restricted_with_classpath_exception"},
+				{"gplWithClasspathException.meta_lic", "restricted_with_classpath_exception"},
+				{"mitLib.meta_lic", "notice|restricted_with_classpath_exception"},
 			},
 		},
 		{
@@ -722,11 +618,10 @@
 				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
 				{"apacheBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
 			},
-			expectedResolutions: []res{
-				{"apacheBin.meta_lic", "apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
-				{"apacheBin.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
-				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
-				{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+			expectedActions: []tcond{
+				{"apacheBin.meta_lic", "notice"},
+				{"gplWithClasspathException.meta_lic", "restricted_with_classpath_exception"},
+				{"mitLib.meta_lic", "notice"},
 			},
 		},
 		{
@@ -736,15 +631,10 @@
 				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
 				{"dependentModule.meta_lic", "mitLib.meta_lic", []string{"static"}},
 			},
-			expectedResolutions: []res{
-				{"dependentModule.meta_lic", "dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
-				{"dependentModule.meta_lic", "dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
-				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
-				{"dependentModule.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
-				{"dependentModule.meta_lic", "mitLib.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
-				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
-				{"mitLib.meta_lic", "mitLib.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
-				{"mitLib.meta_lic", "mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+			expectedActions: []tcond{
+				{"dependentModule.meta_lic", "notice|restricted_with_classpath_exception"},
+				{"gplWithClasspathException.meta_lic", "restricted_with_classpath_exception"},
+				{"mitLib.meta_lic", "notice|restricted_with_classpath_exception"},
 			},
 		},
 	}
@@ -756,9 +646,27 @@
 				t.Errorf("unexpected test data error: got %w, want no error", err)
 				return
 			}
-			expectedRs := toResolutionSet(lg, tt.expectedResolutions)
-			actualRs := ResolveTopDownConditions(lg)
-			checkSame(actualRs, expectedRs, t)
+
+			logGraph(lg, t)
+
+			ResolveTopDownConditions(lg)
+			actual := asActionList(lg)
+			sort.Sort(actual)
+			t.Logf("actual: %s", actual.String())
+
+			expected := toActionList(lg, tt.expectedActions)
+			sort.Sort(expected)
+			t.Logf("expected: %s", expected.String())
+
+			if len(actual) != len(expected) {
+				t.Errorf("unexpected number of actions: got %d, want %d", len(actual), len(expected))
+				return
+			}
+			for i := 0; i < len(actual); i++ {
+				if actual[i] != expected[i] {
+					t.Errorf("unexpected action at index %d: got %s, want %s", i, actual[i].String(), expected[i].String())
+				}
+			}
 		})
 	}
 }
diff --git a/tools/compliance/policy/resolvenotices.go b/tools/compliance/policy/resolvenotices.go
index 80b5e02..99f6d42 100644
--- a/tools/compliance/policy/resolvenotices.go
+++ b/tools/compliance/policy/resolvenotices.go
@@ -15,7 +15,7 @@
 package compliance
 
 // ResolveNotices implements the policy for notices.
-func ResolveNotices(lg *LicenseGraph) *ResolutionSet {
-	rs := ResolveTopDownConditions(lg)
-	return WalkResolutionsForCondition(lg, rs, ImpliesNotice)
+func ResolveNotices(lg *LicenseGraph) ResolutionSet {
+	ResolveTopDownConditions(lg)
+	return WalkResolutionsForCondition(lg, ImpliesNotice)
 }
diff --git a/tools/compliance/policy/resolveprivacy.go b/tools/compliance/policy/resolveprivacy.go
index dabbc62..2a7992e 100644
--- a/tools/compliance/policy/resolveprivacy.go
+++ b/tools/compliance/policy/resolveprivacy.go
@@ -15,7 +15,7 @@
 package compliance
 
 // ResolveSourcePrivacy implements the policy for source privacy.
-func ResolveSourcePrivacy(lg *LicenseGraph) *ResolutionSet {
-	rs := ResolveTopDownConditions(lg)
-	return WalkResolutionsForCondition(lg, rs, ImpliesPrivate)
+func ResolveSourcePrivacy(lg *LicenseGraph) ResolutionSet {
+	ResolveTopDownConditions(lg)
+	return WalkResolutionsForCondition(lg, ImpliesPrivate)
 }
diff --git a/tools/compliance/policy/resolveprivacy_test.go b/tools/compliance/policy/resolveprivacy_test.go
index 25772bb..2072d22 100644
--- a/tools/compliance/policy/resolveprivacy_test.go
+++ b/tools/compliance/policy/resolveprivacy_test.go
@@ -81,7 +81,7 @@
 			}
 			expectedRs := toResolutionSet(lg, tt.expectedResolutions)
 			actualRs := ResolveSourcePrivacy(lg)
-			checkSame(actualRs, expectedRs, t)
+			checkResolves(actualRs, expectedRs, t)
 		})
 	}
 }
diff --git a/tools/compliance/policy/resolveshare.go b/tools/compliance/policy/resolveshare.go
index 24efd28..9b6a8bb 100644
--- a/tools/compliance/policy/resolveshare.go
+++ b/tools/compliance/policy/resolveshare.go
@@ -15,7 +15,7 @@
 package compliance
 
 // ResolveSourceSharing implements the policy for source-sharing.
-func ResolveSourceSharing(lg *LicenseGraph) *ResolutionSet {
-	rs := ResolveTopDownConditions(lg)
-	return WalkResolutionsForCondition(lg, rs, ImpliesShared)
+func ResolveSourceSharing(lg *LicenseGraph) ResolutionSet {
+	ResolveTopDownConditions(lg)
+	return WalkResolutionsForCondition(lg, ImpliesShared)
 }
diff --git a/tools/compliance/policy/resolveshare_test.go b/tools/compliance/policy/resolveshare_test.go
index ad3630d..f73888d 100644
--- a/tools/compliance/policy/resolveshare_test.go
+++ b/tools/compliance/policy/resolveshare_test.go
@@ -291,7 +291,7 @@
 			}
 			expectedRs := toResolutionSet(lg, tt.expectedResolutions)
 			actualRs := ResolveSourceSharing(lg)
-			checkSame(actualRs, expectedRs, t)
+			checkResolves(actualRs, expectedRs, t)
 		})
 	}
 }
diff --git a/tools/compliance/policy/shareprivacyconflicts.go b/tools/compliance/policy/shareprivacyconflicts.go
index dabdff5..279e179 100644
--- a/tools/compliance/policy/shareprivacyconflicts.go
+++ b/tools/compliance/policy/shareprivacyconflicts.go
@@ -28,57 +28,37 @@
 
 // Error returns a string describing the conflict.
 func (conflict SourceSharePrivacyConflict) Error() string {
-	return fmt.Sprintf("%s %s from %s and must share from %s %s\n",
-		conflict.SourceNode.name,
-		conflict.PrivacyCondition.name, conflict.PrivacyCondition.origin.name,
-		conflict.ShareCondition.name, conflict.ShareCondition.origin.name)
+	return fmt.Sprintf("%s %s and must share from %s condition\n", conflict.SourceNode.name,
+		conflict.PrivacyCondition.Name(), conflict.ShareCondition.Name())
 }
 
 // IsEqualTo returns true when `conflict` and `other` describe the same conflict.
 func (conflict SourceSharePrivacyConflict) IsEqualTo(other SourceSharePrivacyConflict) bool {
 	return conflict.SourceNode.name == other.SourceNode.name &&
-		conflict.ShareCondition.name == other.ShareCondition.name &&
-		conflict.ShareCondition.origin.name == other.ShareCondition.origin.name &&
-		conflict.PrivacyCondition.name == other.PrivacyCondition.name &&
-		conflict.PrivacyCondition.origin.name == other.PrivacyCondition.origin.name
+		conflict.ShareCondition == other.ShareCondition &&
+		conflict.PrivacyCondition == other.PrivacyCondition
 }
 
 // ConflictingSharedPrivateSource lists all of the targets where conflicting conditions to
 // share the source and to keep the source private apply to the target.
 func ConflictingSharedPrivateSource(lg *LicenseGraph) []SourceSharePrivacyConflict {
-	// shareSource is the set of all source-sharing resolutions.
-	shareSource := ResolveSourceSharing(lg)
-	if shareSource.IsEmpty() {
-		return []SourceSharePrivacyConflict{}
-	}
 
-	// privateSource is the set of all source privacy resolutions.
-	privateSource := ResolveSourcePrivacy(lg)
-	if privateSource.IsEmpty() {
-		return []SourceSharePrivacyConflict{}
-	}
-
+	ResolveTopDownConditions(lg)
 	// combined is the combination of source-sharing and source privacy.
-	combined := JoinResolutionSets(shareSource, privateSource)
+	combined := WalkActionsForCondition(lg, ImpliesShared.Union(ImpliesPrivate))
 
 	// size is the size of the result
 	size := 0
-	for _, actsOn := range combined.ActsOn() {
-		rl := combined.ResolutionsByActsOn(actsOn)
-		size += rl.CountConditionsByName(ImpliesShared) * rl.CountConditionsByName(ImpliesPrivate)
+	for _, cs := range combined {
+		size += cs.Intersection(ImpliesShared).Len() * cs.Intersection(ImpliesPrivate).Len()
 	}
 	if size == 0 {
-		return []SourceSharePrivacyConflict{}
+		return nil
 	}
 	result := make([]SourceSharePrivacyConflict, 0, size)
-	for _, actsOn := range combined.ActsOn() {
-		rl := combined.ResolutionsByActsOn(actsOn)
-		if len(rl) == 0 {
-			continue
-		}
-
-		pconditions := rl.ByName(ImpliesPrivate).AllConditions().AsList()
-		ssconditions := rl.ByName(ImpliesShared).AllConditions().AsList()
+	for actsOn, cs := range combined {
+		pconditions := cs.Intersection(ImpliesPrivate).AsList()
+		ssconditions := cs.Intersection(ImpliesShared).AsList()
 
 		// report all conflicting condition combinations
 		for _, p := range pconditions {
diff --git a/tools/compliance/policy/shareprivacyconflicts_test.go b/tools/compliance/policy/shareprivacyconflicts_test.go
index 162c1fe..ad3f3f4 100644
--- a/tools/compliance/policy/shareprivacyconflicts_test.go
+++ b/tools/compliance/policy/shareprivacyconflicts_test.go
@@ -33,19 +33,13 @@
 // Less returns true when the `i`th element is lexicographically less than
 // the `j`th element.
 func (l byConflict) Less(i, j int) bool {
-	if l[i].SourceNode.name == l[j].SourceNode.name {
-		if l[i].ShareCondition.origin.name == l[j].ShareCondition.origin.name {
-			if l[i].ShareCondition.name == l[j].ShareCondition.name {
-				if l[i].PrivacyCondition.origin.name == l[j].PrivacyCondition.origin.name {
-					return l[i].PrivacyCondition.name < l[j].PrivacyCondition.name
-				}
-				return l[i].PrivacyCondition.origin.name < l[j].PrivacyCondition.origin.name
-			}
-			return l[i].ShareCondition.name < l[j].ShareCondition.name
+	if l[i].SourceNode.Name() == l[j].SourceNode.Name() {
+		if l[i].ShareCondition.Name() == l[j].ShareCondition.Name() {
+			return l[i].PrivacyCondition.Name() < l[j].PrivacyCondition.Name()
 		}
-		return l[i].ShareCondition.origin.name < l[j].ShareCondition.origin.name
+		return l[i].ShareCondition.Name() < l[j].ShareCondition.Name()
 	}
-	return l[i].SourceNode.name < l[j].SourceNode.name
+	return l[i].SourceNode.Name() < l[j].SourceNode.Name()
 }
 
 func TestConflictingSharedPrivateSource(t *testing.T) {
diff --git a/tools/compliance/policy/shipped.go b/tools/compliance/policy/shipped.go
index 74eb343..75c8399 100644
--- a/tools/compliance/policy/shipped.go
+++ b/tools/compliance/policy/shipped.go
@@ -24,18 +24,18 @@
 		return shipped
 	}
 
-	tset := make(map[*TargetNode]bool)
+	tset := make(map[*TargetNode]struct{})
 
-	WalkTopDown(lg, func(lg *LicenseGraph, tn *TargetNode, path TargetEdgePath) bool {
+	WalkTopDown(NoEdgeContext{}, lg, func(lg *LicenseGraph, tn *TargetNode, path TargetEdgePath) bool {
 		if _, alreadyWalked := tset[tn]; alreadyWalked {
 			return false
 		}
 		if len(path) > 0 {
-			if !edgeIsDerivation(path[len(path)-1]) {
+			if !edgeIsDerivation(path[len(path)-1].edge) {
 				return false
 			}
 		}
-		tset[tn] = true
+		tset[tn] = struct{}{}
 		return true
 	})
 
diff --git a/tools/compliance/policy/shipped_test.go b/tools/compliance/policy/shipped_test.go
index 53a8469..718e56f 100644
--- a/tools/compliance/policy/shipped_test.go
+++ b/tools/compliance/policy/shipped_test.go
@@ -17,6 +17,7 @@
 import (
 	"bytes"
 	"sort"
+	"strings"
 	"testing"
 )
 
@@ -110,19 +111,31 @@
 				t.Errorf("unexpected test data error: got %w, want no error", err)
 				return
 			}
+			t.Logf("graph:")
+			for _, edge := range lg.Edges() {
+				t.Logf("  %s", edge.String())
+			}
 			expectedNodes := append([]string{}, tt.expectedNodes...)
-			actualNodes := ShippedNodes(lg).Names()
+			nodeset := ShippedNodes(lg)
+			t.Logf("shipped node set: %s", nodeset.String())
+
+			actualNodes := nodeset.Names()
+			t.Logf("shipped nodes: [%s]", strings.Join(actualNodes, ", "))
+
 			sort.Strings(expectedNodes)
 			sort.Strings(actualNodes)
+
+			t.Logf("sorted nodes: [%s]", strings.Join(actualNodes, ", "))
+			t.Logf("expected nodes: [%s]", strings.Join(expectedNodes, ", "))
                         if len(expectedNodes) != len(actualNodes) {
-				t.Errorf("unexpected number of shipped nodes: got %v with %d nodes, want %v with %d nodes",
-					actualNodes, len(actualNodes), expectedNodes, len(expectedNodes))
+				t.Errorf("unexpected number of shipped nodes: %d nodes, want %d nodes",
+					len(actualNodes), len(expectedNodes))
 				return
 			}
 			for i := 0; i < len(actualNodes); i++ {
 				if expectedNodes[i] != actualNodes[i] {
-					t.Errorf("unexpected node at index %d: got %q in %v, want %q in %v",
-						i, actualNodes[i], actualNodes, expectedNodes[i], expectedNodes)
+					t.Errorf("unexpected node at index %d: got %q, want %q",
+						i, actualNodes[i], expectedNodes[i])
 				}
 			}
 		})
diff --git a/tools/compliance/policy/walk.go b/tools/compliance/policy/walk.go
index 8b6737d..3e73088 100644
--- a/tools/compliance/policy/walk.go
+++ b/tools/compliance/policy/walk.go
@@ -14,27 +14,60 @@
 
 package compliance
 
+// EdgeContextProvider is an interface for injecting edge-specific context
+// into walk paths.
+type EdgeContextProvider interface {
+	// Context returns the context for `edge` when added to `path`.
+	Context(lg *LicenseGraph, path TargetEdgePath, edge *TargetEdge) interface{}
+}
+
+// NoEdgeContext implements EdgeContextProvider for walks that use no context.
+type NoEdgeContext struct{}
+
+// Context returns nil.
+func (ctx NoEdgeContext) Context(lg *LicenseGraph, path TargetEdgePath, edge *TargetEdge) interface{} {
+	return nil
+}
+
+// ApplicableConditionsContext provides the subset of conditions in `universe`
+// that apply to each edge in a path.
+type ApplicableConditionsContext struct {
+	universe LicenseConditionSet
+}
+
+// Context returns the LicenseConditionSet applicable to the edge.
+func (ctx ApplicableConditionsContext) Context(lg *LicenseGraph, path TargetEdgePath, edge *TargetEdge) interface{} {
+	universe := ctx.universe
+	if len(path) > 0 {
+		universe = path[len(path)-1].ctx.(LicenseConditionSet)
+	}
+	return conditionsAttachingAcrossEdge(lg, edge, universe)
+}
+
 // VisitNode is called for each root and for each walked dependency node by
 // WalkTopDown. When VisitNode returns true, WalkTopDown will proceed to walk
 // down the dependences of the node
-type VisitNode func(*LicenseGraph, *TargetNode, TargetEdgePath) bool
+type VisitNode func(lg *LicenseGraph, target *TargetNode, path TargetEdgePath) bool
 
 // WalkTopDown does a top-down walk of `lg` calling `visit` and descending
 // into depenencies when `visit` returns true.
-func WalkTopDown(lg *LicenseGraph, visit VisitNode) {
+func WalkTopDown(ctx EdgeContextProvider, lg *LicenseGraph, visit VisitNode) {
 	path := NewTargetEdgePath(32)
 
-	// must be indexed for fast lookup
-	lg.indexForward()
-
-	var walk func(f string)
-	walk = func(f string) {
-		visitChildren := visit(lg, lg.targets[f], *path)
+	var walk func(fnode *TargetNode)
+	walk = func(fnode *TargetNode) {
+		visitChildren := visit(lg, fnode, *path)
 		if !visitChildren {
 			return
 		}
-		for _, edge := range lg.index[f] {
-			path.Push(TargetEdge{lg, edge})
+		for _, edge := range fnode.edges {
+			var edgeContext interface{}
+			if ctx == nil {
+				edgeContext = nil
+			} else {
+				edgeContext = ctx.Context(lg, *path, edge)
+			}
+			path.Push(edge, edgeContext)
 			walk(edge.dependency)
 			path.Pop()
 		}
@@ -42,35 +75,164 @@
 
 	for _, r := range lg.rootFiles {
 		path.Clear()
-		walk(r)
+		walk(lg.targets[r])
 	}
 }
 
+// resolutionKey identifies results from walking a specific target for a
+// specific set of conditions.
+type resolutionKey struct {
+	target *TargetNode
+	cs LicenseConditionSet
+}
+
 // WalkResolutionsForCondition performs a top-down walk of the LicenseGraph
-// resolving all distributed works for condition `names`.
-func WalkResolutionsForCondition(lg *LicenseGraph, rs *ResolutionSet, names ConditionNames) *ResolutionSet {
+// resolving all distributed works for `conditions`.
+func WalkResolutionsForCondition(lg *LicenseGraph, conditions LicenseConditionSet) ResolutionSet {
 	shipped := ShippedNodes(lg)
 
 	// rmap maps 'attachesTo' targets to the `actsOn` targets and applicable conditions
-	//
-	// rmap is the resulting ResolutionSet
-	rmap := make(map[*TargetNode]actionSet)
+	rmap := make(map[resolutionKey]ActionSet)
 
-	WalkTopDown(lg, func(lg *LicenseGraph, tn *TargetNode, _ TargetEdgePath) bool {
-		if _, ok := rmap[tn]; ok {
+	// cmap identifies previously walked target/condition pairs.
+	cmap := make(map[resolutionKey]struct{})
+
+	// result accumulates the resolutions to return.
+	result := make(ResolutionSet)
+	WalkTopDown(ApplicableConditionsContext{conditions}, lg, func(lg *LicenseGraph, tn *TargetNode, path TargetEdgePath) bool {
+		universe := conditions
+		if len(path) > 0 {
+			universe = path[len(path)-1].ctx.(LicenseConditionSet)
+		}
+
+		if universe.IsEmpty() {
+			return false
+		}
+		key := resolutionKey{tn, universe}
+
+		if _, alreadyWalked := cmap[key]; alreadyWalked {
+			pure := true
+			for _, p := range path {
+				target := p.Target()
+				tkey := resolutionKey{target, universe}
+				if _, ok := rmap[tkey]; !ok {
+					rmap[tkey] = make(ActionSet)
+				}
+				// attach prior walk outcome to ancestor
+				for actsOn, cs := range rmap[key] {
+					rmap[tkey][actsOn] = cs
+				}
+				// if prior walk produced results, copy results
+				// to ancestor.
+				if _, ok := result[tn]; ok && pure {
+					if _, ok := result[target]; !ok {
+						result[target] = make(ActionSet)
+					}
+					for actsOn, cs := range result[tn] {
+						result[target][actsOn] = cs
+					}
+					pure = target.IsContainer()
+				}
+			}
+			// if all ancestors are pure aggregates, attach
+			// matching prior walk conditions to self. Prior walk
+			// will not have done so if any ancestor was not an
+			// aggregate.
+			if pure {
+				match := rmap[key][tn].Intersection(universe)
+				if !match.IsEmpty() {
+					if _, ok := result[tn]; !ok {
+						result[tn] = make(ActionSet)
+					}
+					result[tn][tn] = match
+				}
+			}
+			return false
+		}
+		// no need to walk node or dependencies if not shipped
+		if !shipped.Contains(tn) {
+			return false
+		}
+		if _, ok := rmap[key]; !ok {
+			rmap[key] = make(ActionSet)
+		}
+		// add self to walk outcome
+		rmap[key][tn] = tn.resolution
+		cmap[key] = struct{}{}
+		cs := tn.resolution
+		if !cs.IsEmpty() {
+			cs = cs.Intersection(universe)
+			pure := true
+			for _, p := range path {
+				target := p.Target()
+				tkey := resolutionKey{target, universe}
+				if _, ok := rmap[tkey]; !ok {
+					rmap[tkey] = make(ActionSet)
+				}
+				// copy current node's action into ancestor
+				rmap[tkey][tn] = tn.resolution
+				// conditionally put matching conditions into
+				// result
+				if pure && !cs.IsEmpty() {
+					if _, ok := result[target]; !ok {
+						result[target] = make(ActionSet)
+					}
+					result[target][tn] = cs
+					pure = target.IsContainer()
+				}
+			}
+			// if all ancestors are pure aggregates, attach
+			// matching conditions to self.
+			if pure && !cs.IsEmpty() {
+				if _, ok := result[tn]; !ok {
+					result[tn] = make(ActionSet)
+				}
+				result[tn][tn] = cs
+			}
+		}
+		return true
+	})
+
+	return result
+}
+
+// WalkActionsForCondition performs a top-down walk of the LicenseGraph
+// resolving all distributed works for `conditions`.
+func WalkActionsForCondition(lg *LicenseGraph, conditions LicenseConditionSet) ActionSet {
+	shipped := ShippedNodes(lg)
+
+	// cmap identifies previously walked target/condition pairs.
+	cmap := make(map[resolutionKey]struct{})
+
+	// amap maps 'actsOn' targets to the applicable conditions
+	//
+	// amap is the resulting ActionSet
+	amap := make(ActionSet)
+	WalkTopDown(ApplicableConditionsContext{conditions}, lg, func(lg *LicenseGraph, tn *TargetNode, path TargetEdgePath) bool {
+		universe := conditions
+		if len(path) > 0 {
+			universe = path[len(path)-1].ctx.(LicenseConditionSet)
+		}
+		if universe.IsEmpty() {
+			return false
+		}
+		key := resolutionKey{tn, universe}
+		if _, ok := cmap[key]; ok {
 			return false
 		}
 		if !shipped.Contains(tn) {
 			return false
 		}
-		if as, ok := rs.resolutions[tn]; ok {
-			fas := as.byActsOn(shipped).byName(names)
-			if !fas.isEmpty() {
-				rmap[tn] = fas
+		cs := universe.Intersection(tn.resolution)
+		if !cs.IsEmpty() {
+			if _, ok := amap[tn]; ok {
+				amap[tn] = cs
+			} else {
+				amap[tn] = amap[tn].Union(cs)
 			}
 		}
-		return tn.IsContainer() // descend into containers
+		return true
 	})
 
-	return &ResolutionSet{rmap}
+	return amap
 }
diff --git a/tools/compliance/policy/walk_test.go b/tools/compliance/policy/walk_test.go
index 07710aa..a2ec6e7 100644
--- a/tools/compliance/policy/walk_test.go
+++ b/tools/compliance/policy/walk_test.go
@@ -22,7 +22,7 @@
 func TestWalkResolutionsForCondition(t *testing.T) {
 	tests := []struct {
 		name                string
-		condition           ConditionNames
+		condition           LicenseConditionSet
 		roots               []string
 		edges               []annotated
 		expectedResolutions []res
@@ -624,8 +624,617 @@
 				return
 			}
 			expectedRs := toResolutionSet(lg, tt.expectedResolutions)
-			actualRs := WalkResolutionsForCondition(lg, ResolveTopDownConditions(lg), tt.condition)
-			checkSame(actualRs, expectedRs, t)
+			ResolveTopDownConditions(lg)
+			actualRs := WalkResolutionsForCondition(lg, tt.condition)
+			checkResolves(actualRs, expectedRs, t)
+		})
+	}
+}
+
+func TestWalkActionsForCondition(t *testing.T) {
+	tests := []struct {
+		name            string
+		condition       LicenseConditionSet
+		roots           []string
+		edges           []annotated
+		expectedActions []act
+	}{
+		{
+			name:      "firstparty",
+			condition: ImpliesNotice,
+			roots:     []string{"apacheBin.meta_lic"},
+			edges: []annotated{
+				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+			},
+			expectedActions: []act{
+				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+				{"apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+			},
+		},
+		{
+			name:      "notice",
+			condition: ImpliesNotice,
+			roots:     []string{"mitBin.meta_lic"},
+			edges: []annotated{
+				{"mitBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+			},
+			expectedActions: []act{
+				{"mitBin.meta_lic", "mitBin.meta_lic", "notice"},
+				{"mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+			},
+		},
+		{
+			name:      "fponlgplnotice",
+			condition: ImpliesNotice,
+			roots:     []string{"apacheBin.meta_lic"},
+			edges: []annotated{
+				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"static"}},
+			},
+			expectedActions: []act{
+				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+				{"apacheBin.meta_lic", "lgplLib.meta_lic", "restricted"},
+				{"lgplLib.meta_lic", "lgplLib.meta_lic", "restricted"},
+			},
+		},
+		{
+			name:      "fponlgpldynamicnotice",
+			condition: ImpliesNotice,
+			roots:     []string{"apacheBin.meta_lic"},
+			edges: []annotated{
+				{"apacheBin.meta_lic", "lgplLib.meta_lic", []string{"dynamic"}},
+			},
+			expectedActions: []act{
+				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+			},
+		},
+		{
+			name:      "independentmodulenotice",
+			condition: ImpliesNotice,
+			roots:     []string{"apacheBin.meta_lic"},
+			edges: []annotated{
+				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+			},
+			expectedActions: []act{
+				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+			},
+		},
+		{
+			name:      "independentmodulerestricted",
+			condition: ImpliesRestricted,
+			roots:     []string{"apacheBin.meta_lic"},
+			edges: []annotated{
+				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+			},
+			expectedActions: []act{},
+		},
+		{
+			name:      "independentmodulestaticnotice",
+			condition: ImpliesNotice,
+			roots:     []string{"apacheBin.meta_lic"},
+			edges: []annotated{
+				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
+			},
+			expectedActions: []act{
+				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+			},
+		},
+		{
+			name:      "independentmodulestaticrestricted",
+			condition: ImpliesRestricted,
+			roots:     []string{"apacheBin.meta_lic"},
+			edges: []annotated{
+				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", []string{"static"}},
+			},
+			expectedActions: []act{
+				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+			},
+		},
+		{
+			name:      "dependentmodulenotice",
+			condition: ImpliesNotice,
+			roots:     []string{"dependentModule.meta_lic"},
+			edges: []annotated{
+				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+			},
+			expectedActions: []act{
+				{"dependentModule.meta_lic", "dependentModule.meta_lic", "notice"},
+				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+			},
+		},
+		{
+			name:      "dependentmodulerestricted",
+			condition: ImpliesRestricted,
+			roots:     []string{"dependentModule.meta_lic"},
+			edges: []annotated{
+				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", []string{"dynamic"}},
+			},
+			expectedActions: []act{
+				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+			},
+		},
+		{
+			name:      "lgplonfpnotice",
+			condition: ImpliesNotice,
+			roots:     []string{"lgplBin.meta_lic"},
+			edges: []annotated{
+				{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+			},
+			expectedActions: []act{
+				{"lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
+				{"apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+				{"apacheLib.meta_lic", "lgplBin.meta_lic", "restricted"},
+			},
+		},
+		{
+			name:      "lgplonfprestricted",
+			condition: ImpliesRestricted,
+			roots:     []string{"lgplBin.meta_lic"},
+			edges: []annotated{
+				{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+			},
+			expectedActions: []act{
+				{"lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
+				{"apacheLib.meta_lic", "lgplBin.meta_lic", "restricted"},
+			},
+		},
+		{
+			name:      "lgplonfpdynamicnotice",
+			condition: ImpliesNotice,
+			roots:     []string{"lgplBin.meta_lic"},
+			edges: []annotated{
+				{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+			},
+			expectedActions: []act{
+				{"lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
+			},
+		},
+		{
+			name:      "lgplonfpdynamicrestricted",
+			condition: ImpliesRestricted,
+			roots:     []string{"lgplBin.meta_lic"},
+			edges: []annotated{
+				{"lgplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+			},
+			expectedActions: []act{
+				{"lgplBin.meta_lic", "lgplBin.meta_lic", "restricted"},
+			},
+		},
+		{
+			name:      "gplonfpnotice",
+			condition: ImpliesNotice,
+			roots:     []string{"gplBin.meta_lic"},
+			edges: []annotated{
+				{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+			},
+			expectedActions: []act{
+				{"gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
+				{"apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+				{"apacheLib.meta_lic", "gplBin.meta_lic", "restricted"},
+			},
+		},
+		{
+			name:      "gplonfprestricted",
+			condition: ImpliesRestricted,
+			roots:     []string{"gplBin.meta_lic"},
+			edges: []annotated{
+				{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+			},
+			expectedActions: []act{
+				{"gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
+				{"apacheLib.meta_lic", "gplBin.meta_lic", "restricted"},
+			},
+		},
+		{
+			name:      "gplcontainernotice",
+			condition: ImpliesNotice,
+			roots:     []string{"gplContainer.meta_lic"},
+			edges: []annotated{
+				{"gplContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+			},
+			expectedActions: []act{
+				{"gplContainer.meta_lic", "gplContainer.meta_lic", "restricted"},
+				{"apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+				{"apacheLib.meta_lic", "gplContainer.meta_lic", "restricted"},
+				{"apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+				{"apacheLib.meta_lic", "gplContainer.meta_lic", "restricted"},
+			},
+		},
+		{
+			name:      "gplcontainerrestricted",
+			condition: ImpliesRestricted,
+			roots:     []string{"gplContainer.meta_lic"},
+			edges: []annotated{
+				{"gplContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+			},
+			expectedActions: []act{
+				{"gplContainer.meta_lic", "gplContainer.meta_lic", "restricted"},
+				{"apacheLib.meta_lic", "gplContainer.meta_lic", "restricted"},
+				{"apacheLib.meta_lic", "gplContainer.meta_lic", "restricted"},
+			},
+		},
+		{
+			name:      "gploncontainernotice",
+			condition: ImpliesNotice,
+			roots:     []string{"apacheContainer.meta_lic"},
+			edges: []annotated{
+				{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+				{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
+			},
+			expectedActions: []act{
+				{"apacheContainer.meta_lic", "apacheContainer.meta_lic", "notice"},
+				{"apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
+				{"apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+				{"apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+				{"gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+			},
+		},
+		{
+			name:      "gploncontainerrestricted",
+			condition: ImpliesRestricted,
+			roots:     []string{"apacheContainer.meta_lic"},
+			edges: []annotated{
+				{"apacheContainer.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+				{"apacheContainer.meta_lic", "gplLib.meta_lic", []string{"static"}},
+			},
+			expectedActions: []act{
+				{"apacheContainer.meta_lic", "gplLib.meta_lic", "restricted"},
+				{"gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+			},
+		},
+		{
+			name:      "gplonbinnotice",
+			condition: ImpliesNotice,
+			roots:     []string{"apacheBin.meta_lic"},
+			edges: []annotated{
+				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
+			},
+			expectedActions: []act{
+				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+				{"apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+				{"apacheLib.meta_lic", "apacheLib.meta_lic", "notice"},
+				{"apacheLib.meta_lic", "gplLib.meta_lic", "restricted"},
+				{"gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+			},
+		},
+		{
+			name:      "gplonbinrestricted",
+			condition: ImpliesRestricted,
+			roots:     []string{"apacheBin.meta_lic"},
+			edges: []annotated{
+				{"apacheBin.meta_lic", "apacheLib.meta_lic", []string{"static"}},
+				{"apacheBin.meta_lic", "gplLib.meta_lic", []string{"static"}},
+			},
+			expectedActions: []act{
+				{"apacheBin.meta_lic", "gplLib.meta_lic", "restricted"},
+				{"apacheLib.meta_lic", "gplLib.meta_lic", "restricted"},
+				{"gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+			},
+		},
+		{
+			name:      "gplonfpdynamicnotice",
+			condition: ImpliesNotice,
+			roots:     []string{"gplBin.meta_lic"},
+			edges: []annotated{
+				{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+			},
+			expectedActions: []act{
+				{"gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
+			},
+		},
+		{
+			name:      "gplonfpdynamicrestricted",
+			condition: ImpliesRestricted,
+			roots:     []string{"gplBin.meta_lic"},
+			edges: []annotated{
+				{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+			},
+			expectedActions: []act{
+				{"gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
+			},
+		},
+		{
+			name:      "gplonfpdynamicrestrictedshipped",
+			condition: ImpliesRestricted,
+			roots:     []string{"gplBin.meta_lic", "apacheLib.meta_lic"},
+			edges: []annotated{
+				{"gplBin.meta_lic", "apacheLib.meta_lic", []string{"dynamic"}},
+			},
+			expectedActions: []act{
+				{"gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
+				{"apacheLib.meta_lic", "gplBin.meta_lic", "restricted"},
+			},
+		},
+		{
+			name:      "independentmodulereversenotice",
+			condition: ImpliesNotice,
+			roots:     []string{"gplWithClasspathException.meta_lic"},
+			edges: []annotated{
+				{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}},
+			},
+			expectedActions: []act{
+				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+			},
+		},
+		{
+			name:      "independentmodulereverserestricted",
+			condition: ImpliesRestricted,
+			roots:     []string{"gplWithClasspathException.meta_lic"},
+			edges: []annotated{
+				{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}},
+			},
+			expectedActions: []act{
+				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+			},
+		},
+		{
+			name:      "independentmodulereverserestrictedshipped",
+			condition: ImpliesRestricted,
+			roots:     []string{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic"},
+			edges: []annotated{
+				{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"dynamic"}},
+			},
+			expectedActions: []act{
+				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+			},
+		},
+		{
+			name:      "independentmodulereversestaticnotice",
+			condition: ImpliesNotice,
+			roots:     []string{"gplWithClasspathException.meta_lic"},
+			edges: []annotated{
+				{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+			},
+			expectedActions: []act{
+				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+				{"apacheBin.meta_lic", "apacheBin.meta_lic", "notice"},
+				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+			},
+		},
+		{
+			name:      "independentmodulereversestaticrestricted",
+			condition: ImpliesRestricted,
+			roots:     []string{"gplWithClasspathException.meta_lic"},
+			edges: []annotated{
+				{"gplWithClasspathException.meta_lic", "apacheBin.meta_lic", []string{"static"}},
+			},
+			expectedActions: []act{
+				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+				{"apacheBin.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+			},
+		},
+		{
+			name:      "dependentmodulereversenotice",
+			condition: ImpliesNotice,
+			roots:     []string{"gplWithClasspathException.meta_lic"},
+			edges: []annotated{
+				{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
+			},
+			expectedActions: []act{
+				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+			},
+		},
+		{
+			name:      "dependentmodulereverserestricted",
+			condition: ImpliesRestricted,
+			roots:     []string{"gplWithClasspathException.meta_lic"},
+			edges: []annotated{
+				{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
+			},
+			expectedActions: []act{
+				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+			},
+		},
+		{
+			name:      "dependentmodulereverserestrictedshipped",
+			condition: ImpliesRestricted,
+			roots:     []string{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic"},
+			edges: []annotated{
+				{"gplWithClasspathException.meta_lic", "dependentModule.meta_lic", []string{"dynamic"}},
+			},
+			expectedActions: []act{
+				{"gplWithClasspathException.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+				{"dependentModule.meta_lic", "gplWithClasspathException.meta_lic", "restricted"},
+			},
+		},
+		{
+			name:      "ponrnotice",
+			condition: ImpliesNotice,
+			roots:     []string{"proprietary.meta_lic"},
+			edges: []annotated{
+				{"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
+			},
+			expectedActions: []act{
+				{"proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
+				{"proprietary.meta_lic", "gplLib.meta_lic", "restricted"},
+				{"gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+			},
+		},
+		{
+			name:      "ponrrestricted",
+			condition: ImpliesRestricted,
+			roots:     []string{"proprietary.meta_lic"},
+			edges: []annotated{
+				{"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
+			},
+			expectedActions: []act{
+				{"gplLib.meta_lic", "gplLib.meta_lic", "restricted"},
+				{"proprietary.meta_lic", "gplLib.meta_lic", "restricted"},
+			},
+		},
+		{
+			name:      "ponrproprietary",
+			condition: ImpliesProprietary,
+			roots:     []string{"proprietary.meta_lic"},
+			edges: []annotated{
+				{"proprietary.meta_lic", "gplLib.meta_lic", []string{"static"}},
+			},
+			expectedActions: []act{
+				{"proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
+			},
+		},
+		{
+			name:      "ronpnotice",
+			condition: ImpliesNotice,
+			roots:     []string{"gplBin.meta_lic"},
+			edges: []annotated{
+				{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
+			},
+			expectedActions: []act{
+				{"gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
+				{"proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
+				{"proprietary.meta_lic", "gplBin.meta_lic", "restricted"},
+			},
+		},
+		{
+			name:      "ronprestricted",
+			condition: ImpliesRestricted,
+			roots:     []string{"gplBin.meta_lic"},
+			edges: []annotated{
+				{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
+			},
+			expectedActions: []act{
+				{"gplBin.meta_lic", "gplBin.meta_lic", "restricted"},
+				{"proprietary.meta_lic", "gplBin.meta_lic", "restricted"},
+			},
+		},
+		{
+			name:      "ronpproprietary",
+			condition: ImpliesProprietary,
+			roots:     []string{"gplBin.meta_lic"},
+			edges: []annotated{
+				{"gplBin.meta_lic", "proprietary.meta_lic", []string{"static"}},
+			},
+			expectedActions: []act{
+				{"proprietary.meta_lic", "proprietary.meta_lic", "proprietary"},
+			},
+		},
+		{
+			name:      "noticeonb_e_onotice",
+			condition: ImpliesNotice,
+			roots:     []string{"mitBin.meta_lic"},
+			edges: []annotated{
+				{"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
+			},
+			expectedActions: []act{
+				{"mitBin.meta_lic", "mitBin.meta_lic", "notice"},
+				{"by_exception.meta_lic", "by_exception.meta_lic", "by_exception_only"},
+			},
+		},
+		{
+			name:      "noticeonb_e_orestricted",
+			condition: ImpliesRestricted,
+			roots:     []string{"mitBin.meta_lic"},
+			edges: []annotated{
+				{"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
+			},
+			expectedActions: []act{},
+		},
+		{
+			name:      "noticeonb_e_ob_e_o",
+			condition: ImpliesByExceptionOnly,
+			roots:     []string{"mitBin.meta_lic"},
+			edges: []annotated{
+				{"mitBin.meta_lic", "by_exception.meta_lic", []string{"static"}},
+			},
+			expectedActions: []act{
+				{"by_exception.meta_lic", "by_exception.meta_lic", "by_exception_only"},
+			},
+		},
+		{
+			name:      "b_e_oonnoticenotice",
+			condition: ImpliesNotice,
+			roots:     []string{"by_exception.meta_lic"},
+			edges: []annotated{
+				{"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
+			},
+			expectedActions: []act{
+				{"by_exception.meta_lic", "by_exception.meta_lic", "by_exception_only"},
+				{"mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+			},
+		},
+		{
+			name:      "b_e_oonnoticerestricted",
+			condition: ImpliesRestricted,
+			roots:     []string{"by_exception.meta_lic"},
+			edges: []annotated{
+				{"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
+			},
+			expectedActions: []act{},
+		},
+		{
+			name:      "b_e_oonnoticeb_e_o",
+			condition: ImpliesByExceptionOnly,
+			roots:     []string{"by_exception.meta_lic"},
+			edges: []annotated{
+				{"by_exception.meta_lic", "mitLib.meta_lic", []string{"static"}},
+			},
+			expectedActions: []act{
+				{"by_exception.meta_lic", "by_exception.meta_lic", "by_exception_only"},
+			},
+		},
+		{
+			name:      "noticeonrecipnotice",
+			condition: ImpliesNotice,
+			roots:     []string{"mitBin.meta_lic"},
+			edges: []annotated{
+				{"mitBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
+			},
+			expectedActions: []act{
+				{"mitBin.meta_lic", "mitBin.meta_lic", "notice"},
+				{"mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
+			},
+		},
+		{
+			name:      "noticeonreciprecip",
+			condition: ImpliesReciprocal,
+			roots:     []string{"mitBin.meta_lic"},
+			edges: []annotated{
+				{"mitBin.meta_lic", "mplLib.meta_lic", []string{"static"}},
+			},
+			expectedActions: []act{
+				{"mplLib.meta_lic", "mplLib.meta_lic", "reciprocal"},
+			},
+		},
+		{
+			name:      "reciponnoticenotice",
+			condition: ImpliesNotice,
+			roots:     []string{"mplBin.meta_lic"},
+			edges: []annotated{
+				{"mplBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+			},
+			expectedActions: []act{
+				{"mplBin.meta_lic", "mplBin.meta_lic", "reciprocal"},
+				{"mitLib.meta_lic", "mitLib.meta_lic", "notice"},
+			},
+		},
+		{
+			name:      "reciponnoticerecip",
+			condition: ImpliesReciprocal,
+			roots:     []string{"mplBin.meta_lic"},
+			edges: []annotated{
+				{"mplBin.meta_lic", "mitLib.meta_lic", []string{"static"}},
+			},
+			expectedActions: []act{
+				{"mplBin.meta_lic", "mplBin.meta_lic", "reciprocal"},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			stderr := &bytes.Buffer{}
+			lg, err := toGraph(stderr, tt.roots, tt.edges)
+			if err != nil {
+				t.Errorf("unexpected test data error: got %w, want no error", err)
+				return
+			}
+			expectedAs := toActionSet(lg, tt.expectedActions)
+			ResolveTopDownConditions(lg)
+			actualAs := WalkActionsForCondition(lg, tt.condition)
+			checkResolvesActions(lg, actualAs, expectedAs, t)
 		})
 	}
 }
diff --git a/tools/compliance/readgraph.go b/tools/compliance/readgraph.go
index 0b5ebfe..c809a96 100644
--- a/tools/compliance/readgraph.go
+++ b/tools/compliance/readgraph.go
@@ -39,16 +39,13 @@
 	// target contains the parsed metadata or nil if an error
 	target *TargetNode
 
-	// edges contains the parsed dependencies
-	edges []*dependencyEdge
-
 	// err is nil unless an error occurs
 	err error
 }
 
 // receiver coordinates the tasks for reading and parsing license metadata files.
 type receiver struct {
-	// lg accumulates the read metadata and becomes the final resulting LicensGraph.
+	// lg accumulates the read metadata and becomes the final resulting LicenseGraph.
 	lg *LicenseGraph
 
 	// rootFS locates the root of the file system from which to read the files.
@@ -80,7 +77,7 @@
 
 	lg := newLicenseGraph()
 	for _, f := range files {
-		if strings.HasSuffix(f, ".meta_lic") {
+		if strings.HasSuffix(f, "meta_lic") {
 			lg.rootFiles = append(lg.rootFiles, f)
 		} else {
 			lg.rootFiles = append(lg.rootFiles, f+".meta_lic")
@@ -138,10 +135,7 @@
 
 				// record the parsed metadata (guarded by mutex)
 				recv.lg.mu.Lock()
-				recv.lg.targets[r.file] = r.target
-				if len(r.edges) > 0 {
-					recv.lg.edges = append(recv.lg.edges, r.edges...)
-				}
+				lg.targets[r.target.name] = r.target
 				recv.lg.mu.Unlock()
 			} else {
 				// finished -- nil the results channel
@@ -150,6 +144,21 @@
 		}
 	}
 
+	if lg != nil {
+		esize := 0
+		for _, tn := range lg.targets {
+			esize += len(tn.proto.Deps)
+		}
+		lg.edges = make(TargetEdgeList, 0, esize)
+		for _, tn := range lg.targets {
+			tn.licenseConditions = LicenseConditionSetFromNames(tn, tn.proto.LicenseConditions...)
+			err = addDependencies(lg, tn)
+			if err != nil {
+				return nil, fmt.Errorf("error indexing dependencies for %q: %w", tn.name, err)
+			}
+			tn.proto.Deps = []*license_metadata_proto.AnnotatedDependency{}
+		}
+	}
 	return lg, err
 
 }
@@ -158,42 +167,48 @@
 type targetNode struct {
 	proto license_metadata_proto.LicenseMetadata
 
-	// name is the path to the metadata file
+	// name is the path to the metadata file.
 	name string
-}
 
-// dependencyEdge describes a single edge in the license graph.
-type dependencyEdge struct {
-	// target identifies the target node being built and/or installed.
-	target string
+	// lg is the license graph the node belongs to.
+	lg *LicenseGraph
 
-	// dependency identifies the target node being depended on.
-	//
-	// i.e. `dependency` is necessary to build `target`.
-	dependency string
+	// edges identifies the dependencies of the target.
+	edges TargetEdgeList
 
-	// annotations are a set of text attributes attached to the edge.
-	//
-	// Policy prescribes meaning to a limited set of annotations; others
-	// are preserved and ignored.
-	annotations TargetEdgeAnnotations
+	// licenseConditions identifies the set of license conditions originating at the target node.
+	licenseConditions LicenseConditionSet
+
+	// resolution identifies the set of conditions resolved by acting on the target node.
+	resolution LicenseConditionSet
 }
 
 // addDependencies converts the proto AnnotatedDependencies into `edges`
-func addDependencies(edges *[]*dependencyEdge, target string, dependencies []*license_metadata_proto.AnnotatedDependency) error {
-	for _, ad := range dependencies {
+func addDependencies(lg *LicenseGraph, tn *TargetNode) error {
+	tn.edges = make(TargetEdgeList, 0,len(tn.proto.Deps))
+	for _, ad := range tn.proto.Deps {
 		dependency := ad.GetFile()
 		if len(dependency) == 0 {
 			return fmt.Errorf("missing dependency name")
 		}
+		dtn, ok := lg.targets[dependency]
+		if !ok {
+			return fmt.Errorf("unknown dependency name %q", dependency)
+		}
+		if dtn == nil {
+			return fmt.Errorf("nil dependency for name %q", dependency)
+		}
 		annotations := newEdgeAnnotations()
 		for _, a := range ad.Annotations {
-			if len(a) == 0 {
-				continue
+			// look up a common constant annotation string from a small map
+			// instead of creating 1000's of copies of the same 3 strings.
+			if ann, ok := RecognizedAnnotations[a]; ok {
+				annotations.annotations[ann] = struct{}{}
 			}
-			annotations.annotations[a] = true
 		}
-		*edges = append(*edges, &dependencyEdge{target, dependency, annotations})
+		edge := &TargetEdge{tn, dtn, annotations}
+		lg.edges = append(lg.edges, edge)
+		tn.edges = append(tn.edges, edge)
 	}
 	return nil
 }
@@ -206,50 +221,44 @@
 	go func() {
 		f, err := recv.rootFS.Open(file)
 		if err != nil {
-			recv.results <- &result{file, nil, nil, fmt.Errorf("error opening license metadata %q: %w", file, err)}
+			recv.results <- &result{file, nil, fmt.Errorf("error opening license metadata %q: %w", file, err)}
 			return
 		}
 
 		// read the file
 		data, err := io.ReadAll(f)
 		if err != nil {
-			recv.results <- &result{file, nil, nil, fmt.Errorf("error reading license metadata %q: %w", file, err)}
+			recv.results <- &result{file, nil, fmt.Errorf("error reading license metadata %q: %w", file, err)}
 			return
 		}
+		f.Close()
 
-		tn := &TargetNode{name: file}
+		tn := &TargetNode{lg: recv.lg, name: file}
 
 		err = prototext.Unmarshal(data, &tn.proto)
 		if err != nil {
-			recv.results <- &result{file, nil, nil, fmt.Errorf("error license metadata %q: %w", file, err)}
+			recv.results <- &result{file, nil, fmt.Errorf("error license metadata %q: %w", file, err)}
 			return
 		}
 
-		edges := []*dependencyEdge{}
-		err = addDependencies(&edges, file, tn.proto.Deps)
-		if err != nil {
-			recv.results <- &result{file, nil, nil, fmt.Errorf("error license metadata dependency %q: %w", file, err)}
-			return
-		}
-		tn.proto.Deps = []*license_metadata_proto.AnnotatedDependency{}
-
 		// send result for this file and release task before scheduling dependencies,
 		// but do not signal done to WaitGroup until dependencies are scheduled.
-		recv.results <- &result{file, tn, edges, nil}
+		recv.results <- &result{file, tn, nil}
 		recv.task <- true
 
 		// schedule tasks as necessary to read dependencies
-		for _, e := range edges {
+		for _, ad := range tn.proto.Deps {
+			dependency := ad.GetFile()
 			// decide, signal and record whether to schedule task in critical section
 			recv.lg.mu.Lock()
-			_, alreadyScheduled := recv.lg.targets[e.dependency]
+			_, alreadyScheduled := recv.lg.targets[dependency]
 			if !alreadyScheduled {
-				recv.lg.targets[e.dependency] = nil
+				recv.lg.targets[dependency] = nil
 			}
 			recv.lg.mu.Unlock()
 			// schedule task to read dependency file outside critical section
 			if !alreadyScheduled {
-				readFile(recv, e.dependency)
+				readFile(recv, dependency)
 			}
 		}
 
diff --git a/tools/compliance/readgraph_test.go b/tools/compliance/readgraph_test.go
index 6248209..6ff7a6c 100644
--- a/tools/compliance/readgraph_test.go
+++ b/tools/compliance/readgraph_test.go
@@ -108,29 +108,40 @@
 			}
 			sort.Sort(byEdge(tt.expectedEdges))
 			sort.Sort(byEdge(actualEdges))
+			t.Logf("actualEdges:")
+			for _, edge := range actualEdges {
+				t.Logf("  %s", edge.String())
+			}
+			t.Logf("expectedEdges:")
+			for _, edge := range actualEdges {
+				t.Logf("  %s", edge.String())
+			}
 			if len(tt.expectedEdges) != len(actualEdges) {
-				t.Errorf("unexpected number of edges: got %v with %d elements, want %v with %d elements",
-					actualEdges, len(actualEdges), tt.expectedEdges, len(tt.expectedEdges))
+				t.Errorf("len(actualEdges): got %d, want %d", len(actualEdges), len(tt.expectedEdges))
 			} else {
 				for i := 0; i < len(actualEdges); i++ {
 					if tt.expectedEdges[i] != actualEdges[i] {
-						t.Errorf("unexpected edge at element %d: got %s, want %s", i, actualEdges[i], tt.expectedEdges[i])
+						t.Errorf("actualEdges[%d]: got %s, want %s", i, actualEdges[i], tt.expectedEdges[i])
 					}
 				}
 			}
+
 			actualTargets := make([]string, 0)
 			for _, t := range lg.Targets() {
 				actualTargets = append(actualTargets, t.Name())
 			}
 			sort.Strings(tt.expectedTargets)
 			sort.Strings(actualTargets)
+
+			t.Logf("actualTargets: %v", actualTargets)
+			t.Logf("expectedTargets: %v", tt.expectedTargets)
+
 			if len(tt.expectedTargets) != len(actualTargets) {
-				t.Errorf("unexpected number of targets: got %v with %d elements, want %v with %d elements",
-					actualTargets, len(actualTargets), tt.expectedTargets, len(tt.expectedTargets))
+				t.Errorf("len(actualTargets): got %d, want %d", len(actualTargets), len(tt.expectedTargets))
 			} else {
 				for i := 0; i < len(actualTargets); i++ {
 					if tt.expectedTargets[i] != actualTargets[i] {
-						t.Errorf("unexpected target at element %d: got %s, want %s", i, actualTargets[i], tt.expectedTargets[i])
+						t.Errorf("actualTargets[%d]: got %s, want %s", i, actualTargets[i], tt.expectedTargets[i])
 					}
 				}
 			}
diff --git a/tools/compliance/resolution.go b/tools/compliance/resolution.go
index 0865ecd..6f15ca3 100644
--- a/tools/compliance/resolution.go
+++ b/tools/compliance/resolution.go
@@ -32,7 +32,7 @@
 // resolve the restricted condition originating from the GPL code.
 type Resolution struct {
 	attachesTo, actsOn *TargetNode
-	cs                 *LicenseConditionSet
+	cs                 LicenseConditionSet
 }
 
 // AttachesTo returns the target node the resolution attaches to.
@@ -48,16 +48,16 @@
 }
 
 // Resolves returns the set of license condition the resolution satisfies.
-func (r Resolution) Resolves() *LicenseConditionSet {
-	return r.cs.Copy()
+func (r Resolution) Resolves() LicenseConditionSet {
+	return r.cs
 }
 
 // asString returns a string representation of the resolution.
 func (r Resolution) asString() string {
 	var sb strings.Builder
-	cl := r.cs.AsList()
-	sort.Sort(cl)
-	fmt.Fprintf(&sb, "%s -> %s -> %s", r.attachesTo.name, r.actsOn.name, cl.String())
+	names := r.cs.Names()
+	sort.Strings(names)
+	fmt.Fprintf(&sb, "%s -> %s{%s}", r.attachesTo.name, r.actsOn.name, strings.Join(names, ", "))
 	return sb.String()
 }
 
@@ -94,67 +94,32 @@
 
 // AllConditions returns the union of all license conditions resolved by any
 // element of the list.
-func (rl ResolutionList) AllConditions() *LicenseConditionSet {
-	result := newLicenseConditionSet()
+func (rl ResolutionList) AllConditions() LicenseConditionSet {
+	result := NewLicenseConditionSet()
 	for _, r := range rl {
-		result.AddSet(r.cs)
+		result = result.Union(r.cs)
 	}
 	return result
 }
 
 // ByName returns the sub-list of resolutions resolving conditions matching
 // `names`.
-func (rl ResolutionList) ByName(names ConditionNames) ResolutionList {
-	result := make(ResolutionList, 0, rl.CountByName(names))
+func (rl ResolutionList) Matching(conditions LicenseConditionSet) ResolutionList {
+	result := make(ResolutionList, 0, rl.CountMatching(conditions))
 	for _, r := range rl {
-		if r.Resolves().HasAnyByName(names) {
-			result = append(result, Resolution{r.attachesTo, r.actsOn, r.cs.ByName(names)})
+		if r.Resolves().MatchesAnySet(conditions) {
+			result = append(result, Resolution{r.attachesTo, r.actsOn, r.cs.MatchingAnySet(conditions)})
 		}
 	}
 	return result
 }
 
-// CountByName returns the number of resolutions resolving conditions matching
-// `names`.
-func (rl ResolutionList) CountByName(names ConditionNames) int {
+// CountMatching returns the number of resolutions resolving conditions matching
+// `conditions`.
+func (rl ResolutionList) CountMatching(conditions LicenseConditionSet) int {
 	c := 0
 	for _, r := range rl {
-		if r.Resolves().HasAnyByName(names) {
-			c++
-		}
-	}
-	return c
-}
-
-// CountConditionsByName returns a count of distinct resolution/conditions
-// pairs matching `names`.
-//
-// A single resolution might resolve multiple conditions matching `names`.
-func (rl ResolutionList) CountConditionsByName(names ConditionNames) int {
-	c := 0
-	for _, r := range rl {
-		c += r.Resolves().CountByName(names)
-	}
-	return c
-}
-
-// ByAttachesTo returns the sub-list of resolutions attached to `attachesTo`.
-func (rl ResolutionList) ByAttachesTo(attachesTo *TargetNode) ResolutionList {
-	result := make(ResolutionList, 0, rl.CountByActsOn(attachesTo))
-	for _, r := range rl {
-		if r.attachesTo == attachesTo {
-			result = append(result, r)
-		}
-	}
-	return result
-}
-
-// CountByAttachesTo returns the number of resolutions attached to
-// `attachesTo`.
-func (rl ResolutionList) CountByAttachesTo(attachesTo *TargetNode) int {
-	c := 0
-	for _, r := range rl {
-		if r.attachesTo == attachesTo {
+		if r.Resolves().MatchesAnySet(conditions) {
 			c++
 		}
 	}
@@ -182,27 +147,3 @@
 	}
 	return c
 }
-
-// ByOrigin returns the sub-list of resolutions resolving license conditions
-// originating at `origin`.
-func (rl ResolutionList) ByOrigin(origin *TargetNode) ResolutionList {
-	result := make(ResolutionList, 0, rl.CountByOrigin(origin))
-	for _, r := range rl {
-		if r.Resolves().HasAnyByOrigin(origin) {
-			result = append(result, Resolution{r.attachesTo, r.actsOn, r.cs.ByOrigin(origin)})
-		}
-	}
-	return result
-}
-
-// CountByOrigin returns the number of resolutions resolving license conditions
-// originating at `origin`.
-func (rl ResolutionList) CountByOrigin(origin *TargetNode) int {
-	c := 0
-	for _, r := range rl {
-		if r.Resolves().HasAnyByOrigin(origin) {
-			c++
-		}
-	}
-	return c
-}
diff --git a/tools/compliance/resolutionset.go b/tools/compliance/resolutionset.go
index ea49db9..893ef26 100644
--- a/tools/compliance/resolutionset.go
+++ b/tools/compliance/resolutionset.go
@@ -19,34 +19,6 @@
 	"strings"
 )
 
-// JoinResolutionSets returns a new ResolutionSet combining the resolutions from
-// multiple resolution sets. All sets must be derived from the same license
-// graph.
-//
-// e.g. combine "restricted", "reciprocal", and "proprietary" resolutions.
-func JoinResolutionSets(resolutions ...*ResolutionSet) *ResolutionSet {
-	if len(resolutions) < 1 {
-		panic(fmt.Errorf("attempt to join 0 resolution sets"))
-	}
-	rmap := make(map[*TargetNode]actionSet)
-	for _, r := range resolutions {
-		if len(r.resolutions) < 1 {
-			continue
-		}
-		for attachesTo, as := range r.resolutions {
-			if as.isEmpty() {
-				continue
-			}
-			if _, ok := rmap[attachesTo]; !ok {
-				rmap[attachesTo] = as.copy()
-				continue
-			}
-			rmap[attachesTo].addSet(as)
-		}
-	}
-	return &ResolutionSet{rmap}
-}
-
 // ResolutionSet describes an immutable set of targets and the license
 // conditions each target must satisfy or "resolve" in a specific context.
 //
@@ -68,8 +40,8 @@
 //
 // An "unencumbered" condition would originate from the binary, and a "notice"
 // condition would originate from the .a library. A ResolutionSet for the
-// context of the Notice policy might apply both conditions to the binary while
-// preserving the origin of each condition. By applying the notice condition to
+// context of the Notice policy might attach both conditions to the binary to
+// act on the origin of each condition. By attaching the notice condition to
 // the binary, the ResolutionSet stipulates the policy that the release of the
 // unencumbered binary must provide suitable notice for the .a library.
 //
@@ -77,228 +49,71 @@
 // validating that a suitable notice has been built into the distribution, or
 // for reporting what notices need to be given.
 //
-// Resolutions for different contexts may be combined in a new ResolutionSet
-// using JoinResolutions(...).
-//
-// See: resolve.go for:
-//  * ResolveBottomUpConditions(...)
-//  * ResolveTopDownForCondition(...)
-// See also: policy.go for:
-//  * ResolveSourceSharing(...)
-//  * ResolveSourcePrivacy(...)
-type ResolutionSet struct {
-	// resolutions maps names of target with applicable conditions to the set of conditions that apply.
-	resolutions map[*TargetNode]actionSet
+// The action is defined by the context. In the above example, the action is
+// providing notice for the module acted on. In another context, the action
+// might be sharing the source-code or preserving the privacy of the module
+// acted on.
+type ResolutionSet map[*TargetNode]ActionSet
+
+// AttachesTo identifies the list of targets triggering action to resolve
+// conditions. (unordered)
+func (rs ResolutionSet) AttachesTo() TargetNodeList {
+	result := make(TargetNodeList, 0, len(rs))
+	for attachesTo := range rs {
+		result = append(result, attachesTo)
+	}
+	return result
 }
 
-// String returns a string representation of the set.
-func (rs *ResolutionSet) String() string {
+
+// AttachesToTarget returns true if the set contains conditions that
+// are `attachedTo`.
+func (rs ResolutionSet) AttachesToTarget(target *TargetNode) bool {
+	_, isPresent := rs[target]
+	return isPresent
+}
+
+
+// Resolutions returns the list of resolutions that `attachedTo`
+// target must resolve. Returns empty list if no conditions apply.
+func (rs ResolutionSet) Resolutions(attachesTo *TargetNode) ResolutionList {
+	as, ok := rs[attachesTo]
+	if !ok {
+		return nil
+	}
+	result := make(ResolutionList, 0, len(as))
+	for actsOn, cs := range as {
+		result = append(result, Resolution{attachesTo, actsOn, cs})
+	}
+	return result
+}
+
+// String returns a human-readable string representation of the set.
+func (rs ResolutionSet) String() string {
 	var sb strings.Builder
 	fmt.Fprintf(&sb, "{")
 	sep := ""
-	for attachesTo, as := range rs.resolutions {
-		fmt.Fprintf(&sb, "%s%s -> %s", sep, attachesTo.name, as.String())
+	for attachesTo, as := range rs {
+		fmt.Fprintf(&sb, "%s%s -> %s", sep, attachesTo.Name(), as.String())
 		sep = ", "
 	}
 	fmt.Fprintf(&sb, "}")
 	return sb.String()
 }
 
-// AttachesTo identifies the list of targets triggering action to resolve
-// conditions. (unordered)
-func (rs *ResolutionSet) AttachesTo() TargetNodeList {
-	targets := make(TargetNodeList, 0, len(rs.resolutions))
-	for attachesTo := range rs.resolutions {
-		targets = append(targets, attachesTo)
-	}
-	return targets
-}
+// ActionSet identifies a set of targets to act on and the license conditions
+// the action will resolve.
+type ActionSet map[*TargetNode]LicenseConditionSet
 
-// ActsOn identifies the list of targets to act on (share, give notice etc.)
-// to resolve conditions. (unordered)
-func (rs *ResolutionSet) ActsOn() TargetNodeList {
-	tset := make(map[*TargetNode]bool)
-	for _, as := range rs.resolutions {
-		for actsOn := range as {
-			tset[actsOn] = true
-		}
-	}
-	targets := make(TargetNodeList, 0, len(tset))
-	for target := range tset {
-		targets = append(targets, target)
-	}
-	return targets
-}
-
-// Origins identifies the list of targets originating conditions to resolve.
-// (unordered)
-func (rs *ResolutionSet) Origins() TargetNodeList {
-	tset := make(map[*TargetNode]bool)
-	for _, as := range rs.resolutions {
-		for _, cs := range as {
-			for _, origins := range cs.conditions {
-				for origin := range origins {
-					tset[origin] = true
-				}
-			}
-		}
-	}
-	targets := make(TargetNodeList, 0, len(tset))
-	for target := range tset {
-		targets = append(targets, target)
-	}
-	return targets
-}
-
-// Resolutions returns the list of resolutions that `attachedTo`
-// target must resolve. Returns empty list if no conditions apply.
-//
-// Panics if `attachedTo` does not appear in the set.
-func (rs *ResolutionSet) Resolutions(attachedTo *TargetNode) ResolutionList {
-	as, ok := rs.resolutions[attachedTo]
-	if !ok {
-		return ResolutionList{}
-	}
-	result := make(ResolutionList, 0, len(as))
+// String returns a human-readable string representation of the set.
+func (as ActionSet) String() string {
+	var sb strings.Builder
+	fmt.Fprintf(&sb, "{")
+	sep := ""
 	for actsOn, cs := range as {
-		result = append(result, Resolution{attachedTo, actsOn, cs.Copy()})
+		fmt.Fprintf(&sb, "%s%s%s", sep, actsOn.Name(), cs.String())
+		sep = ", "
 	}
-	return result
-}
-
-// ResolutionsByActsOn returns the list of resolutions that must `actOn` to
-// resolvee. Returns empty list if no conditions apply.
-//
-// Panics if `actOn` does not appear in the set.
-func (rs *ResolutionSet) ResolutionsByActsOn(actOn *TargetNode) ResolutionList {
-	c := 0
-	for _, as := range rs.resolutions {
-		if _, ok := as[actOn]; ok {
-			c++
-		}
-	}
-	result := make(ResolutionList, 0, c)
-	for attachedTo, as := range rs.resolutions {
-		if cs, ok := as[actOn]; ok {
-			result = append(result, Resolution{attachedTo, actOn, cs.Copy()})
-		}
-	}
-	return result
-}
-
-// AttachesToByOrigin identifies the list of targets requiring action to
-// resolve conditions originating at `origin`. (unordered)
-func (rs *ResolutionSet) AttachesToByOrigin(origin *TargetNode) TargetNodeList {
-	tset := make(map[*TargetNode]bool)
-	for attachesTo, as := range rs.resolutions {
-		for _, cs := range as {
-			if cs.HasAnyByOrigin(origin) {
-				tset[attachesTo] = true
-				break
-			}
-		}
-	}
-	targets := make(TargetNodeList, 0, len(tset))
-	for target := range tset {
-		targets = append(targets, target)
-	}
-	return targets
-}
-
-// AttachesToTarget returns true if the set contains conditions that
-// are `attachedTo`.
-func (rs *ResolutionSet) AttachesToTarget(attachedTo *TargetNode) bool {
-	_, isPresent := rs.resolutions[attachedTo]
-	return isPresent
-}
-
-// AnyByNameAttachToTarget returns true if the set contains conditions matching
-// `names` that attach to `attachedTo`.
-func (rs *ResolutionSet) AnyByNameAttachToTarget(attachedTo *TargetNode, names ...ConditionNames) bool {
-	as, isPresent := rs.resolutions[attachedTo]
-	if !isPresent {
-		return false
-	}
-	for _, cs := range as {
-		for _, cn := range names {
-			for _, name := range cn {
-				_, isPresent = cs.conditions[name]
-				if isPresent {
-					return true
-				}
-			}
-		}
-	}
-	return false
-}
-
-// AllByNameAttachTo returns true if the set contains at least one condition
-// matching each element of `names` for `attachedTo`.
-func (rs *ResolutionSet) AllByNameAttachToTarget(attachedTo *TargetNode, names ...ConditionNames) bool {
-	as, isPresent := rs.resolutions[attachedTo]
-	if !isPresent {
-		return false
-	}
-	for _, cn := range names {
-		found := false
-	asloop:
-		for _, cs := range as {
-			for _, name := range cn {
-				_, isPresent = cs.conditions[name]
-				if isPresent {
-					found = true
-					break asloop
-				}
-			}
-		}
-		if !found {
-			return false
-		}
-	}
-	return true
-}
-
-// IsEmpty returns true if the set contains no conditions to resolve.
-func (rs *ResolutionSet) IsEmpty() bool {
-	for _, as := range rs.resolutions {
-		if !as.isEmpty() {
-			return false
-		}
-	}
-	return true
-}
-
-// compliance-only ResolutionSet methods
-
-// newResolutionSet constructs a new, empty instance of resolutionSetImp for graph `lg`.
-func newResolutionSet() *ResolutionSet {
-	return &ResolutionSet{make(map[*TargetNode]actionSet)}
-}
-
-// addConditions attaches all of the license conditions in `as` to `attachTo` to act on the originating node if not already applied.
-func (rs *ResolutionSet) addConditions(attachTo *TargetNode, as actionSet) {
-	_, ok := rs.resolutions[attachTo]
-	if !ok {
-		rs.resolutions[attachTo] = as.copy()
-		return
-	}
-	rs.resolutions[attachTo].addSet(as)
-}
-
-// add attaches all of the license conditions in `as` to `attachTo` to act on `attachTo` if not already applied.
-func (rs *ResolutionSet) addSelf(attachTo *TargetNode, as actionSet) {
-	for _, cs := range as {
-		if cs.IsEmpty() {
-			return
-		}
-		_, ok := rs.resolutions[attachTo]
-		if !ok {
-			rs.resolutions[attachTo] = make(actionSet)
-		}
-		_, ok = rs.resolutions[attachTo][attachTo]
-		if !ok {
-			rs.resolutions[attachTo][attachTo] = newLicenseConditionSet()
-		}
-		rs.resolutions[attachTo][attachTo].AddSet(cs)
-	}
+	fmt.Fprintf(&sb, "}")
+	return sb.String()
 }
diff --git a/tools/compliance/resolutionset_test.go b/tools/compliance/resolutionset_test.go
index e50e823..89cdfeb 100644
--- a/tools/compliance/resolutionset_test.go
+++ b/tools/compliance/resolutionset_test.go
@@ -77,113 +77,46 @@
 	proprietary = []res{}
 )
 
-func TestResolutionSet_JoinResolutionSets(t *testing.T) {
-	lg := newLicenseGraph()
-
-	rsNotice := toResolutionSet(lg, notice)
-	rsShare := toResolutionSet(lg, share)
-	rsExpected := toResolutionSet(lg, append(notice, share...))
-
-	rsActual := JoinResolutionSets(rsNotice, rsShare)
-	checkSame(rsActual, rsExpected, t)
-}
-
-func TestResolutionSet_JoinResolutionsEmpty(t *testing.T) {
-	lg := newLicenseGraph()
-
-	rsShare := toResolutionSet(lg, share)
-	rsProprietary := toResolutionSet(lg, proprietary)
-	rsExpected := toResolutionSet(lg, append(share, proprietary...))
-
-	rsActual := JoinResolutionSets(rsShare, rsProprietary)
-	checkSame(rsActual, rsExpected, t)
-}
-
-func TestResolutionSet_Origins(t *testing.T) {
+func TestResolutionSet_AttachesTo(t *testing.T) {
 	lg := newLicenseGraph()
 
 	rsShare := toResolutionSet(lg, share)
 
-	origins := make([]string, 0)
-	for _, target := range rsShare.Origins() {
-		origins = append(origins, target.Name())
-	}
-	sort.Strings(origins)
-	if len(origins) != 2 {
-		t.Errorf("unexpected number of origins: got %v with %d elements, want [\"bin1\", \"bin2\"] with 2 elements", origins, len(origins))
-	}
-	if origins[0] != "bin1" {
-		t.Errorf("unexpected origin at element 0: got %s, want \"bin1\"", origins[0])
-	}
-	if origins[1] != "bin2" {
-		t.Errorf("unexpected origin at element 0: got %s, want \"bin2\"", origins[0])
-	}
-}
+	t.Logf("checking resolution set %s", rsShare.String())
 
-func TestResolutionSet_AttachedToTarget(t *testing.T) {
-	lg := newLicenseGraph()
+	actual := rsShare.AttachesTo().Names()
+	sort.Strings(actual)
 
-	rsShare := toResolutionSet(lg, share)
+	expected := []string{"bin1", "bin2", "image"}
 
-	if rsShare.AttachesToTarget(newTestNode(lg, "binc")) {
-		t.Errorf("unexpected AttachedToTarget(\"binc\"): got true, want false")
-	}
-	if !rsShare.AttachesToTarget(newTestNode(lg, "image")) {
-		t.Errorf("unexpected AttachedToTarget(\"image\"): got false want true")
-	}
-}
+	t.Logf("actual rsShare: %v", actual)
+	t.Logf("expected rsShare: %v", expected)
 
-func TestResolutionSet_AnyByNameAttachToTarget(t *testing.T) {
-	lg := newLicenseGraph()
+	if len(actual) != len(expected) {
+		t.Errorf("rsShare: wrong number of targets: got %d, want %d", len(actual), len(expected))
+		return
+	}
+	for i := 0; i < len(actual); i++ {
+		if actual[i] != expected[i] {
+			t.Errorf("rsShare: unexpected target at index %d: got %s, want %s", i, actual[i], expected[i])
+		}
+	}
 
-	rs := toResolutionSet(lg, bottomUp)
+	rsPrivate := toResolutionSet(lg, proprietary)
+	actual = rsPrivate.AttachesTo().Names()
+	expected = []string{}
 
-	pandp := ConditionNames{"permissive", "proprietary"}
-	pandn := ConditionNames{"permissive", "notice"}
-	p := ConditionNames{"proprietary"}
-	r := ConditionNames{"restricted"}
+	t.Logf("actual rsPrivate: %v", actual)
+	t.Logf("expected rsPrivate: %v", expected)
 
-	if rs.AnyByNameAttachToTarget(newTestNode(lg, "image"), pandp, p) {
-		t.Errorf("unexpected AnyByNameAttachToTarget(\"image\", \"proprietary\", \"permissive\"): want false, got true")
+	if len(actual) != len(expected) {
+		t.Errorf("rsPrivate: wrong number of targets: got %d, want %d", len(actual), len(expected))
+		return
 	}
-	if !rs.AnyByNameAttachToTarget(newTestNode(lg, "binc"), p) {
-		t.Errorf("unexpected AnyByNameAttachToTarget(\"binc\", \"proprietary\"): want true, got false")
-	}
-	if !rs.AnyByNameAttachToTarget(newTestNode(lg, "image"), pandn) {
-		t.Errorf("unexpected AnyByNameAttachToTarget(\"image\", \"permissive\", \"notice\"): want true, got false")
-	}
-	if !rs.AnyByNameAttachToTarget(newTestNode(lg, "image"), r, pandn) {
-		t.Errorf("unexpected AnyByNameAttachToTarget(\"image\", \"restricted\", \"notice\"): want true, got false")
-	}
-	if !rs.AnyByNameAttachToTarget(newTestNode(lg, "image"), r, p) {
-		t.Errorf("unexpected AnyByNameAttachToTarget(\"image\", \"restricted\", \"proprietary\"): want true, got false")
-	}
-}
-
-func TestResolutionSet_AllByNameAttachToTarget(t *testing.T) {
-	lg := newLicenseGraph()
-
-	rs := toResolutionSet(lg, bottomUp)
-
-	pandp := ConditionNames{"permissive", "proprietary"}
-	pandn := ConditionNames{"permissive", "notice"}
-	p := ConditionNames{"proprietary"}
-	r := ConditionNames{"restricted"}
-
-	if rs.AllByNameAttachToTarget(newTestNode(lg, "image"), pandp, p) {
-		t.Errorf("unexpected AllByNameAttachToTarget(\"image\", \"proprietary\", \"permissive\"): want false, got true")
-	}
-	if !rs.AllByNameAttachToTarget(newTestNode(lg, "binc"), p) {
-		t.Errorf("unexpected AllByNameAttachToTarget(\"binc\", \"proprietary\"): want true, got false")
-	}
-	if !rs.AllByNameAttachToTarget(newTestNode(lg, "image"), pandn) {
-		t.Errorf("unexpected AllByNameAttachToTarget(\"image\", \"notice\"): want true, got false")
-	}
-	if !rs.AllByNameAttachToTarget(newTestNode(lg, "image"), r, pandn) {
-		t.Errorf("unexpected AllByNameAttachToTarget(\"image\", \"restricted\", \"notice\"): want true, got false")
-	}
-	if rs.AllByNameAttachToTarget(newTestNode(lg, "image"), r, p) {
-		t.Errorf("unexpected AllByNameAttachToTarget(\"image\", \"restricted\", \"proprietary\"): want false, got true")
+	for i := 0; i < len(actual); i++ {
+		if actual[i] != expected[i] {
+			t.Errorf("rsPrivate: unexpected target at index %d: got %s, want %s", i, actual[i], expected[i])
+		}
 	}
 }
 
@@ -192,10 +125,12 @@
 
 	rsShare := toResolutionSet(lg, share)
 
+	t.Logf("checking resolution set %s", rsShare.String())
+
 	if rsShare.AttachesToTarget(newTestNode(lg, "binc")) {
-		t.Errorf("unexpected hasTarget(\"binc\"): got true, want false")
+		t.Errorf("actual.AttachesToTarget(\"binc\"): got true, want false")
 	}
 	if !rsShare.AttachesToTarget(newTestNode(lg, "image")) {
-		t.Errorf("unexpected AttachesToTarget(\"image\"): got false want true")
+		t.Errorf("actual.AttachesToTarget(\"image\"): got false want true")
 	}
 }
diff --git a/tools/compliance/test_util.go b/tools/compliance/test_util.go
index a183b90..8f4088a 100644
--- a/tools/compliance/test_util.go
+++ b/tools/compliance/test_util.go
@@ -86,7 +86,6 @@
 license_kinds: "legacy_by_exception_only"
 license_conditions: "by_exception_only"
 `
-
 )
 
 var (
@@ -111,23 +110,39 @@
 	}
 )
 
-// toConditionList converts a test data map of condition name to origin names into a ConditionList.
-func toConditionList(lg *LicenseGraph, conditions map[string][]string) ConditionList {
-	cl := make(ConditionList, 0)
-	for name, origins := range conditions {
-		for _, origin := range origins {
-			cl = append(cl, LicenseCondition{name, newTestNode(lg, origin)})
-		}
-	}
-	return cl
-}
-
 // newTestNode constructs a test node in the license graph.
 func newTestNode(lg *LicenseGraph, targetName string) *TargetNode {
-	if _, ok := lg.targets[targetName]; !ok {
-		lg.targets[targetName] = &TargetNode{name: targetName}
+	if tn, alreadyExists := lg.targets[targetName]; alreadyExists {
+		return tn
 	}
-	return lg.targets[targetName]
+	tn := &TargetNode{name: targetName}
+	lg.targets[targetName] = tn
+	return tn
+}
+
+// newTestCondition constructs a test license condition in the license graph.
+func newTestCondition(lg *LicenseGraph, targetName string, conditionName string) LicenseCondition {
+	tn := newTestNode(lg, targetName)
+	cl := LicenseConditionSetFromNames(tn, conditionName).AsList()
+	if len(cl) == 0 {
+		panic(fmt.Errorf("attempt to create unrecognized condition: %q", conditionName))
+	} else if len(cl) != 1 {
+		panic(fmt.Errorf("unexpected multiple conditions from condition name: %q: got %d, want 1", conditionName, len(cl)))
+	}
+	lc := cl[0]
+	tn.licenseConditions = tn.licenseConditions.Plus(lc)
+	return lc
+}
+
+// newTestConditionSet constructs a test license condition set in the license graph.
+func newTestConditionSet(lg *LicenseGraph, targetName string, conditionName []string) LicenseConditionSet {
+	tn := newTestNode(lg, targetName)
+	cs := LicenseConditionSetFromNames(tn, conditionName...)
+	if cs.IsEmpty() {
+		panic(fmt.Errorf("attempt to create unrecognized condition: %q", conditionName))
+	}
+	tn.licenseConditions = tn.licenseConditions.Union(cs)
+	return cs
 }
 
 // testFS implements a test file system (fs.FS) simulated by a map from filename to []byte content.
@@ -270,6 +285,21 @@
 	return ReadLicenseGraph(&fs, stderr, roots)
 }
 
+// logGraph outputs a representation of the graph to a test log.
+func logGraph(lg *LicenseGraph, t *testing.T) {
+	t.Logf("license graph:")
+	t.Logf("  targets:")
+	for _, target := range lg.Targets() {
+		t.Logf("    %s%s in package %q", target.Name(), target.LicenseConditions().String(), target.PackageName())
+	}
+	t.Logf("  /targets")
+	t.Logf("  edges:")
+	for _, edge := range lg.Edges() {
+		t.Logf("    %s", edge.String())
+	}
+	t.Logf("  /edges")
+	t.Logf("/license graph")
+}
 
 // byAnnotatedEdge orders edges by target then dep name then annotations.
 type byAnnotatedEdge []annotated
@@ -296,33 +326,137 @@
 	return l[i].target < l[j].target
 }
 
+// act describes test data resolution actions to define test action sets.
+type act struct {
+	actsOn, origin, condition string
+}
+
+// String returns a human-readable string representing the test action.
+func (a act) String() string {
+	return fmt.Sprintf("%s{%s:%s}", a.actsOn, a.origin, a.condition)
+}
+
+// toActionSet converts a list of act test data into a test action set.
+func toActionSet(lg *LicenseGraph, data []act) ActionSet {
+	as := make(ActionSet)
+	for _, a := range data {
+		actsOn := newTestNode(lg, a.actsOn)
+		cs := newTestConditionSet(lg, a.origin, strings.Split(a.condition, "|"))
+		as[actsOn] = cs
+	}
+	return as
+}
+
 // res describes test data resolutions to define test resolution sets.
 type res struct {
 	attachesTo, actsOn, origin, condition string
 }
 
 // toResolutionSet converts a list of res test data into a test resolution set.
-func toResolutionSet(lg *LicenseGraph, data []res) *ResolutionSet {
-	rmap := make(map[*TargetNode]actionSet)
+func toResolutionSet(lg *LicenseGraph, data []res) ResolutionSet {
+	rmap := make(ResolutionSet)
 	for _, r := range data {
 		attachesTo := newTestNode(lg, r.attachesTo)
 		actsOn := newTestNode(lg, r.actsOn)
-		origin := newTestNode(lg, r.origin)
 		if _, ok := rmap[attachesTo]; !ok {
-			rmap[attachesTo] = make(actionSet)
+			rmap[attachesTo] = make(ActionSet)
 		}
-		if _, ok := rmap[attachesTo][actsOn]; !ok {
-			rmap[attachesTo][actsOn] = newLicenseConditionSet()
-		}
-		rmap[attachesTo][actsOn].add(origin, r.condition)
+		cs := newTestConditionSet(lg, r.origin, strings.Split(r.condition, ":"))
+		rmap[attachesTo][actsOn] |= cs
 	}
-	return &ResolutionSet{rmap}
+	return rmap
 }
 
+// tcond associates a target name with '|' separated string conditions.
+type tcond struct {
+	target, conditions string
+}
+
+// action represents a single element of an ActionSet for testing.
+type action struct {
+	target *TargetNode
+	cs     LicenseConditionSet
+}
+
+// String returns a human-readable string representation of the action.
+func (a action) String() string {
+	return fmt.Sprintf("%s%s", a.target.Name(), a.cs.String())
+}
+
+// actionList represents an array of actions and a total order defined by
+// target name followed by license condition set.
+type actionList []action
+
+// String returns a human-readable string representation of the list.
+func (l actionList) String() string {
+	var sb strings.Builder
+	fmt.Fprintf(&sb, "[")
+	sep := ""
+	for _, a := range l {
+		fmt.Fprintf(&sb, "%s%s", sep, a.String())
+		sep = ", "
+	}
+	fmt.Fprintf(&sb, "]")
+	return sb.String()
+}
+
+// Len returns the count of elements in the slice.
+func (l actionList) Len() int      { return len(l) }
+
+// Swap rearranges 2 elements of the slice so that each occupies the other's
+// former position.
+func (l actionList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
+
+// Less returns true when the `i`th element is lexicographically less than
+// the `j`th element.
+func (l actionList) Less(i, j int) bool {
+	if l[i].target == l[j].target {
+		return l[i].cs < l[j].cs
+	}
+	return l[i].target.Name() < l[j].target.Name()
+}
+
+// asActionList represents the resolved license conditions in a license graph
+// as an actionList for comparison in a test.
+func asActionList(lg *LicenseGraph) actionList {
+	result := make(actionList, 0, len(lg.targets))
+	for _, target := range lg.targets {
+		cs := target.resolution
+		if cs.IsEmpty() {
+			continue
+		}
+		result = append(result, action{target, cs})
+	}
+	return result
+}
+
+// toActionList converts an array of tcond into an actionList for comparison
+// in a test.
+func toActionList(lg *LicenseGraph, actions []tcond) actionList {
+	result := make(actionList, 0, len(actions))
+	for _, actn := range actions {
+		target := newTestNode(lg, actn.target)
+		cs := NewLicenseConditionSet()
+		for _, name := range strings.Split(actn.conditions, "|") {
+			lc, ok := RecognizedConditionNames[name]
+			if !ok {
+				panic(fmt.Errorf("Unrecognized test condition name: %q", name))
+			}
+			cs = cs.Plus(lc)
+		}
+		result = append(result, action{target, cs})
+	}
+	return result
+}
+
+// confl defines test data for a SourceSharePrivacyConflict as a target name,
+// source condition name, privacy condition name triple.
 type confl struct {
 	sourceNode, share, privacy string
 }
 
+// toConflictList converts confl test data into an array of
+// SourceSharePrivacyConflict for comparison in a test.
 func toConflictList(lg *LicenseGraph, data []confl) []SourceSharePrivacyConflict {
 	result := make([]SourceSharePrivacyConflict, 0, len(data))
 	for _, c := range data {
@@ -334,30 +468,40 @@
 		cprivacy := fields[1]
 		result = append(result, SourceSharePrivacyConflict{
 				newTestNode(lg, c.sourceNode),
-				LicenseCondition{cshare, newTestNode(lg, oshare)},
-				LicenseCondition{cprivacy, newTestNode(lg, oprivacy)},
+				newTestCondition(lg, oshare, cshare),
+				newTestCondition(lg, oprivacy, cprivacy),
 			})
 	}
 	return result
 }
 
 // checkSameActions compares an actual action set to an expected action set for a test.
-func checkSameActions(lg *LicenseGraph, asActual, asExpected actionSet, t *testing.T) {
-	rsActual := ResolutionSet{make(map[*TargetNode]actionSet)}
-	rsExpected := ResolutionSet{make(map[*TargetNode]actionSet)}
+func checkSameActions(lg *LicenseGraph, asActual, asExpected ActionSet, t *testing.T) {
+	rsActual := make(ResolutionSet)
+	rsExpected := make(ResolutionSet)
 	testNode := newTestNode(lg, "test")
-	rsActual.resolutions[testNode] = asActual
-	rsExpected.resolutions[testNode] = asExpected
-	checkSame(&rsActual, &rsExpected, t)
+	rsActual[testNode] = asActual
+	rsExpected[testNode] = asExpected
+	checkSame(rsActual, rsExpected, t)
 }
 
 // checkSame compares an actual resolution set to an expected resolution set for a test.
-func checkSame(rsActual, rsExpected *ResolutionSet, t *testing.T) {
+func checkSame(rsActual, rsExpected ResolutionSet, t *testing.T) {
+	t.Logf("actual resolution set: %s", rsActual.String())
+	t.Logf("expected resolution set: %s", rsExpected.String())
+
+	actualTargets := rsActual.AttachesTo()
+	sort.Sort(actualTargets)
+
 	expectedTargets := rsExpected.AttachesTo()
 	sort.Sort(expectedTargets)
+
+	t.Logf("actual targets: %s", actualTargets.String())
+	t.Logf("expected targets: %s", expectedTargets.String())
+
 	for _, target := range expectedTargets {
 		if !rsActual.AttachesToTarget(target) {
-			t.Errorf("unexpected missing target: got AttachesToTarget(%q) is false in %s, want true in %s", target.name, rsActual, rsExpected)
+			t.Errorf("unexpected missing target: got AttachesToTarget(%q) is false, want true", target.name)
 			continue
 		}
 		expectedRl := rsExpected.Resolutions(target)
@@ -365,8 +509,8 @@
 		actualRl := rsActual.Resolutions(target)
 		sort.Sort(actualRl)
 		if len(expectedRl) != len(actualRl) {
-			t.Errorf("unexpected number of resolutions attach to %q: got %s with %d elements, want %s with %d elements",
-				target.name, actualRl, len(actualRl), expectedRl, len(expectedRl))
+			t.Errorf("unexpected number of resolutions attach to %q: %d elements, %d elements",
+				target.name, len(actualRl), len(expectedRl))
 			continue
 		}
 		for i := 0; i < len(expectedRl); i++ {
@@ -375,34 +519,86 @@
 					target.name, i, actualRl[i].asString(), expectedRl[i].asString())
 				continue
 			}
-			expectedConditions := expectedRl[i].Resolves().AsList()
-			actualConditions := actualRl[i].Resolves().AsList()
-			sort.Sort(expectedConditions)
-			sort.Sort(actualConditions)
-			if len(expectedConditions) != len(actualConditions) {
-				t.Errorf("unexpected number of conditions apply to %q acting on %q: got %s with %d elements, want %s with %d elements",
+			expectedConditions := expectedRl[i].Resolves()
+			actualConditions := actualRl[i].Resolves()
+			if expectedConditions != actualConditions {
+				t.Errorf("unexpected conditions apply to %q acting on %q: got %04x with names %s, want %04x with names %s",
 					target.name, expectedRl[i].actsOn.name,
-					actualConditions, len(actualConditions),
-					expectedConditions, len(expectedConditions))
+					actualConditions, actualConditions.Names(),
+					expectedConditions, expectedConditions.Names())
 				continue
 			}
-			for j := 0; j < len(expectedConditions); j++ {
-				if expectedConditions[j] != actualConditions[j] {
-					t.Errorf("unexpected condition attached to %q acting on %q at index %d: got %s at index %d in %s, want %s in %s",
-						target.name, expectedRl[i].actsOn.name, i,
-						actualConditions[j].asString(":"), j, actualConditions,
-						expectedConditions[j].asString(":"), expectedConditions)
-				}
-			}
 		}
 
 	}
+	for _, target := range actualTargets {
+		if !rsExpected.AttachesToTarget(target) {
+			t.Errorf("unexpected extra target: got expected.AttachesTo(%q) is false, want true", target.name)
+		}
+	}
+}
+
+// checkResolvesActions compares an actual action set to an expected action set for a test verifying the actual set
+// resolves all of the expected conditions.
+func checkResolvesActions(lg *LicenseGraph, asActual, asExpected ActionSet, t *testing.T) {
+	rsActual := make(ResolutionSet)
+	rsExpected := make(ResolutionSet)
+	testNode := newTestNode(lg, "test")
+	rsActual[testNode] = asActual
+	rsExpected[testNode] = asExpected
+	checkResolves(rsActual, rsExpected, t)
+}
+
+// checkResolves compares an actual resolution set to an expected resolution set for a test verifying the actual set
+// resolves all of the expected conditions.
+func checkResolves(rsActual, rsExpected ResolutionSet, t *testing.T) {
+	t.Logf("actual resolution set: %s", rsActual.String())
+	t.Logf("expected resolution set: %s", rsExpected.String())
+
 	actualTargets := rsActual.AttachesTo()
 	sort.Sort(actualTargets)
-	for i, target := range actualTargets {
+
+	expectedTargets := rsExpected.AttachesTo()
+	sort.Sort(expectedTargets)
+
+	t.Logf("actual targets: %s", actualTargets.String())
+	t.Logf("expected targets: %s", expectedTargets.String())
+
+	for _, target := range expectedTargets {
+		if !rsActual.AttachesToTarget(target) {
+			t.Errorf("unexpected missing target: got AttachesToTarget(%q) is false, want true", target.name)
+			continue
+		}
+		expectedRl := rsExpected.Resolutions(target)
+		sort.Sort(expectedRl)
+		actualRl := rsActual.Resolutions(target)
+		sort.Sort(actualRl)
+		if len(expectedRl) != len(actualRl) {
+			t.Errorf("unexpected number of resolutions attach to %q: %d elements, %d elements",
+				target.name, len(actualRl), len(expectedRl))
+			continue
+		}
+		for i := 0; i < len(expectedRl); i++ {
+			if expectedRl[i].attachesTo.name != actualRl[i].attachesTo.name || expectedRl[i].actsOn.name != actualRl[i].actsOn.name {
+				t.Errorf("unexpected resolution attaches to %q at index %d: got %s, want %s",
+					target.name, i, actualRl[i].asString(), expectedRl[i].asString())
+				continue
+			}
+			expectedConditions := expectedRl[i].Resolves()
+			actualConditions := actualRl[i].Resolves()
+			if expectedConditions != (expectedConditions & actualConditions) {
+				t.Errorf("expected conditions missing from %q acting on %q: got %04x with names %s, want %04x with names %s",
+					target.name, expectedRl[i].actsOn.name,
+					actualConditions, actualConditions.Names(),
+					expectedConditions, expectedConditions.Names())
+				continue
+			}
+		}
+
+	}
+	for _, target := range actualTargets {
 		if !rsExpected.AttachesToTarget(target) {
-			t.Errorf("unexpected target: got %q element %d in AttachesTo() %s with %d elements in %s, want %s with %d elements in %s",
-				target.name, i, actualTargets, len(actualTargets), rsActual, expectedTargets, len(expectedTargets), rsExpected)
+			t.Errorf("unexpected extra target: got expected.AttachesTo(%q) is false, want true", target.name)
 		}
 	}
 }
diff --git a/tools/event_log_tags.py b/tools/event_log_tags.py
index 35b2de0..a6ae9f1 100644
--- a/tools/event_log_tags.py
+++ b/tools/event_log_tags.py
@@ -55,12 +55,13 @@
     if file_object is None:
       try:
         file_object = open(filename, "rb")
-      except (IOError, OSError), e:
+      except (IOError, OSError) as e:
         self.AddError(str(e))
         return
 
     try:
       for self.linenum, line in enumerate(file_object):
+        line = line.decode('utf-8')
         self.linenum += 1
         line = re.sub('#.*$', '', line) # strip trailing comments
         line = line.strip()
@@ -100,7 +101,7 @@
 
         self.tags.append(Tag(tag, tagname, description,
                              self.filename, self.linenum))
-    except (IOError, OSError), e:
+    except (IOError, OSError) as e:
       self.AddError(str(e))
 
 
@@ -128,8 +129,8 @@
       output_file = "<stdout>"
     else:
       out = open(output_file, "wb")
-    out.write(data)
+    out.write(str.encode(data))
     out.close()
-  except (IOError, OSError), e:
-    print >> sys.stderr, "failed to write %s: %s" % (output_file, e)
+  except (IOError, OSError) as e:
+    print("failed to write %s: %s" % (output_file, e), file=sys.stderr)
     sys.exit(1)
diff --git a/tools/generate-self-extracting-archive.py b/tools/generate-self-extracting-archive.py
index 5b0628d..c9f56cb 100755
--- a/tools/generate-self-extracting-archive.py
+++ b/tools/generate-self-extracting-archive.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (C) 2019 The Android Open Source Project
 #
@@ -120,7 +120,7 @@
 
 def main(argv):
   if len(argv) != 5:
-    print 'generate-self-extracting-archive.py expects exactly 4 arguments'
+    print('generate-self-extracting-archive.py expects exactly 4 arguments')
     sys.exit(1)
 
   output_filename = argv[1]
@@ -134,11 +134,11 @@
     license = license_file.read()
 
   if not license:
-    print 'License file was empty'
+    print('License file was empty')
     sys.exit(1)
 
   if 'SOFTWARE LICENSE AGREEMENT' not in license:
-    print 'License does not look like a license'
+    print('License does not look like a license')
     sys.exit(1)
 
   comment_line = '# %s\n' % comment
diff --git a/tools/java-event-log-tags.py b/tools/java-event-log-tags.py
index 37cd712..4bd6d2b 100755
--- a/tools/java-event-log-tags.py
+++ b/tools/java-event-log-tags.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (C) 2009 The Android Open Source Project
 #
@@ -23,7 +23,7 @@
 -h to display this usage message and exit.
 """
 
-import cStringIO
+from io import StringIO
 import getopt
 import os
 import os.path
@@ -36,24 +36,24 @@
 
 try:
   opts, args = getopt.getopt(sys.argv[1:], "ho:")
-except getopt.GetoptError, err:
-  print str(err)
-  print __doc__
+except getopt.GetoptError as err:
+  print(str(err))
+  print(__doc__)
   sys.exit(2)
 
 for o, a in opts:
   if o == "-h":
-    print __doc__
+    print(__doc__)
     sys.exit(2)
   elif o == "-o":
     output_file = a
   else:
-    print >> sys.stderr, "unhandled option %s" % (o,)
+    print("unhandled option %s" % (o,), file=sys.stderr)
     sys.exit(1)
 
 if len(args) != 1 and len(args) != 2:
-  print "need one or two input files, not %d" % (len(args),)
-  print __doc__
+  print("need one or two input files, not %d" % (len(args),))
+  print(__doc__)
   sys.exit(1)
 
 fn = args[0]
@@ -92,10 +92,10 @@
 
 if tagfile.errors:
   for fn, ln, msg in tagfile.errors:
-    print >> sys.stderr, "%s:%d: error: %s" % (fn, ln, msg)
+    print("%s:%d: error: %s" % (fn, ln, msg), file=sys.stderr)
   sys.exit(1)
 
-buffer = cStringIO.StringIO()
+buffer = StringIO()
 buffer.write("/* This file is auto-generated.  DO NOT MODIFY.\n"
              " * Source file: %s\n"
              " */\n\n" % (fn,))
diff --git a/tools/merge-event-log-tags.py b/tools/merge-event-log-tags.py
index 64bad3f..292604c 100755
--- a/tools/merge-event-log-tags.py
+++ b/tools/merge-event-log-tags.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
 #
 # Copyright (C) 2009 The Android Open Source Project
 #
@@ -24,7 +24,7 @@
 -h to display this usage message and exit.
 """
 
-import cStringIO
+from io import StringIO
 import getopt
 try:
   import hashlib
@@ -48,21 +48,21 @@
 
 try:
   opts, args = getopt.getopt(sys.argv[1:], "ho:m:")
-except getopt.GetoptError, err:
-  print str(err)
-  print __doc__
+except getopt.GetoptError as err:
+  print(str(err))
+  print(__doc__)
   sys.exit(2)
 
 for o, a in opts:
   if o == "-h":
-    print __doc__
+    print(__doc__)
     sys.exit(2)
   elif o == "-o":
     output_file = a
   elif o == "-m":
     pre_merged_file = a
   else:
-    print >> sys.stderr, "unhandled option %s" % (o,)
+    print("unhandled option %s" % (o,), file=sys.stderr)
     sys.exit(1)
 
 # Restrictions on tags:
@@ -133,12 +133,12 @@
 
 if errors:
   for fn, ln, msg in errors:
-    print >> sys.stderr, "%s:%d: error: %s" % (fn, ln, msg)
+    print("%s:%d: error: %s" % (fn, ln, msg), file=sys.stderr)
   sys.exit(1)
 
 if warnings:
   for fn, ln, msg in warnings:
-    print >> sys.stderr, "%s:%d: warning: %s" % (fn, ln, msg)
+    print("%s:%d: warning: %s" % (fn, ln, msg), file=sys.stderr)
 
 # Python's hash function (a) isn't great and (b) varies between
 # versions of python.  Using md5 is overkill here but is the same from
@@ -154,14 +154,14 @@
 # If we were provided pre-merged tags (w/ the -m option), then don't
 # ever try to allocate one, just fail if we don't have a number
 
-for name, t in sorted(by_tagname.iteritems()):
+for name, t in sorted(by_tagname.items()):
   if t.tagnum is None:
     if pre_merged_tags:
       try:
         t.tagnum = pre_merged_tags[t.tagname]
       except KeyError:
-        print >> sys.stderr, ("Error: Tag number not defined for tag `%s'."
-            +" Have you done a full build?") % t.tagname
+        print("Error: Tag number not defined for tag `%s'. Have you done a full build?" % t.tagname,
+              file=sys.stderr)
         sys.exit(1)
     else:
       while True:
@@ -174,8 +174,8 @@
 
 # by_tagnum should be complete now; we've assigned numbers to all tags.
 
-buffer = cStringIO.StringIO()
-for n, t in sorted(by_tagnum.iteritems()):
+buffer = StringIO()
+for n, t in sorted(by_tagnum.items()):
   if t.description:
     buffer.write("%d %s %s\n" % (t.tagnum, t.tagname, t.description))
   else:
diff --git a/tools/releasetools/Android.bp b/tools/releasetools/Android.bp
index a979a8e..7b2c290 100644
--- a/tools/releasetools/Android.bp
+++ b/tools/releasetools/Android.bp
@@ -162,6 +162,7 @@
     required: [
         "brillo_update_payload",
         "checkvintf",
+        "generate_gki_certificate",
         "minigzip",
         "lz4",
         "toybox",
@@ -236,6 +237,7 @@
         "boot_signer",
         "brotli",
         "bsdiff",
+        "generate_gki_certificate",
         "imgdiff",
         "minigzip",
         "lz4",
@@ -301,6 +303,7 @@
         "brotli",
         "bsdiff",
         "deapexer",
+        "generate_gki_certificate",
         "imgdiff",
         "minigzip",
         "lz4",
@@ -553,6 +556,32 @@
     ],
 }
 
+python_binary_host {
+    name: "fsverity_manifest_generator",
+    srcs: [
+        "fsverity_manifest_generator.py",
+    ],
+    libs: [
+        "fsverity_digests_proto_python",
+        "releasetools_common",
+    ],
+    required: [
+        "aapt2",
+        "apksigner",
+        "fsverity",
+    ],
+}
+
+python_binary_host {
+    name: "fsverity_metadata_generator",
+    srcs: [
+        "fsverity_metadata_generator.py",
+    ],
+    required: [
+        "fsverity",
+    ],
+}
+
 //
 // Tests.
 //
diff --git a/tools/releasetools/add_img_to_target_files.py b/tools/releasetools/add_img_to_target_files.py
index 2a4b56b..0c39827 100644
--- a/tools/releasetools/add_img_to_target_files.py
+++ b/tools/releasetools/add_img_to_target_files.py
@@ -759,6 +759,7 @@
 
   has_recovery = OPTIONS.info_dict.get("no_recovery") != "true"
   has_boot = OPTIONS.info_dict.get("no_boot") != "true"
+  has_init_boot = OPTIONS.info_dict.get("init_boot") == "true"
   has_vendor_boot = OPTIONS.info_dict.get("vendor_boot") == "true"
 
   # {vendor,odm,product,system_ext,vendor_dlkm,odm_dlkm, system, system_other}.img
@@ -819,6 +820,17 @@
           if output_zip:
             boot_image.AddToZip(output_zip)
 
+  if has_init_boot:
+    banner("init_boot")
+    init_boot_image = common.GetBootableImage(
+        "IMAGES/init_boot.img", "init_boot.img", OPTIONS.input_tmp, "INIT_BOOT")
+    if init_boot_image:
+      partitions['init_boot'] = os.path.join(OPTIONS.input_tmp, "IMAGES", "init_boot.img")
+      if not os.path.exists(partitions['init_boot']):
+        init_boot_image.WriteToDir(OPTIONS.input_tmp)
+        if output_zip:
+          init_boot_image.AddToZip(output_zip)
+
   if has_vendor_boot:
     banner("vendor_boot")
     vendor_boot_image = common.GetVendorBootImage(
diff --git a/tools/releasetools/build_image.py b/tools/releasetools/build_image.py
index 34aa1a6..a4377c7 100755
--- a/tools/releasetools/build_image.py
+++ b/tools/releasetools/build_image.py
@@ -35,9 +35,6 @@
 import common
 import verity_utils
 
-from fsverity_digests_pb2 import FSVerityDigests
-from fsverity_metadata_generator import FSVerityMetadataGenerator
-
 logger = logging.getLogger(__name__)
 
 OPTIONS = common.OPTIONS
@@ -451,69 +448,6 @@
 
   return mkfs_output
 
-def GenerateFSVerityMetadata(in_dir, fsverity_path, apk_key_path, apk_manifest_path, apk_out_path):
-  """Generates fsverity metadata files.
-
-  By setting PRODUCT_SYSTEM_FSVERITY_GENERATE_METADATA := true, fsverity
-  metadata files will be generated. For the input files, see `patterns` below.
-
-  One metadata file per one input file will be generated with the suffix
-  .fsv_meta. e.g. system/framework/foo.jar -> system/framework/foo.jar.fsv_meta
-  Also a mapping file containing fsverity digests will be generated to
-  system/etc/security/fsverity/BuildManifest.apk.
-
-  Args:
-    in_dir: temporary working directory (same as BuildImage)
-    fsverity_path: path to host tool fsverity
-    apk_key_path: path to key (e.g. build/make/target/product/security/platform)
-    apk_manifest_path: path to AndroidManifest.xml for APK
-    apk_out_path: path to the output APK
-
-  Returns:
-    None. The files are generated directly under in_dir.
-  """
-
-  patterns = [
-    "system/framework/*.jar",
-    "system/framework/oat/*/*.oat",
-    "system/framework/oat/*/*.vdex",
-    "system/framework/oat/*/*.art",
-    "system/etc/boot-image.prof",
-    "system/etc/dirty-image-objects",
-  ]
-  files = []
-  for pattern in patterns:
-    files += glob.glob(os.path.join(in_dir, pattern))
-  files = sorted(set(files))
-
-  generator = FSVerityMetadataGenerator(fsverity_path)
-  generator.set_hash_alg("sha256")
-
-  digests = FSVerityDigests()
-  for f in files:
-    generator.generate(f)
-    # f is a full path for now; make it relative so it starts with {mount_point}/
-    digest = digests.digests[os.path.relpath(f, in_dir)]
-    digest.digest = generator.digest(f)
-    digest.hash_alg = "sha256"
-
-  temp_dir = common.MakeTempDir()
-
-  os.mkdir(os.path.join(temp_dir, "assets"))
-  metadata_path = os.path.join(temp_dir, "assets", "build_manifest")
-  with open(metadata_path, "wb") as f:
-    f.write(digests.SerializeToString())
-
-  apk_path = os.path.join(in_dir, apk_out_path)
-
-  common.RunAndCheckOutput(["aapt2", "link",
-      "-A", os.path.join(temp_dir, "assets"),
-      "-o", apk_path,
-      "--manifest", apk_manifest_path])
-  common.RunAndCheckOutput(["apksigner", "sign", "--in", apk_path,
-      "--cert", apk_key_path + ".x509.pem",
-      "--key", apk_key_path + ".pk8"])
-
 def BuildImage(in_dir, prop_dict, out_file, target_out=None):
   """Builds an image for the files under in_dir and writes it to out_file.
 
@@ -541,13 +475,6 @@
   elif fs_type.startswith("f2fs") and prop_dict.get("f2fs_compress") == "true":
     fs_spans_partition = False
 
-  if "fsverity_generate_metadata" in prop_dict:
-    GenerateFSVerityMetadata(in_dir,
-        fsverity_path=prop_dict["fsverity"],
-        apk_key_path=prop_dict["fsverity_apk_key"],
-        apk_manifest_path=prop_dict["fsverity_apk_manifest"],
-        apk_out_path=prop_dict["fsverity_apk_out"])
-
   # Get a builder for creating an image that's to be verified by Verified Boot,
   # or None if not applicable.
   verity_image_builder = verity_utils.CreateVerityImageBuilder(prop_dict)
@@ -801,11 +728,6 @@
     copy_prop("system_root_image", "system_root_image")
     copy_prop("root_dir", "root_dir")
     copy_prop("root_fs_config", "root_fs_config")
-    copy_prop("fsverity", "fsverity")
-    copy_prop("fsverity_generate_metadata", "fsverity_generate_metadata")
-    copy_prop("fsverity_apk_key","fsverity_apk_key")
-    copy_prop("fsverity_apk_manifest","fsverity_apk_manifest")
-    copy_prop("fsverity_apk_out","fsverity_apk_out")
   elif mount_point == "data":
     # Copy the generic fs type first, override with specific one if available.
     copy_prop("flash_logical_block_size", "flash_logical_block_size")
diff --git a/tools/releasetools/check_target_files_signatures.py b/tools/releasetools/check_target_files_signatures.py
index 6e02e4d..0f56fb9 100755
--- a/tools/releasetools/check_target_files_signatures.py
+++ b/tools/releasetools/check_target_files_signatures.py
@@ -65,10 +65,13 @@
 # extra field anyway).
 # Issue #14315: https://bugs.python.org/issue14315, fixed in Python 2.7.8 and
 # Python 3.5.0 alpha 1.
+
+
 class MyZipInfo(zipfile.ZipInfo):
   def _decodeExtra(self):
     pass
 
+
 zipfile.ZipInfo = MyZipInfo
 
 
@@ -83,6 +86,7 @@
 
 
 def AddProblem(msg):
+  logger.error(msg)
   PROBLEMS.append(" ".join(PROBLEM_PREFIX) + " " + msg)
 
 
@@ -204,7 +208,7 @@
       for info in apk.infolist():
         filename = info.filename
         if (filename.startswith("META-INF/") and
-            info.filename.endswith((".DSA", ".RSA"))):
+                info.filename.endswith((".DSA", ".RSA"))):
           pkcs7 = apk.read(filename)
           cert = CertFromPKCS7(pkcs7, filename)
           if not cert:
@@ -266,7 +270,7 @@
                    stdout=subprocess.PIPE)
     manifest, err = p.communicate()
     if err:
-      AddProblem("failed to read manifest")
+      AddProblem("failed to read manifest " + full_filename)
       return
 
     self.shared_uid = None
@@ -279,15 +283,15 @@
         name = m.group(1)
         if name == "android:sharedUserId":
           if self.shared_uid is not None:
-            AddProblem("multiple sharedUserId declarations")
+            AddProblem("multiple sharedUserId declarations " + full_filename)
           self.shared_uid = m.group(2)
         elif name == "package":
           if self.package is not None:
-            AddProblem("multiple package declarations")
+            AddProblem("multiple package declarations " + full_filename)
           self.package = m.group(2)
 
     if self.package is None:
-      AddProblem("no package declaration")
+      AddProblem("no package declaration " + full_filename)
 
 
 class TargetFiles(object):
@@ -400,7 +404,12 @@
     for _, digest in order:
       print("%s:" % (ALL_CERTS.Get(digest),))
       apks = by_digest[digest]
-      apks.sort()
+      apks.sort(key=lambda x: x[0])
+      for i in range(1, len(apks)):
+        pkgname, apk = apks[i]
+        if pkgname == apks[i-1][0]:
+          print("Both {} and {} have same package name {}".format(
+              apk.filename, apks[i-1][1].filename, pkgname))
       for _, apk in apks:
         if apk.shared_uid:
           print("  %-*s  %-*s  [%s]" % (self.max_fn_len, apk.filename,
@@ -527,8 +536,5 @@
   try:
     r = main(sys.argv[1:])
     sys.exit(r)
-  except common.ExternalError as e:
-    print("\n   ERROR: %s\n" % (e,))
-    sys.exit(1)
   finally:
     common.Cleanup()
diff --git a/tools/releasetools/common.py b/tools/releasetools/common.py
index 64ac95a..e5c68bc 100644
--- a/tools/releasetools/common.py
+++ b/tools/releasetools/common.py
@@ -73,6 +73,7 @@
         self.search_path = os.environ["ANDROID_HOST_OUT"]
     self.signapk_shared_library_path = "lib64"   # Relative to search_path
     self.extra_signapk_args = []
+    self.aapt2_path = "aapt2"
     self.java_path = "java"  # Use the one on the path by default.
     self.java_args = ["-Xmx2048m"]  # The default JVM args.
     self.android_jar_path = None
@@ -111,7 +112,7 @@
 # descriptor into vbmeta.img. When adding a new entry here, the
 # AVB_FOOTER_ARGS_BY_PARTITION in sign_target_files_apks need to be updated
 # accordingly.
-AVB_PARTITIONS = ('boot', 'dtbo', 'odm', 'product', 'pvmfw', 'recovery',
+AVB_PARTITIONS = ('boot', 'init_boot', 'dtbo', 'odm', 'product', 'pvmfw', 'recovery',
                   'system', 'system_ext', 'vendor', 'vendor_boot',
                   'vendor_dlkm', 'odm_dlkm')
 
@@ -130,7 +131,7 @@
 ]
 
 # Partitions with a build.prop file
-PARTITIONS_WITH_BUILD_PROP = PARTITIONS_WITH_CARE_MAP + ['boot']
+PARTITIONS_WITH_BUILD_PROP = PARTITIONS_WITH_CARE_MAP + ['boot', 'init_boot']
 
 # See sysprop.mk. If file is moved, add new search paths here; don't remove
 # existing search paths.
@@ -935,9 +936,9 @@
   def FromInputFile(input_file, name, placeholder_values=None, ramdisk_format=RamdiskFormat.LZ4):
     """Loads the build.prop file and builds the attributes."""
 
-    if name == "boot":
+    if name in ("boot", "init_boot"):
       data = PartitionBuildProps._ReadBootPropFile(
-          input_file, ramdisk_format=ramdisk_format)
+          input_file, name, ramdisk_format=ramdisk_format)
     else:
       data = PartitionBuildProps._ReadPartitionPropFile(input_file, name)
 
@@ -946,15 +947,16 @@
     return props
 
   @staticmethod
-  def _ReadBootPropFile(input_file, ramdisk_format):
+  def _ReadBootPropFile(input_file, partition_name, ramdisk_format):
     """
     Read build.prop for boot image from input_file.
     Return empty string if not found.
     """
+    image_path = 'IMAGES/' + partition_name + '.img'
     try:
-      boot_img = ExtractFromInputFile(input_file, 'IMAGES/boot.img')
+      boot_img = ExtractFromInputFile(input_file, image_path)
     except KeyError:
-      logger.warning('Failed to read IMAGES/boot.img')
+      logger.warning('Failed to read %s', image_path)
       return ''
     prop_file = GetBootImageBuildProp(boot_img, ramdisk_format=ramdisk_format)
     if prop_file is None:
@@ -1394,34 +1396,52 @@
   return "{}:{}:{}".format(partition, rollback_index_location, pubkey_path)
 
 
-def AppendGkiSigningArgs(cmd):
-  """Append GKI signing arguments for mkbootimg."""
-  # e.g., --gki_signing_key path/to/signing_key
-  #       --gki_signing_algorithm SHA256_RSA4096"
+def _HasGkiCertificationArgs():
+  return ("gki_signing_key_path" in OPTIONS.info_dict and
+          "gki_signing_algorithm" in OPTIONS.info_dict)
 
+
+def _GenerateGkiCertificate(image, image_name, partition_name):
   key_path = OPTIONS.info_dict.get("gki_signing_key_path")
-  # It's fine that a non-GKI boot.img has no gki_signing_key_path.
-  if not key_path:
-    return
+  algorithm = OPTIONS.info_dict.get("gki_signing_algorithm")
 
   if not os.path.exists(key_path) and OPTIONS.search_path:
     new_key_path = os.path.join(OPTIONS.search_path, key_path)
     if os.path.exists(new_key_path):
       key_path = new_key_path
 
-  # Checks key_path exists, before appending --gki_signing_* args.
+  # Checks key_path exists, before processing --gki_signing_* args.
   if not os.path.exists(key_path):
     raise ExternalError(
         'gki_signing_key_path: "{}" not found'.format(key_path))
 
-  algorithm = OPTIONS.info_dict.get("gki_signing_algorithm")
-  if key_path and algorithm:
-    cmd.extend(["--gki_signing_key", key_path,
-                "--gki_signing_algorithm", algorithm])
+  output_certificate = tempfile.NamedTemporaryFile()
+  cmd = [
+      "generate_gki_certificate",
+      "--name", image_name,
+      "--algorithm", algorithm,
+      "--key", key_path,
+      "--output", output_certificate.name,
+      image,
+  ]
 
-    signature_args = OPTIONS.info_dict.get("gki_signing_signature_args")
-    if signature_args:
-      cmd.extend(["--gki_signing_signature_args", signature_args])
+  signature_args = OPTIONS.info_dict.get("gki_signing_signature_args", "")
+  signature_args = signature_args.strip()
+  if signature_args:
+    cmd.extend(["--additional_avb_args", signature_args])
+
+  args = OPTIONS.info_dict.get(
+      "avb_" + partition_name + "_add_hash_footer_args", "")
+  args = args.strip()
+  if args:
+    cmd.extend(["--additional_avb_args", args])
+
+  RunAndCheckOutput(cmd)
+
+  output_certificate.seek(os.SEEK_SET, 0)
+  data = output_certificate.read()
+  output_certificate.close()
+  return data
 
 
 def BuildVBMeta(image_path, partitions, name, needed_partitions):
@@ -1539,12 +1559,16 @@
       logger.info("Excluded kernel binary from recovery image.")
     else:
       kernel = "kernel"
+  elif partition_name == "init_boot":
+    pass
   else:
     kernel = image_name.replace("boot", "kernel")
     kernel = kernel.replace(".img", "")
   if kernel and not os.access(os.path.join(sourcedir, kernel), os.F_OK):
     return None
 
+  kernel_path = os.path.join(sourcedir, kernel) if kernel else None
+
   if has_ramdisk and not os.access(os.path.join(sourcedir, "RAMDISK"), os.F_OK):
     return None
 
@@ -1559,8 +1583,8 @@
   mkbootimg = os.getenv('MKBOOTIMG') or "mkbootimg"
 
   cmd = [mkbootimg]
-  if kernel:
-    cmd += ["--kernel", os.path.join(sourcedir, kernel)]
+  if kernel_path is not None:
+    cmd.extend(["--kernel", kernel_path])
 
   fn = os.path.join(sourcedir, "second")
   if os.access(fn, os.F_OK):
@@ -1593,20 +1617,38 @@
       # Fall back to "mkbootimg_args" for recovery image
       # in case "recovery_mkbootimg_args" is not set.
       args = info_dict.get("mkbootimg_args")
+  elif partition_name == "init_boot":
+    args = info_dict.get("mkbootimg_init_args")
   else:
     args = info_dict.get("mkbootimg_args")
   if args and args.strip():
     cmd.extend(shlex.split(args))
 
-  args = info_dict.get("mkbootimg_version_args")
-  if args and args.strip():
-    cmd.extend(shlex.split(args))
+  boot_signature = None
+  if _HasGkiCertificationArgs():
+    # Certify GKI images.
+    boot_signature_bytes = b''
+    if kernel_path is not None:
+      boot_signature_bytes += _GenerateGkiCertificate(
+          kernel_path, "generic_kernel", "boot")
+    if has_ramdisk:
+      boot_signature_bytes += _GenerateGkiCertificate(
+          ramdisk_img.name, "generic_ramdisk", "init_boot")
+
+    if len(boot_signature_bytes) > 0:
+      boot_signature = tempfile.NamedTemporaryFile()
+      boot_signature.write(boot_signature_bytes)
+      boot_signature.flush()
+      cmd.extend(["--boot_signature", boot_signature.name])
+  else:
+    # Certified GKI boot/init_boot image mustn't set 'mkbootimg_version_args'.
+    args = info_dict.get("mkbootimg_version_args")
+    if args and args.strip():
+      cmd.extend(shlex.split(args))
 
   if has_ramdisk:
     cmd.extend(["--ramdisk", ramdisk_img.name])
 
-  AppendGkiSigningArgs(cmd)
-
   img_unsigned = None
   if info_dict.get("vboot"):
     img_unsigned = tempfile.NamedTemporaryFile()
@@ -1684,6 +1726,9 @@
     ramdisk_img.close()
   img.close()
 
+  if boot_signature is not None:
+    boot_signature.close()
+
   return data
 
 
@@ -1694,8 +1739,8 @@
   Args:
     image_path: The full path of the image, e.g., /path/to/boot.img.
     prebuilt_name: The prebuilt image name, e.g., boot.img, boot-5.4-gz.img,
-        boot-5.10.img, recovery.img.
-    partition_name: The partition name, e.g., 'boot' or 'recovery'.
+        boot-5.10.img, recovery.img or init_boot.img.
+    partition_name: The partition name, e.g., 'boot', 'init_boot' or 'recovery'.
     info_dict: The information dict read from misc_info.txt.
   """
   if info_dict is None:
@@ -1719,6 +1764,35 @@
     RunAndCheckOutput(cmd)
 
 
+def HasRamdisk(partition_name, info_dict=None):
+  """Returns true/false to see if a bootable image should have a ramdisk.
+
+  Args:
+    partition_name: The partition name, e.g., 'boot', 'init_boot' or 'recovery'.
+    info_dict: The information dict read from misc_info.txt.
+  """
+  if info_dict is None:
+    info_dict = OPTIONS.info_dict
+
+  if partition_name != "boot":
+    return True  # init_boot.img or recovery.img has a ramdisk.
+
+  if info_dict.get("recovery_as_boot") == "true":
+    return True  # the recovery-as-boot boot.img has a RECOVERY ramdisk.
+
+  if info_dict.get("system_root_image") == "true":
+    # The ramdisk content is merged into the system.img, so there is NO
+    # ramdisk in the boot.img or boot-<kernel version>.img.
+    return False
+
+  if info_dict.get("init_boot") == "true":
+    # The ramdisk is moved to the init_boot.img, so there is NO
+    # ramdisk in the boot.img or boot-<kernel version>.img.
+    return False
+
+  return True
+
+
 def GetBootableImage(name, prebuilt_name, unpack_dir, tree_subdir,
                      info_dict=None, two_step_image=False):
   """Return a File object with the desired bootable image.
@@ -1740,23 +1814,18 @@
     logger.info("using prebuilt %s from IMAGES...", prebuilt_name)
     return File.FromLocalFile(name, prebuilt_path)
 
+  partition_name = tree_subdir.lower()
   prebuilt_path = os.path.join(unpack_dir, "PREBUILT_IMAGES", prebuilt_name)
   if os.path.exists(prebuilt_path):
     logger.info("Re-signing prebuilt %s from PREBUILT_IMAGES...", prebuilt_name)
     signed_img = MakeTempFile()
     shutil.copy(prebuilt_path, signed_img)
-    partition_name = tree_subdir.lower()
     _SignBootableImage(signed_img, prebuilt_name, partition_name, info_dict)
     return File.FromLocalFile(name, signed_img)
 
   logger.info("building image from target_files %s...", tree_subdir)
 
-  # With system_root_image == "true", we don't pack ramdisk into the boot image.
-  # Unless "recovery_as_boot" is specified, in which case we carry the ramdisk
-  # for recovery.
-  has_ramdisk = (info_dict.get("system_root_image") != "true" or
-                 prebuilt_name != "boot.img" or
-                 info_dict.get("recovery_as_boot") == "true")
+  has_ramdisk = HasRamdisk(partition_name, info_dict)
 
   fs_config = "META/" + tree_subdir.lower() + "_filesystem_config.txt"
   data = _BuildBootableImage(prebuilt_name, os.path.join(unpack_dir, tree_subdir),
@@ -2155,8 +2224,8 @@
 def GetMinSdkVersion(apk_name):
   """Gets the minSdkVersion declared in the APK.
 
-  It calls 'aapt2' to query the embedded minSdkVersion from the given APK file.
-  This can be both a decimal number (API Level) or a codename.
+  It calls OPTIONS.aapt2_path to query the embedded minSdkVersion from the given
+  APK file. This can be both a decimal number (API Level) or a codename.
 
   Args:
     apk_name: The APK filename.
@@ -2168,7 +2237,7 @@
     ExternalError: On failing to obtain the min SDK version.
   """
   proc = Run(
-      ["aapt2", "dump", "badging", apk_name], stdout=subprocess.PIPE,
+      [OPTIONS.aapt2_path, "dump", "badging", apk_name], stdout=subprocess.PIPE,
       stderr=subprocess.PIPE)
   stdoutdata, stderrdata = proc.communicate()
   if proc.returncode != 0:
@@ -2444,7 +2513,7 @@
     opts, args = getopt.getopt(
         argv, "hvp:s:x:" + extra_opts,
         ["help", "verbose", "path=", "signapk_path=",
-         "signapk_shared_library_path=", "extra_signapk_args=",
+         "signapk_shared_library_path=", "extra_signapk_args=", "aapt2_path=",
          "java_path=", "java_args=", "android_jar_path=", "public_key_suffix=",
          "private_key_suffix=", "boot_signer_path=", "boot_signer_args=",
          "verity_signer_path=", "verity_signer_args=", "device_specific=",
@@ -2468,6 +2537,8 @@
       OPTIONS.signapk_shared_library_path = a
     elif o in ("--extra_signapk_args",):
       OPTIONS.extra_signapk_args = shlex.split(a)
+    elif o in ("--aapt2_path",):
+      OPTIONS.aapt2_path = a
     elif o in ("--java_path",):
       OPTIONS.java_path = a
     elif o in ("--java_args",):
@@ -3863,7 +3934,10 @@
   disable_sparse = OPTIONS.info_dict.get(which + "_disable_sparse")
 
   image_blocks = int(image_size) // 4096 - 1
-  assert image_blocks > 0, "blocks for {} must be positive".format(which)
+  # It's OK for image_blocks to be 0, because care map ranges are inclusive.
+  # So 0-0 means "just block 0", which is valid.
+  assert image_blocks >= 0, "blocks for {} must be non-negative, image size: {}".format(
+      which, image_size)
 
   # For sparse images, we will only check the blocks that are listed in the care
   # map, i.e. the ones with meaningful data.
diff --git a/tools/releasetools/fsverity_manifest_generator.py b/tools/releasetools/fsverity_manifest_generator.py
new file mode 100644
index 0000000..527cddb
--- /dev/null
+++ b/tools/releasetools/fsverity_manifest_generator.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python3
+#
+# Copyright 2022 Google Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""
+`fsverity_manifest_generator` generates build manifest APK file containing
+digests of target files. The APK file is signed so the manifest inside the APK
+can be trusted.
+"""
+
+import argparse
+import common
+import os
+import subprocess
+import sys
+from fsverity_digests_pb2 import FSVerityDigests
+
+HASH_ALGORITHM = 'sha256'
+
+def _digest(fsverity_path, input_file):
+  cmd = [fsverity_path, 'digest', input_file]
+  cmd.extend(['--compact'])
+  cmd.extend(['--hash-alg', HASH_ALGORITHM])
+  out = subprocess.check_output(cmd, universal_newlines=True).strip()
+  return bytes(bytearray.fromhex(out))
+
+if __name__ == '__main__':
+  p = argparse.ArgumentParser()
+  p.add_argument(
+      '--output',
+      help='Path to the output manifest APK',
+      required=True)
+  p.add_argument(
+      '--fsverity-path',
+      help='path to the fsverity program',
+      required=True)
+  p.add_argument(
+      '--aapt2-path',
+      help='path to the aapt2 program',
+      required=True)
+  p.add_argument(
+      '--min-sdk-version',
+      help='minimum supported sdk version of the generated manifest apk',
+      required=True)
+  p.add_argument(
+      '--framework-res',
+      help='path to framework-res.apk',
+      required=True)
+  p.add_argument(
+      '--apksigner-path',
+      help='path to the apksigner program',
+      required=True)
+  p.add_argument(
+      '--apk-key-path',
+      help='path to the apk key',
+      required=True)
+  p.add_argument(
+      '--apk-manifest-path',
+      help='path to AndroidManifest.xml',
+      required=True)
+  p.add_argument(
+      '--base-dir',
+      help='directory to use as a relative root for the inputs',
+      required=True)
+  p.add_argument(
+      'inputs',
+      nargs='+',
+      help='input file for the build manifest')
+  args = p.parse_args(sys.argv[1:])
+
+  digests = FSVerityDigests()
+  for f in sorted(args.inputs):
+    # f is a full path for now; make it relative so it starts with {mount_point}/
+    digest = digests.digests[os.path.relpath(f, args.base_dir)]
+    digest.digest = _digest(args.fsverity_path, f)
+    digest.hash_alg = HASH_ALGORITHM
+
+  temp_dir = common.MakeTempDir()
+
+  os.mkdir(os.path.join(temp_dir, "assets"))
+  metadata_path = os.path.join(temp_dir, "assets", "build_manifest.pb")
+  with open(metadata_path, "wb") as f:
+    f.write(digests.SerializeToString())
+
+  common.RunAndCheckOutput([args.aapt2_path, "link",
+      "-A", os.path.join(temp_dir, "assets"),
+      "-o", args.output,
+      "--min-sdk-version", args.min_sdk_version,
+      "-I", args.framework_res,
+      "--manifest", args.apk_manifest_path])
+  common.RunAndCheckOutput([args.apksigner_path, "sign", "--in", args.output,
+      "--cert", args.apk_key_path + ".x509.pem",
+      "--key", args.apk_key_path + ".pk8"])
diff --git a/tools/releasetools/fsverity_metadata_generator.py b/tools/releasetools/fsverity_metadata_generator.py
index 666efd5..fa7cd39 100644
--- a/tools/releasetools/fsverity_metadata_generator.py
+++ b/tools/releasetools/fsverity_metadata_generator.py
@@ -55,6 +55,9 @@
     self.set_hash_alg("sha256")
     self.set_signature('none')
 
+  def set_key_format(self, key_format):
+    self._key_format = key_format
+
   def set_key(self, key):
     self._key = key
 
@@ -130,14 +133,17 @@
       cmd.append(input_file)
       cmd.append(sig_file)
 
-      # convert DER private key to PEM
-      pem_key = os.path.join(work_dir, 'key.pem')
-      key_cmd = ['openssl', 'pkcs8']
-      key_cmd.extend(['-inform', 'DER'])
-      key_cmd.extend(['-in', self._key])
-      key_cmd.extend(['-nocrypt'])
-      key_cmd.extend(['-out', pem_key])
-      subprocess.check_call(key_cmd)
+      # If key is DER, convert DER private key to PEM
+      if self._key_format == 'der':
+        pem_key = os.path.join(work_dir, 'key.pem')
+        key_cmd = ['openssl', 'pkcs8']
+        key_cmd.extend(['-inform', 'DER'])
+        key_cmd.extend(['-in', self._key])
+        key_cmd.extend(['-nocrypt'])
+        key_cmd.extend(['-out', pem_key])
+        subprocess.check_call(key_cmd)
+      else:
+        pem_key = self._key
 
       cmd.extend(['--key', pem_key])
       cmd.extend(['--cert', self._cert])
@@ -172,6 +178,7 @@
           out.write(sig)
       else:
         out.write(pack('<I', SIG_TYPE_NONE))
+        out.write(pack('<I', 0))
 
       # 4. merkle tree
       with open(merkletree_file, 'rb') as f:
@@ -196,8 +203,13 @@
       'input',
       help='input file to be signed')
   p.add_argument(
+      '--key-format',
+      choices=['pem', 'der'],
+      default='der',
+      help='format of the input key. Default is der')
+  p.add_argument(
       '--key',
-      help='PKCS#8 private key file in DER format')
+      help='PKCS#8 private key file')
   p.add_argument(
       '--cert',
       help='x509 certificate file in PEM format')
@@ -227,5 +239,6 @@
       raise ValueError("To generate signature, key and cert must be set")
     generator.set_key(args.key)
     generator.set_cert(args.cert)
+  generator.set_key_format(args.key_format)
   generator.set_hash_alg(args.hash_alg)
   generator.generate(args.input, args.output)
diff --git a/tools/releasetools/img_from_target_files.py b/tools/releasetools/img_from_target_files.py
index cbb51e1..0b2b187 100755
--- a/tools/releasetools/img_from_target_files.py
+++ b/tools/releasetools/img_from_target_files.py
@@ -124,7 +124,7 @@
 
   for image_path in [name for name in namelist if name.startswith('IMAGES/')]:
     image = os.path.basename(image_path)
-    if OPTIONS.bootable_only and image not in('boot.img', 'recovery.img', 'bootloader'):
+    if OPTIONS.bootable_only and image not in('boot.img', 'recovery.img', 'bootloader', 'init_boot.img'):
       continue
     if not image.endswith('.img') and image != 'bootloader':
       continue
diff --git a/tools/releasetools/ota_from_target_files.py b/tools/releasetools/ota_from_target_files.py
index c21de14..9b9422c 100755
--- a/tools/releasetools/ota_from_target_files.py
+++ b/tools/releasetools/ota_from_target_files.py
@@ -230,6 +230,13 @@
 
   --compressor_types
       A colon ':' separated list of compressors. Allowed values are bz2 and brotli.
+
+  --enable_zucchini
+      Whether to enable to zucchini feature. Will generate smaller OTA but uses more memory.
+
+  --enable_lz4diff
+      Whether to enable lz4diff feature. Will generate smaller OTA for EROFS but
+      uses more memory.
 """
 
 from __future__ import print_function
@@ -299,6 +306,8 @@
 OPTIONS.enable_vabc_xor = True
 OPTIONS.force_minor_version = None
 OPTIONS.compressor_types = None
+OPTIONS.enable_zucchini = True
+OPTIONS.enable_lz4diff = False
 
 POSTINSTALL_CONFIG = 'META/postinstall_config.txt'
 DYNAMIC_PARTITION_INFO = 'META/dynamic_partitions_info.txt'
@@ -1141,6 +1150,32 @@
     partition_timestamps_flags = GeneratePartitionTimestampFlags(
         metadata.postcondition.partition_state)
 
+  if not ota_utils.IsZucchiniCompatible(source_file, target_file):
+    OPTIONS.enable_zucchini = False
+
+  additional_args += ["--enable_zucchini",
+                      str(OPTIONS.enable_zucchini).lower()]
+
+  if not ota_utils.IsLz4diffCompatible(source_file, target_file):
+    logger.warn(
+        "Source build doesn't support lz4diff, or source/target don't have compatible lz4diff versions. Disabling lz4diff.")
+    OPTIONS.enable_lz4diff = False
+
+  additional_args += ["--enable_lz4diff",
+                      str(OPTIONS.enable_lz4diff).lower()]
+
+  if source_file and OPTIONS.enable_lz4diff:
+    input_tmp = common.UnzipTemp(source_file, ["META/liblz4.so"])
+    liblz4_path = os.path.join(input_tmp, "META", "liblz4.so")
+    assert os.path.exists(
+        liblz4_path), "liblz4.so not found in META/ dir of target file {}".format(liblz4_path)
+    logger.info("Enabling lz4diff %s", liblz4_path)
+    additional_args += ["--liblz4_path", liblz4_path]
+    erofs_compression_param = OPTIONS.target_info_dict.get(
+        "erofs_default_compressor")
+    assert erofs_compression_param is not None, "'erofs_default_compressor' not found in META/misc_info.txt of target build. This is required to enable lz4diff."
+    additional_args += ["--erofs_compression_param", erofs_compression_param]
+
   if OPTIONS.disable_vabc:
     additional_args += ["--disable_vabc", "true"]
   if OPTIONS.enable_vabc_xor:
@@ -1321,11 +1356,18 @@
     elif o == "--vabc_downgrade":
       OPTIONS.vabc_downgrade = True
     elif o == "--enable_vabc_xor":
+      assert a.lower() in ["true", "false"]
       OPTIONS.enable_vabc_xor = a.lower() != "false"
     elif o == "--force_minor_version":
       OPTIONS.force_minor_version = a
     elif o == "--compressor_types":
       OPTIONS.compressor_types = a
+    elif o == "--enable_zucchini":
+      assert a.lower() in ["true", "false"]
+      OPTIONS.enable_zucchini = a.lower() != "false"
+    elif o == "--enable_lz4diff":
+      assert a.lower() in ["true", "false"]
+      OPTIONS.enable_lz4diff = a.lower() != "false"
     else:
       return False
     return True
@@ -1373,6 +1415,8 @@
                                  "enable_vabc_xor=",
                                  "force_minor_version=",
                                  "compressor_types=",
+                                 "enable_zucchin=",
+                                 "enable_lz4diff=",
                              ], extra_option_handler=option_handler)
 
   if len(args) != 2:
diff --git a/tools/releasetools/ota_utils.py b/tools/releasetools/ota_utils.py
index 6c5fc05..6896f83 100644
--- a/tools/releasetools/ota_utils.py
+++ b/tools/releasetools/ota_utils.py
@@ -638,3 +638,60 @@
       target_apex.source_version = source_apex_versions[name]
 
   return target_apex_proto.SerializeToString()
+
+
+def IsLz4diffCompatible(source_file: str, target_file: str):
+  """Check whether lz4diff versions in two builds are compatible
+
+  Args:
+    source_file: Path to source build's target_file.zip
+    target_file: Path to target build's target_file.zip
+
+  Returns:
+    bool true if and only if lz4diff versions are compatible
+  """
+  if source_file is None or target_file is None:
+    return False
+  # Right now we enable lz4diff as long as source build has liblz4.so.
+  # In the future we might introduce version system to lz4diff as well.
+  if zipfile.is_zipfile(source_file):
+    with zipfile.ZipFile(source_file, "r") as zfp:
+      return "META/liblz4.so" in zfp.namelist()
+  else:
+    assert os.path.isdir(source_file)
+    return os.path.exists(os.path.join(source_file, "META", "liblz4.so"))
+
+
+def IsZucchiniCompatible(source_file: str, target_file: str):
+  """Check whether zucchini versions in two builds are compatible
+
+  Args:
+    source_file: Path to source build's target_file.zip
+    target_file: Path to target build's target_file.zip
+
+  Returns:
+    bool true if and only if zucchini versions are compatible
+  """
+  if source_file is None or target_file is None:
+    return False
+  assert os.path.exists(source_file)
+  assert os.path.exists(target_file)
+
+  assert zipfile.is_zipfile(source_file) or os.path.isdir(source_file)
+  assert zipfile.is_zipfile(target_file) or os.path.isdir(target_file)
+  _ZUCCHINI_CONFIG_ENTRY_NAME = "META/zucchini_config.txt"
+
+  def ReadEntry(path, entry):
+    # Read an entry inside a .zip file or extracted dir of .zip file
+    if zipfile.is_zipfile(path):
+      with zipfile.ZipFile(path, "r", allowZip64=True) as zfp:
+        if entry in zfp.namelist():
+          return zfp.read(entry).decode()
+    else:
+      entry_path = os.path.join(entry, path)
+      if os.path.exists(entry_path):
+        with open(entry_path, "r") as fp:
+          return fp.read()
+      else:
+        return ""
+  return ReadEntry(source_file, _ZUCCHINI_CONFIG_ENTRY_NAME) == ReadEntry(target_file, _ZUCCHINI_CONFIG_ENTRY_NAME)
diff --git a/tools/releasetools/sign_apex.py b/tools/releasetools/sign_apex.py
index 679f57a..66f5e05 100755
--- a/tools/releasetools/sign_apex.py
+++ b/tools/releasetools/sign_apex.py
@@ -141,7 +141,7 @@
       signing_args=options.get('payload_extra_args'),
       codename_to_api_level_map=options.get(
           'codename_to_api_level_map', {}),
-      sign_tool=options['sign_tool'])
+      sign_tool=options.get('sign_tool', None))
   shutil.copyfile(signed_apex, args[1])
   logger.info("done.")
 
diff --git a/tools/releasetools/sign_target_files_apks.py b/tools/releasetools/sign_target_files_apks.py
index 5626980..c615b84 100755
--- a/tools/releasetools/sign_target_files_apks.py
+++ b/tools/releasetools/sign_target_files_apks.py
@@ -201,6 +201,7 @@
 
 AVB_FOOTER_ARGS_BY_PARTITION = {
     'boot': 'avb_boot_add_hash_footer_args',
+    'init_boot': 'avb_init_boot_add_hash_footer_args',
     'dtbo': 'avb_dtbo_add_hash_footer_args',
     'product': 'avb_product_add_hashtree_footer_args',
     'recovery': 'avb_recovery_add_hash_footer_args',
@@ -519,9 +520,14 @@
                        compressed_extension):
   # maxsize measures the maximum filename length, including the ones to be
   # skipped.
-  maxsize = max(
-      [len(os.path.basename(i.filename)) for i in input_tf_zip.infolist()
-       if GetApkFileInfo(i.filename, compressed_extension, [])[0]])
+  try:
+    maxsize = max(
+        [len(os.path.basename(i.filename)) for i in input_tf_zip.infolist()
+         if GetApkFileInfo(i.filename, compressed_extension, [])[0]])
+  except ValueError:
+    # Sets this to zero for targets without APK files, e.g., gki_arm64.
+    maxsize = 0
+
   system_root_image = misc_info.get("system_root_image") == "true"
 
   for info in input_tf_zip.infolist():
@@ -608,7 +614,7 @@
       common.ZipWriteStr(output_tf_zip, out_info, new_data)
 
     # Replace the certs in *mac_permissions.xml (there could be multiple, such
-    # as {system,vendor}/etc/selinux/{plat,nonplat}_mac_permissions.xml).
+    # as {system,vendor}/etc/selinux/{plat,vendor}_mac_permissions.xml).
     elif filename.endswith("mac_permissions.xml"):
       print("Rewriting %s with new keys." % (filename,))
       new_data = ReplaceCerts(data.decode())
@@ -882,14 +888,27 @@
   except KeyError:
     raise common.ExternalError("can't read META/otakeys.txt from input")
 
-  extra_recovery_keys = misc_info.get("extra_recovery_keys")
-  if extra_recovery_keys:
+  extra_ota_keys_info = misc_info.get("extra_ota_keys")
+  if extra_ota_keys_info:
+    extra_ota_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem"
+                      for k in extra_ota_keys_info.split()]
+    print("extra ota key(s): " + ", ".join(extra_ota_keys))
+  else:
+    extra_ota_keys = []
+  for k in extra_ota_keys:
+    if not os.path.isfile(k):
+      raise common.ExternalError(k + " does not exist or is not a file")
+
+  extra_recovery_keys_info = misc_info.get("extra_recovery_keys")
+  if extra_recovery_keys_info:
     extra_recovery_keys = [OPTIONS.key_map.get(k, k) + ".x509.pem"
-                           for k in extra_recovery_keys.split()]
-    if extra_recovery_keys:
-      print("extra recovery-only key(s): " + ", ".join(extra_recovery_keys))
+                           for k in extra_recovery_keys_info.split()]
+    print("extra recovery-only key(s): " + ", ".join(extra_recovery_keys))
   else:
     extra_recovery_keys = []
+  for k in extra_recovery_keys:
+    if not os.path.isfile(k):
+      raise common.ExternalError(k + " does not exist or is not a file")
 
   mapped_keys = []
   for k in keylist:
@@ -912,13 +931,20 @@
     mapped_keys.append(mapped_devkey + ".x509.pem")
     print("META/otakeys.txt has no keys; using %s for OTA package"
           " verification." % (mapped_keys[0],))
+  for k in mapped_keys:
+    if not os.path.isfile(k):
+      raise common.ExternalError(k + " does not exist or is not a file")
 
   otacerts = [info
               for info in input_tf_zip.infolist()
               if info.filename.endswith("/otacerts.zip")]
   for info in otacerts:
-    print("Rewriting OTA key:", info.filename, mapped_keys)
-    WriteOtacerts(output_tf_zip, info.filename, mapped_keys)
+    if info.filename.startswith(("BOOT/", "RECOVERY/", "VENDOR_BOOT/")):
+      extra_keys = extra_recovery_keys
+    else:
+      extra_keys = extra_ota_keys
+    print("Rewriting OTA key:", info.filename, mapped_keys + extra_keys)
+    WriteOtacerts(output_tf_zip, info.filename, mapped_keys + extra_keys)
 
 
 def ReplaceVerityPublicKey(output_zip, filename, key_path):
diff --git a/tools/releasetools/test_apex_utils.py b/tools/releasetools/test_apex_utils.py
index ed920f2..2aa6f6c 100644
--- a/tools/releasetools/test_apex_utils.py
+++ b/tools/releasetools/test_apex_utils.py
@@ -198,8 +198,9 @@
 
     # pass `false` as a sign_tool to see the invocation error
     with self.assertRaises(common.ExternalError) as cm:
-        signer = apex_utils.ApexApkSigner(apex_path, None, None, sign_tool='false')
-        signer.ProcessApexFile(apk_keys, self.payload_key)
+      signer = apex_utils.ApexApkSigner(
+          apex_path, None, None, sign_tool='false')
+      signer.ProcessApexFile(apk_keys, self.payload_key)
 
     the_exception = cm.exception
-    self.assertIn('Failed to run command \'[\'false\'', the_exception.message)
+    self.assertIn('Failed to run command \'[\'false\'', str(the_exception))
diff --git a/tools/releasetools/test_common.py b/tools/releasetools/test_common.py
index e42d417..7dd365f 100644
--- a/tools/releasetools/test_common.py
+++ b/tools/releasetools/test_common.py
@@ -1631,66 +1631,7 @@
     self.assertEqual('3', chained_partition_args[1])
     self.assertTrue(os.path.exists(chained_partition_args[2]))
 
-  @test_utils.SkipIfExternalToolsUnavailable()
-  def test_AppendGkiSigningArgs_NoSigningKeyPath(self):
-    # A non-GKI boot.img has no gki_signing_key_path.
-    common.OPTIONS.info_dict = {
-        # 'gki_signing_key_path': pubkey,
-        'gki_signing_algorithm': 'SHA256_RSA4096',
-        'gki_signing_signature_args': '--prop foo:bar',
-    }
-
-    # Tests no --gki_signing_* args are appended if there is no
-    # gki_signing_key_path.
-    cmd = ['mkbootimg', '--header_version', '4']
-    expected_cmd = ['mkbootimg', '--header_version', '4']
-    common.AppendGkiSigningArgs(cmd)
-    self.assertEqual(cmd, expected_cmd)
-
-  def test_AppendGkiSigningArgs_NoSigningAlgorithm(self):
-    pubkey = os.path.join(self.testdata_dir, 'testkey_gki.pem')
-    with open(pubkey, 'wb') as f:
-      f.write(b'\x00' * 100)
-    self.assertTrue(os.path.exists(pubkey))
-
-    # Tests no --gki_signing_* args are appended if there is no
-    # gki_signing_algorithm.
-    common.OPTIONS.info_dict = {
-        'gki_signing_key_path': pubkey,
-        # 'gki_signing_algorithm': 'SHA256_RSA4096',
-        'gki_signing_signature_args': '--prop foo:bar',
-    }
-
-    cmd = ['mkbootimg', '--header_version', '4']
-    expected_cmd = ['mkbootimg', '--header_version', '4']
-    common.AppendGkiSigningArgs(cmd)
-    self.assertEqual(cmd, expected_cmd)
-
-  @test_utils.SkipIfExternalToolsUnavailable()
-  def test_AppendGkiSigningArgs(self):
-    pubkey = os.path.join(self.testdata_dir, 'testkey_gki.pem')
-    with open(pubkey, 'wb') as f:
-      f.write(b'\x00' * 100)
-    self.assertTrue(os.path.exists(pubkey))
-
-    common.OPTIONS.info_dict = {
-        'gki_signing_key_path': pubkey,
-        'gki_signing_algorithm': 'SHA256_RSA4096',
-        'gki_signing_signature_args': '--prop foo:bar',
-    }
-    cmd = ['mkbootimg', '--header_version', '4']
-    common.AppendGkiSigningArgs(cmd)
-
-    expected_cmd = [
-      'mkbootimg', '--header_version', '4',
-      '--gki_signing_key', pubkey,
-      '--gki_signing_algorithm', 'SHA256_RSA4096',
-      '--gki_signing_signature_args', '--prop foo:bar'
-    ]
-    self.assertEqual(cmd, expected_cmd)
-
-  @test_utils.SkipIfExternalToolsUnavailable()
-  def test_AppendGkiSigningArgs_KeyPathNotFound(self):
+  def test_GenerateGkiCertificate_KeyPathNotFound(self):
     pubkey = os.path.join(self.testdata_dir, 'no_testkey_gki.pem')
     self.assertFalse(os.path.exists(pubkey))
 
@@ -1699,41 +1640,11 @@
         'gki_signing_algorithm': 'SHA256_RSA4096',
         'gki_signing_signature_args': '--prop foo:bar',
     }
-    cmd = ['mkbootimg', '--header_version', '4']
-    self.assertRaises(common.ExternalError, common.AppendGkiSigningArgs, cmd)
+    test_file = tempfile.NamedTemporaryFile()
+    self.assertRaises(common.ExternalError, common._GenerateGkiCertificate,
+                      test_file.name, 'generic_kernel', 'boot')
 
-  @test_utils.SkipIfExternalToolsUnavailable()
-  def test_AppendGkiSigningArgs_SearchKeyPath(self):
-    pubkey = 'testkey_gki.pem'
-    self.assertFalse(os.path.exists(pubkey))
-
-    # Tests it should replace the pubkey with an existed key under
-    # OPTIONS.search_path, i.e., os.path.join(OPTIONS.search_path, pubkey).
-    search_path_dir = common.MakeTempDir()
-    search_pubkey = os.path.join(search_path_dir, pubkey)
-    with open(search_pubkey, 'wb') as f:
-      f.write(b'\x00' * 100)
-    self.assertTrue(os.path.exists(search_pubkey))
-
-    common.OPTIONS.search_path = search_path_dir
-    common.OPTIONS.info_dict = {
-        'gki_signing_key_path': pubkey,
-        'gki_signing_algorithm': 'SHA256_RSA4096',
-        'gki_signing_signature_args': '--prop foo:bar',
-    }
-    cmd = ['mkbootimg', '--header_version', '4']
-    common.AppendGkiSigningArgs(cmd)
-
-    expected_cmd = [
-      'mkbootimg', '--header_version', '4',
-      '--gki_signing_key', search_pubkey,
-      '--gki_signing_algorithm', 'SHA256_RSA4096',
-      '--gki_signing_signature_args', '--prop foo:bar'
-    ]
-    self.assertEqual(cmd, expected_cmd)
-
-  @test_utils.SkipIfExternalToolsUnavailable()
-  def test_AppendGkiSigningArgs_SearchKeyPathNotFound(self):
+  def test_GenerateGkiCertificate_SearchKeyPathNotFound(self):
     pubkey = 'no_testkey_gki.pem'
     self.assertFalse(os.path.exists(pubkey))
 
@@ -1749,9 +1660,9 @@
         'gki_signing_algorithm': 'SHA256_RSA4096',
         'gki_signing_signature_args': '--prop foo:bar',
     }
-    cmd = ['mkbootimg', '--header_version', '4']
-    self.assertRaises(common.ExternalError, common.AppendGkiSigningArgs, cmd)
-
+    test_file = tempfile.NamedTemporaryFile()
+    self.assertRaises(common.ExternalError, common._GenerateGkiCertificate,
+                      test_file.name, 'generic_kernel', 'boot')
 
 class InstallRecoveryScriptFormatTest(test_utils.ReleaseToolsTestCase):
   """Checks the format of install-recovery.sh.
diff --git a/tools/releasetools/test_sign_target_files_apks.py b/tools/releasetools/test_sign_target_files_apks.py
index 92dca9a..0f13add 100644
--- a/tools/releasetools/test_sign_target_files_apks.py
+++ b/tools/releasetools/test_sign_target_files_apks.py
@@ -62,6 +62,9 @@
       'avb_boot_add_hash_footer_args':
           ('--prop com.android.build.boot.os_version:R '
            '--prop com.android.build.boot.security_patch:2019-09-05'),
+      'avb_init_boot_add_hash_footer_args':
+          ('--prop com.android.build.boot.os_version:R '
+           '--prop com.android.build.boot.security_patch:2019-09-05'),
       'avb_system_add_hashtree_footer_args':
           ('--prop com.android.build.system.os_version:R '
            '--prop com.android.build.system.security_patch:2019-09-05 '
@@ -77,6 +80,9 @@
       'avb_boot_add_hash_footer_args':
           ('--prop com.android.build.boot.os_version:R '
            '--prop com.android.build.boot.security_patch:2019-09-05'),
+      'avb_init_boot_add_hash_footer_args':
+          ('--prop com.android.build.boot.os_version:R '
+           '--prop com.android.build.boot.security_patch:2019-09-05'),
       'avb_system_add_hashtree_footer_args':
           ('--prop com.android.build.system.os_version:R '
            '--prop com.android.build.system.security_patch:2019-09-05 '
diff --git a/tools/signapk/src/com/android/signapk/SignApk.java b/tools/signapk/src/com/android/signapk/SignApk.java
index 232e119..c127dbe 100644
--- a/tools/signapk/src/com/android/signapk/SignApk.java
+++ b/tools/signapk/src/com/android/signapk/SignApk.java
@@ -204,26 +204,23 @@
      * If a console doesn't exist, reads the password from stdin
      * If a console exists, reads the password from console and returns it as a string.
      *
-     * @param keyFile The file containing the private key.  Used to prompt the user.
+     * @param keyFileName Name of the file containing the private key.  Used to prompt the user.
      */
-    private static String readPassword(File keyFile) {
+    private static char[] readPassword(String keyFileName) {
         Console console;
-        char[] pwd;
         if ((console = System.console()) == null) {
-            System.out.print("Enter password for " + keyFile + " (password will not be hidden): ");
+            System.out.print(
+                "Enter password for " + keyFileName + " (password will not be hidden): ");
             System.out.flush();
             BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
             try {
-                return stdin.readLine();
+                String result = stdin.readLine();
+                return result == null ? null : result.toCharArray();
             } catch (IOException ex) {
                 return null;
             }
         } else {
-            if ((pwd = console.readPassword("[%s]", "Enter password for " + keyFile)) != null) {
-                return String.valueOf(pwd);
-            } else {
-                return null;
-            }
+            return console.readPassword("[%s]", "Enter password for " + keyFileName);
         }
     }
 
@@ -246,11 +243,8 @@
             return null;
         }
 
-        char[] password = readPassword(keyFile).toCharArray();
-
         SecretKeyFactory skFactory = SecretKeyFactory.getInstance(epkInfo.getAlgName());
-        Key key = skFactory.generateSecret(new PBEKeySpec(password));
-
+        Key key = skFactory.generateSecret(new PBEKeySpec(readPassword(keyFile.getPath())));
         Cipher cipher = Cipher.getInstance(epkInfo.getAlgName());
         cipher.init(Cipher.DECRYPT_MODE, key, epkInfo.getAlgParameters());
 
@@ -305,10 +299,10 @@
 
     /** Get a PKCS#11 private key from keyStore */
     private static PrivateKey loadPrivateKeyFromKeyStore(
-            final KeyStore keyStore, final String keyName, final String password)
+            final KeyStore keyStore, final String keyName)
             throws CertificateException, KeyStoreException, NoSuchAlgorithmException,
                     UnrecoverableKeyException, UnrecoverableEntryException {
-        final Key key = keyStore.getKey(keyName, password == null ? null : password.toCharArray());
+        final Key key = keyStore.getKey(keyName, readPassword(keyName));
         final PrivateKeyEntry privateKeyEntry = (PrivateKeyEntry) keyStore.getEntry(keyName, null);
         if (privateKeyEntry == null) {
         throw new Error(
@@ -1201,10 +1195,8 @@
                 if (keyStore == null) {
                     privateKey[i] = readPrivateKey(new File(args[argNum]));
                 } else {
-                    String[] splits = args[argNum].split(":", 2);
-                    final String keyAlias = splits[0];
-                    final String password = splits.length > 1 ? splits[1] : null;
-                    privateKey[i] = loadPrivateKeyFromKeyStore(keyStore, keyAlias, password);
+                    final String keyAlias = args[argNum];
+                    privateKey[i] = loadPrivateKeyFromKeyStore(keyStore, keyAlias);
                 }
             }
             inputJar = new JarFile(new File(inputFilename), false);  // Don't verify.
diff --git a/tools/warn/html_writer.py b/tools/warn/html_writer.py
index ef173bc..3fa822a 100644
--- a/tools/warn/html_writer.py
+++ b/tools/warn/html_writer.py
@@ -63,6 +63,11 @@
 from .severity import Severity
 
 
+# Report files with this number of warnings or more.
+LIMIT_WARNINGS_PER_FILE = 100
+# Report files/directories with this percentage of total warnings or more.
+LIMIT_PERCENT_WARNINGS = 1
+
 HTML_HEAD_SCRIPTS = """\
   <script type="text/javascript">
   function expand(id) {
@@ -89,12 +94,13 @@
   </script>
   <style type="text/css">
   th,td{border-collapse:collapse; border:1px solid black;}
-  .button{color:blue;font-size:110%;font-weight:bolder;}
+  .button{color:blue;font-size:100%;font-weight:bolder;}
   .bt{color:black;background-color:transparent;border:none;outline:none;
       font-size:140%;font-weight:bolder;}
   .c0{background-color:#e0e0e0;}
   .c1{background-color:#d0d0d0;}
   .t1{border-collapse:collapse; width:100%; border:1px solid black;}
+  .box{margin:5pt; padding:5pt; border:1px solid;}
   </style>
   <script src="https://www.gstatic.com/charts/loader.js"></script>
 """
@@ -287,14 +293,14 @@
 #     sort by project, severity, warn_id, warning_message
 def emit_buttons(writer):
   """Write the button elements in HTML."""
-  writer('<button class="button" onclick="expandCollapse(1);">'
+  writer('<p><button class="button" onclick="expandCollapse(1);">'
          'Expand all warnings</button>\n'
          '<button class="button" onclick="expandCollapse(0);">'
          'Collapse all warnings</button>\n'
-         '<button class="button" onclick="groupBySeverity();">'
+         '<p><button class="button" onclick="groupBySeverity();">'
          'Group warnings by severity</button>\n'
          '<button class="button" onclick="groupByProject();">'
-         'Group warnings by project</button><br>')
+         'Group warnings by project</button>')
 
 
 def all_patterns(category):
@@ -559,6 +565,11 @@
 """
 
 
+# Emit a JavaScript const number
+def emit_const_number(name, value, writer):
+  writer('const ' + name + ' = ' + str(value) + ';')
+
+
 # Emit a JavaScript const string
 def emit_const_string(name, value, writer):
   writer('const ' + name + ' = "' + escape_string(value) + '";')
@@ -602,6 +613,8 @@
   emit_const_string('FlagPlatform', flags.platform, writer)
   emit_const_string('FlagURL', flags.url, writer)
   emit_const_string('FlagSeparator', flags.separator, writer)
+  emit_const_number('LimitWarningsPerFile', LIMIT_WARNINGS_PER_FILE, writer)
+  emit_const_number('LimitPercentWarnings', LIMIT_PERCENT_WARNINGS, writer)
   emit_const_string_array('SeverityColors', [s.color for s in Severity.levels],
                           writer)
   emit_const_string_array('SeverityHeaders',
@@ -624,8 +637,8 @@
 
 DRAW_TABLE_JAVASCRIPT = """
 google.charts.load('current', {'packages':['table']});
-google.charts.setOnLoadCallback(drawTable);
-function drawTable() {
+google.charts.setOnLoadCallback(genTables);
+function genSelectedProjectsTable() {
   var data = new google.visualization.DataTable();
   data.addColumn('string', StatsHeader[0]);
   for (var i=1; i<StatsHeader.length; i++) {
@@ -638,12 +651,167 @@
     }
   }
   var table = new google.visualization.Table(
-      document.getElementById('stats_table'));
+      document.getElementById('selected_projects_section'));
   table.draw(data, {allowHtml: true, alternatingRowStyle: true});
 }
+// Global TopDirs and TopFiles are computed later by genTables.
+window.TopDirs = [];
+window.TopFiles = [];
+function computeTopDirsFiles() {
+  var numWarnings = WarningMessages.length;
+  var warningsOfFiles = {};
+  var warningsOfDirs = {};
+  var subDirs = {};
+  function addOneWarning(map, key) {
+    map[key] = 1 + ((key in map) ? map[key] : 0);
+  }
+  for (var i = 0; i < numWarnings; i++) {
+    var file = WarningMessages[i].replace(/:.*/, "");
+    addOneWarning(warningsOfFiles, file);
+    var dirs = file.split("/");
+    var dir = dirs[0];
+    addOneWarning(warningsOfDirs, dir);
+    for (var d = 1; d < dirs.length - 1; d++) {
+      var subDir = dir + "/" + dirs[d];
+      if (!(dir in subDirs)) {
+        subDirs[dir] = {};
+      }
+      subDirs[dir][subDir] = 1;
+      dir = subDir;
+      addOneWarning(warningsOfDirs, dir);
+    }
+  }
+  var minDirWarnings = numWarnings*(LimitPercentWarnings/100);
+  var minFileWarnings = Math.min(LimitWarningsPerFile, minDirWarnings);
+  // Each row in TopDirs and TopFiles has
+  // [index, {v:<num_of_warnings>, f:<percent>}, file_or_dir_name]
+  function countWarnings(minWarnings, warningsOf, isDir) {
+    var rows = [];
+    for (var name in warningsOf) {
+      if (isDir && name in subDirs && Object.keys(subDirs[name]).length < 2) {
+        continue; // skip a directory if it has only one subdir
+      }
+      var count = warningsOf[name];
+      if (count >= minWarnings) {
+        name = isDir ? (name + "/...") : name;
+        var percent = (100*count/numWarnings).toFixed(1);
+        var countFormat = count + ' (' + percent + '%)';
+        rows.push([0, {v:count, f:countFormat}, name]);
+      }
+    }
+    rows.sort((a,b) => b[1].v - a[1].v);
+    for (var i=0; i<rows.length; i++) {
+      rows[i][0] = i;
+    }
+    return rows;
+  }
+  TopDirs = countWarnings(minDirWarnings, warningsOfDirs, true);
+  TopFiles = countWarnings(minFileWarnings, warningsOfFiles, false);
+}
+function genTopDirsFilesTables() {
+  computeTopDirsFiles();
+  function addTable(name, divName, rows, clickFunction) {
+    var data = new google.visualization.DataTable();
+    data.addColumn("number", "index"); // not shown in view
+    data.addColumn("number", "# of warnings");
+    data.addColumn("string", name);
+    data.addRows(rows);
+    var formatter = new google.visualization.PatternFormat(
+      '<p onclick="' + clickFunction + '({0})">{2}</p>');
+    formatter.format(data, [0, 1, 2], 2);
+    var view = new google.visualization.DataView(data);
+    view.setColumns([1,2]); // hide the index column
+    var table = new google.visualization.Table(
+        document.getElementById(divName));
+    table.draw(view, {allowHtml: true, alternatingRowStyle: true});
+  }
+  addTable("Directory", "top_dirs_table", TopDirs, "selectDir");
+  addTable("File", "top_files_table", TopFiles, "selectFile");
+}
+function selectDirFile(idx, rows, dirFile) {
+  if (rows.length <= idx) {
+    return;
+  }
+  var name = rows[idx][2];
+  var spanName = "selected_" + dirFile + "_name";
+  document.getElementById(spanName).innerHTML = name;
+  var divName = "selected_" + dirFile + "_warnings";
+  var numWarnings = rows[idx][1].v;
+  var prefix = name.replace(/\\.\\.\\.$/, "");
+  var data = new google.visualization.DataTable();
+  data.addColumn('string', numWarnings + ' warnings in ' + name);
+  var getWarningMessage = (FlagPlatform == "chrome")
+        ? ((x) => addURLToLine(WarningMessages[Warnings[x][2]],
+                               WarningLinks[Warnings[x][3]]))
+        : ((x) => addURL(WarningMessages[Warnings[x][2]]));
+  for (var i = 0; i < Warnings.length; i++) {
+    if (WarningMessages[Warnings[i][2]].startsWith(prefix)) {
+      data.addRow([getWarningMessage(i)]);
+    }
+  }
+  var table = new google.visualization.Table(
+      document.getElementById(divName));
+  table.draw(data, {allowHtml: true, alternatingRowStyle: true});
+}
+function selectDir(idx) {
+  selectDirFile(idx, TopDirs, "directory")
+}
+function selectFile(idx) {
+  selectDirFile(idx, TopFiles, "file");
+}
+function genTables() {
+  genSelectedProjectsTable();
+  if (WarningMessages.length > 1) {
+    genTopDirsFilesTables();
+  }
+}
 """
 
 
+def dump_boxed_section(writer, func):
+  writer('<div class="box">')
+  func()
+  writer('</div>')
+
+
+def dump_section_header(writer, table_name, section_title):
+  writer('<h3><b><button id="' + table_name + '_mark" class="bt"\n' +
+         ' onclick="expand(\'' + table_name + '\');">&#x2295</button></b>\n' +
+         section_title + '</h3>')
+
+
+def dump_table_section(writer, table_name, section_title):
+  dump_section_header(writer, table_name, section_title)
+  writer('<div id="' + table_name + '" style="display:none;"></div>')
+
+
+def dump_dir_file_section(writer, dir_file, table_name, section_title):
+  section_name = 'top_' + dir_file + '_section'
+  dump_section_header(writer, section_name, section_title)
+  writer('<div id="' + section_name + '" style="display:none;">')
+  writer('<div id="' + table_name + '"></div>')
+  def subsection():
+    subsection_name = 'selected_' + dir_file + '_warnings'
+    subsection_title = ('Warnings in <span id="selected_' + dir_file +
+                        '_name">(click a ' + dir_file +
+                        ' in the above table)</span>')
+    dump_section_header(writer, subsection_name, subsection_title)
+    writer('<div id="' + subsection_name + '" style="display:none;"></div>')
+  dump_boxed_section(writer, subsection)
+  writer('</div>')
+
+
+# HTML output has the following major div elements:
+#  selected_projects_section
+#  top_directory_section
+#    top_dirs_table
+#    selected_directory_warnings
+#  top_file_section
+#    top_files_table
+#    selected_file_warnings
+#  all_warnings_section
+#    warning_groups
+#    fixed_warnings
 def dump_html(flags, output_stream, warning_messages, warning_links,
               warning_records, header_str, warn_patterns, project_names):
   """Dump the flags output to output_stream."""
@@ -651,20 +819,44 @@
   dump_html_prologue('Warnings for ' + header_str, writer, warn_patterns,
                      project_names)
   dump_stats(writer, warn_patterns)
-  writer('<br><div id="stats_table"></div><br>')
-  writer('\n<script>')
-  emit_js_data(writer, flags, warning_messages, warning_links, warning_records,
-               warn_patterns, project_names)
-  writer(SCRIPTS_FOR_WARNING_GROUPS)
-  writer('</script>')
-  emit_buttons(writer)
-  # Warning messages are grouped by severities or project names.
-  writer('<br><div id="warning_groups"></div>')
-  if flags.byproject:
-    writer('<script>groupByProject();</script>')
-  else:
-    writer('<script>groupBySeverity();</script>')
-  dump_fixed(writer, warn_patterns)
+  writer('<br><br>Press &#x2295 to show section content,'
+         ' and &#x2296 to hide the content.')
+  def section1():
+    dump_table_section(writer, 'selected_projects_section',
+                       'Number of warnings in preselected project directories')
+  def section2():
+    dump_dir_file_section(
+        writer, 'directory', 'top_dirs_table',
+        'Directories with at least ' +
+        str(LIMIT_PERCENT_WARNINGS) + '% warnings')
+  def section3():
+    dump_dir_file_section(
+        writer, 'file', 'top_files_table',
+        'Files with at least ' +
+        str(LIMIT_PERCENT_WARNINGS) + '% or ' +
+        str(LIMIT_WARNINGS_PER_FILE) + ' warnings')
+  def section4():
+    writer('<script>')
+    emit_js_data(writer, flags, warning_messages, warning_links,
+                 warning_records, warn_patterns, project_names)
+    writer(SCRIPTS_FOR_WARNING_GROUPS)
+    writer('</script>')
+    dump_section_header(writer, 'all_warnings_section',
+                        'All warnings grouped by severities or projects')
+    writer('<div id="all_warnings_section" style="display:none;">')
+    emit_buttons(writer)
+    # Warning messages are grouped by severities or project names.
+    writer('<br><div id="warning_groups"></div>')
+    if flags.byproject:
+      writer('<script>groupByProject();</script>')
+    else:
+      writer('<script>groupBySeverity();</script>')
+    dump_fixed(writer, warn_patterns)
+    writer('</div>')
+  dump_boxed_section(writer, section1)
+  dump_boxed_section(writer, section2)
+  dump_boxed_section(writer, section3)
+  dump_boxed_section(writer, section4)
   dump_html_epilogue(writer)
 
 
diff --git a/tools/zipalign/tests/src/align_test.cpp b/tools/zipalign/tests/src/align_test.cpp
index 96d4f73..ff45187 100644
--- a/tools/zipalign/tests/src/align_test.cpp
+++ b/tools/zipalign/tests/src/align_test.cpp
@@ -3,6 +3,7 @@
 
 #include "ZipAlign.h"
 
+#include <filesystem>
 #include <stdio.h>
 #include <string>
 
@@ -16,9 +17,15 @@
   return test_data_dir + filename;
 }
 
+static std::string GetTempPath(const std::string& filename) {
+  std::filesystem::path temp_path = std::filesystem::path(testing::TempDir());
+  temp_path += filename;
+  return temp_path.string();
+}
+
 TEST(Align, Unaligned) {
   const std::string src = GetTestPath("unaligned.zip");
-  const std::string dst = GetTestPath("unaligned_out.zip");
+  const std::string dst = GetTempPath("unaligned_out.zip");
 
   int processed = process(src.c_str(), dst.c_str(), 4, true, false, 4096);
   ASSERT_EQ(0, processed);
@@ -29,8 +36,8 @@
 
 TEST(Align, DoubleAligment) {
   const std::string src = GetTestPath("unaligned.zip");
-  const std::string tmp = GetTestPath("da_aligned.zip");
-  const std::string dst = GetTestPath("da_d_aligner.zip");
+  const std::string tmp = GetTempPath("da_aligned.zip");
+  const std::string dst = GetTempPath("da_d_aligner.zip");
 
   int processed = process(src.c_str(), tmp.c_str(), 4, true, false, 4096);
   ASSERT_EQ(0, processed);
@@ -60,7 +67,7 @@
 // Directory.
 TEST(Align, Holes) {
   const std::string src = GetTestPath("holes.zip");
-  const std::string dst = GetTestPath("holes_out.zip");
+  const std::string dst = GetTempPath("holes_out.zip");
 
   int processed = process(src.c_str(), dst.c_str(), 4, true, false, 4096);
   ASSERT_EQ(0, processed);
@@ -72,7 +79,7 @@
 // Align a zip where LFH order and CD entries differ.
 TEST(Align, DifferenteOrders) {
   const std::string src = GetTestPath("diffOrders.zip");
-  const std::string dst = GetTestPath("diffOrders_out.zip");
+  const std::string dst = GetTempPath("diffOrders_out.zip");
 
   int processed = process(src.c_str(), dst.c_str(), 4, true, false, 4096);
   ASSERT_EQ(0, processed);