source_tree_test_config: restructure config for better readability

I lay out the intention here: https://crbug.com/1130018#c19

This structure makes it a lot clearer how the rules take effect and
combine. Roughly speaking,
1. Take default set of CQ test suites
2. Apply subtractive rules to remove some suites
3. Apply additive rules to add additional suites
4. Combine rule results for files in different source test configs

After this, I'll dual write to infra/config, then update infra/go to use
the new fields, then remove dual writing, then remove the old proto
messages/fields.

Bug: 1130018
Change-Id: I104b751912001368783bcfbce82816949322b4e3
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/infra/proto/+/2436933
Commit-Queue: Sean Abraham <seanabraham@chromium.org>
Reviewed-by: Dhanya Ganesh <dhanyaganesh@chromium.org>
Reviewed-by: Prathmesh Prabhu <pprabhu@google.com>
diff --git a/go/testplans/source_tree_test_config.pb.go b/go/testplans/source_tree_test_config.pb.go
index 15f7030..3383bab 100644
--- a/go/testplans/source_tree_test_config.pb.go
+++ b/go/testplans/source_tree_test_config.pb.go
@@ -60,6 +60,141 @@
 	return nil
 }
 
+// A description of how to remove test suites from the default set of test
+// suites in Chrome OS CQ, as a result of source configuration.
+// See http://go/cq-source-config
+type SubtractiveRule struct {
+	// Whether to disable hardware test suites.
+	DisableHwTests bool `protobuf:"varint,1,opt,name=disable_hw_tests,json=disableHwTests,proto3" json:"disable_hw_tests,omitempty"`
+	// Whether to disable virtual machine test suites.
+	DisableVmTests bool `protobuf:"varint,2,opt,name=disable_vm_tests,json=disableVmTests,proto3" json:"disable_vm_tests,omitempty"`
+	// Prunes away all default suites except those in the provided test groups.
+	// e.g. setting this to "bluetooth" will have CQ run only those test suites
+	// in the "bluetooth" test group.
+	OnlyKeepAllSuitesInGroups *TestGroups `protobuf:"bytes,3,opt,name=only_keep_all_suites_in_groups,json=onlyKeepAllSuitesInGroups,proto3" json:"only_keep_all_suites_in_groups,omitempty"`
+	// Prunes away all default suites except one in each provided test group.
+	// e.g. can be used to ensure a test suite is launched on each of the
+	// supported Chrome OS processor architectures.
+	OnlyKeepOneSuiteFromEachGroup *TestGroups `protobuf:"bytes,4,opt,name=only_keep_one_suite_from_each_group,json=onlyKeepOneSuiteFromEachGroup,proto3" json:"only_keep_one_suite_from_each_group,omitempty"`
+	XXX_NoUnkeyedLiteral          struct{}    `json:"-"`
+	XXX_unrecognized              []byte      `json:"-"`
+	XXX_sizecache                 int32       `json:"-"`
+}
+
+func (m *SubtractiveRule) Reset()         { *m = SubtractiveRule{} }
+func (m *SubtractiveRule) String() string { return proto.CompactTextString(m) }
+func (*SubtractiveRule) ProtoMessage()    {}
+func (*SubtractiveRule) Descriptor() ([]byte, []int) {
+	return fileDescriptor_9aed450ade7161bc, []int{1}
+}
+
+func (m *SubtractiveRule) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_SubtractiveRule.Unmarshal(m, b)
+}
+func (m *SubtractiveRule) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_SubtractiveRule.Marshal(b, m, deterministic)
+}
+func (m *SubtractiveRule) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_SubtractiveRule.Merge(m, src)
+}
+func (m *SubtractiveRule) XXX_Size() int {
+	return xxx_messageInfo_SubtractiveRule.Size(m)
+}
+func (m *SubtractiveRule) XXX_DiscardUnknown() {
+	xxx_messageInfo_SubtractiveRule.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_SubtractiveRule proto.InternalMessageInfo
+
+func (m *SubtractiveRule) GetDisableHwTests() bool {
+	if m != nil {
+		return m.DisableHwTests
+	}
+	return false
+}
+
+func (m *SubtractiveRule) GetDisableVmTests() bool {
+	if m != nil {
+		return m.DisableVmTests
+	}
+	return false
+}
+
+func (m *SubtractiveRule) GetOnlyKeepAllSuitesInGroups() *TestGroups {
+	if m != nil {
+		return m.OnlyKeepAllSuitesInGroups
+	}
+	return nil
+}
+
+func (m *SubtractiveRule) GetOnlyKeepOneSuiteFromEachGroup() *TestGroups {
+	if m != nil {
+		return m.OnlyKeepOneSuiteFromEachGroup
+	}
+	return nil
+}
+
+// A descriptive of how to add extra test suites to a CQ run based on source
+// configuration. An AdditiveRule will trigger so long as any file matches the
+// relevant source pattern. AdditiveRules combine as a union of additional test
+// suites to run. disable_by_default=True test suites are particularly relevant
+// to AdditiveRules, since AdditiveRules are the only thing that will include
+// them.
+type AdditiveRule struct {
+	// Adds on all test suites in the provided groups, even if they have
+	// disable_by_default=True.
+	// e.g. could be used to trigger wificell testing (which is expensive) based
+	// on the presence of files in wifi-related directories.
+	AddAllSuitesInGroups *TestGroups `protobuf:"bytes,1,opt,name=add_all_suites_in_groups,json=addAllSuitesInGroups,proto3" json:"add_all_suites_in_groups,omitempty"`
+	// Adds on one test suite from each of the provided groups, even if that suite
+	// has disable_by_default=True.
+	// e.g. could be used to trigger wificell testing (which is expensive) on one
+	// board in each Chrome OS processor architecture.
+	AddOneSuiteFromEachGroup *TestGroups `protobuf:"bytes,2,opt,name=add_one_suite_from_each_group,json=addOneSuiteFromEachGroup,proto3" json:"add_one_suite_from_each_group,omitempty"`
+	XXX_NoUnkeyedLiteral     struct{}    `json:"-"`
+	XXX_unrecognized         []byte      `json:"-"`
+	XXX_sizecache            int32       `json:"-"`
+}
+
+func (m *AdditiveRule) Reset()         { *m = AdditiveRule{} }
+func (m *AdditiveRule) String() string { return proto.CompactTextString(m) }
+func (*AdditiveRule) ProtoMessage()    {}
+func (*AdditiveRule) Descriptor() ([]byte, []int) {
+	return fileDescriptor_9aed450ade7161bc, []int{2}
+}
+
+func (m *AdditiveRule) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_AdditiveRule.Unmarshal(m, b)
+}
+func (m *AdditiveRule) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_AdditiveRule.Marshal(b, m, deterministic)
+}
+func (m *AdditiveRule) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_AdditiveRule.Merge(m, src)
+}
+func (m *AdditiveRule) XXX_Size() int {
+	return xxx_messageInfo_AdditiveRule.Size(m)
+}
+func (m *AdditiveRule) XXX_DiscardUnknown() {
+	xxx_messageInfo_AdditiveRule.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_AdditiveRule proto.InternalMessageInfo
+
+func (m *AdditiveRule) GetAddAllSuitesInGroups() *TestGroups {
+	if m != nil {
+		return m.AddAllSuitesInGroups
+	}
+	return nil
+}
+
+func (m *AdditiveRule) GetAddOneSuiteFromEachGroup() *TestGroups {
+	if m != nil {
+		return m.AddOneSuiteFromEachGroup
+	}
+	return nil
+}
+
 // Testing restrictions to apply to a source tree.
 type TestRestriction struct {
 	// Whether to disable hardware test suites.
@@ -82,7 +217,7 @@
 func (m *TestRestriction) String() string { return proto.CompactTextString(m) }
 func (*TestRestriction) ProtoMessage()    {}
 func (*TestRestriction) Descriptor() ([]byte, []int) {
-	return fileDescriptor_9aed450ade7161bc, []int{1}
+	return fileDescriptor_9aed450ade7161bc, []int{3}
 }
 
 func (m *TestRestriction) XXX_Unmarshal(b []byte) error {
@@ -198,7 +333,7 @@
 func (m *SourceTreeTestRestriction) String() string { return proto.CompactTextString(m) }
 func (*SourceTreeTestRestriction) ProtoMessage()    {}
 func (*SourceTreeTestRestriction) Descriptor() ([]byte, []int) {
-	return fileDescriptor_9aed450ade7161bc, []int{2}
+	return fileDescriptor_9aed450ade7161bc, []int{4}
 }
 
 func (m *SourceTreeTestRestriction) XXX_Unmarshal(b []byte) error {
@@ -233,21 +368,86 @@
 	return nil
 }
 
+// A set of test subtractions and additions to perform for a source tree.
+// See http://go/cq-source-config for documentation.
+type SourceTestRules struct {
+	// A file pattern, representing a segment of Chrome OS code.
+	FilePattern *FilePattern `protobuf:"bytes,1,opt,name=file_pattern,json=filePattern,proto3" json:"file_pattern,omitempty"`
+	// A subtractive rule to apply to this file pattern. See the relevant message
+	// documentation.
+	SubtractiveRule *SubtractiveRule `protobuf:"bytes,2,opt,name=subtractive_rule,json=subtractiveRule,proto3" json:"subtractive_rule,omitempty"`
+	// An additive rule to apply to this file pattern. See the relevant message
+	// documentation.
+	AdditiveRule         *AdditiveRule `protobuf:"bytes,3,opt,name=additive_rule,json=additiveRule,proto3" json:"additive_rule,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}      `json:"-"`
+	XXX_unrecognized     []byte        `json:"-"`
+	XXX_sizecache        int32         `json:"-"`
+}
+
+func (m *SourceTestRules) Reset()         { *m = SourceTestRules{} }
+func (m *SourceTestRules) String() string { return proto.CompactTextString(m) }
+func (*SourceTestRules) ProtoMessage()    {}
+func (*SourceTestRules) Descriptor() ([]byte, []int) {
+	return fileDescriptor_9aed450ade7161bc, []int{5}
+}
+
+func (m *SourceTestRules) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_SourceTestRules.Unmarshal(m, b)
+}
+func (m *SourceTestRules) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_SourceTestRules.Marshal(b, m, deterministic)
+}
+func (m *SourceTestRules) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_SourceTestRules.Merge(m, src)
+}
+func (m *SourceTestRules) XXX_Size() int {
+	return xxx_messageInfo_SourceTestRules.Size(m)
+}
+func (m *SourceTestRules) XXX_DiscardUnknown() {
+	xxx_messageInfo_SourceTestRules.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_SourceTestRules proto.InternalMessageInfo
+
+func (m *SourceTestRules) GetFilePattern() *FilePattern {
+	if m != nil {
+		return m.FilePattern
+	}
+	return nil
+}
+
+func (m *SourceTestRules) GetSubtractiveRule() *SubtractiveRule {
+	if m != nil {
+		return m.SubtractiveRule
+	}
+	return nil
+}
+
+func (m *SourceTestRules) GetAdditiveRule() *AdditiveRule {
+	if m != nil {
+		return m.AdditiveRule
+	}
+	return nil
+}
+
 // Configures test restrictions for all relevant source trees.
 // This is the root message.
 type SourceTreeTestCfg struct {
 	// (Source tree, test restriction) pairs.
+	// Will be removed soon in favor of source_test_rules.
 	SourceTreeTestRestriction []*SourceTreeTestRestriction `protobuf:"bytes,1,rep,name=source_tree_test_restriction,json=sourceTreeTestRestriction,proto3" json:"source_tree_test_restriction,omitempty"`
-	XXX_NoUnkeyedLiteral      struct{}                     `json:"-"`
-	XXX_unrecognized          []byte                       `json:"-"`
-	XXX_sizecache             int32                        `json:"-"`
+	// Rules for how to test changes to specified source trees in the codebase.
+	SourceTestRules      []*SourceTestRules `protobuf:"bytes,2,rep,name=source_test_rules,json=sourceTestRules,proto3" json:"source_test_rules,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}           `json:"-"`
+	XXX_unrecognized     []byte             `json:"-"`
+	XXX_sizecache        int32              `json:"-"`
 }
 
 func (m *SourceTreeTestCfg) Reset()         { *m = SourceTreeTestCfg{} }
 func (m *SourceTreeTestCfg) String() string { return proto.CompactTextString(m) }
 func (*SourceTreeTestCfg) ProtoMessage()    {}
 func (*SourceTreeTestCfg) Descriptor() ([]byte, []int) {
-	return fileDescriptor_9aed450ade7161bc, []int{3}
+	return fileDescriptor_9aed450ade7161bc, []int{6}
 }
 
 func (m *SourceTreeTestCfg) XXX_Unmarshal(b []byte) error {
@@ -275,10 +475,20 @@
 	return nil
 }
 
+func (m *SourceTreeTestCfg) GetSourceTestRules() []*SourceTestRules {
+	if m != nil {
+		return m.SourceTestRules
+	}
+	return nil
+}
+
 func init() {
 	proto.RegisterType((*TestGroups)(nil), "testplans.TestGroups")
+	proto.RegisterType((*SubtractiveRule)(nil), "testplans.SubtractiveRule")
+	proto.RegisterType((*AdditiveRule)(nil), "testplans.AdditiveRule")
 	proto.RegisterType((*TestRestriction)(nil), "testplans.TestRestriction")
 	proto.RegisterType((*SourceTreeTestRestriction)(nil), "testplans.SourceTreeTestRestriction")
+	proto.RegisterType((*SourceTestRules)(nil), "testplans.SourceTestRules")
 	proto.RegisterType((*SourceTreeTestCfg)(nil), "testplans.SourceTreeTestCfg")
 }
 
@@ -287,33 +497,47 @@
 }
 
 var fileDescriptor_9aed450ade7161bc = []byte{
-	// 440 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0xc1, 0x6e, 0xd3, 0x40,
-	0x10, 0x86, 0xeb, 0xd4, 0x6d, 0x9d, 0x4d, 0x45, 0x5c, 0x03, 0x91, 0x5b, 0x71, 0xb0, 0x22, 0x24,
-	0x7c, 0xc1, 0x96, 0x12, 0x71, 0xe0, 0x48, 0x2b, 0x20, 0xca, 0x01, 0x90, 0x89, 0x40, 0xe2, 0x62,
-	0xb9, 0x66, 0xec, 0x58, 0x5a, 0xef, 0x24, 0x3b, 0x1b, 0xaa, 0xf2, 0x3a, 0xbc, 0x16, 0x0f, 0x83,
-	0xbc, 0x76, 0x6d, 0xa7, 0x28, 0x37, 0xcf, 0x3f, 0xdf, 0xec, 0x3f, 0x33, 0x1e, 0xf6, 0x4a, 0x01,
-	0xa9, 0x0d, 0x4f, 0x04, 0x85, 0x84, 0x3b, 0x99, 0x42, 0xac, 0x24, 0x40, 0x5c, 0xa9, 0x71, 0x8a,
-	0x22, 0x2b, 0xf2, 0x60, 0x23, 0x51, 0xa1, 0x33, 0x6c, 0xc1, 0xab, 0x49, 0x57, 0x93, 0x62, 0x59,
-	0xa2, 0xa8, 0x91, 0xa9, 0xc7, 0xd8, 0x0a, 0x48, 0x7d, 0x94, 0xb8, 0xdb, 0x90, 0xe3, 0x30, 0x53,
-	0x24, 0x25, 0xb8, 0x86, 0x77, 0xec, 0x0f, 0x23, 0xfd, 0x3d, 0xfd, 0x3b, 0x60, 0xe3, 0x0a, 0x89,
-	0x80, 0x94, 0x2c, 0x52, 0x55, 0xa0, 0x70, 0x7c, 0x66, 0xff, 0x2c, 0x28, 0xb9, 0xe5, 0x10, 0xaf,
-	0xef, 0xb4, 0x31, 0xb9, 0x86, 0x67, 0xf8, 0x56, 0xf4, 0xa4, 0xd1, 0x17, 0x77, 0x55, 0x0d, 0xf5,
-	0xc9, 0x5f, 0x65, 0x43, 0x1e, 0xef, 0x91, 0xdf, 0xca, 0x9a, 0x9c, 0xb3, 0xc9, 0x03, 0x29, 0x50,
-	0xc4, 0x2a, 0x21, 0xd5, 0xf0, 0x27, 0x9a, 0x7f, 0xda, 0x64, 0x3f, 0xa1, 0x58, 0x25, 0xa4, 0xea,
-	0xa2, 0xd7, 0xcc, 0x49, 0xb7, 0x31, 0x0a, 0x7e, 0x5f, 0x8f, 0x9f, 0x57, 0x73, 0xb8, 0x67, 0x9e,
-	0xe1, 0x0f, 0x17, 0x47, 0xd1, 0x38, 0xdd, 0x7e, 0x16, 0xfc, 0xbe, 0x1d, 0xb0, 0xc1, 0x13, 0x4e,
-	0xd8, 0xc7, 0xad, 0x0e, 0x7f, 0xc7, 0x09, 0x3b, 0x7c, 0xc1, 0x9e, 0xe9, 0xd7, 0x01, 0xb3, 0x1e,
-	0x4f, 0xee, 0xd0, 0x33, 0xfc, 0xd1, 0xec, 0x79, 0xd0, 0xee, 0x34, 0xe8, 0x76, 0xb8, 0x38, 0x8a,
-	0x2e, 0x2a, 0x5b, 0xc0, 0xac, 0x13, 0xaf, 0xcf, 0x19, 0xbb, 0xd9, 0x56, 0xf1, 0xf7, 0x35, 0x88,
-	0xa5, 0x69, 0x99, 0xf6, 0xc9, 0xd2, 0xb4, 0x4e, 0xed, 0xb3, 0xe9, 0x1f, 0x83, 0x5d, 0x7e, 0xd5,
-	0x7f, 0x71, 0x25, 0x01, 0x1e, 0x2f, 0xfa, 0x2d, 0x3b, 0xcf, 0x0a, 0x0e, 0xf1, 0x26, 0x51, 0x0a,
-	0xa4, 0xd0, 0xab, 0x1b, 0xcd, 0x26, 0x3d, 0xe7, 0x0f, 0x05, 0x87, 0x2f, 0x75, 0x36, 0x1a, 0x65,
-	0x5d, 0xe0, 0xbc, 0x67, 0xb6, 0xee, 0x59, 0x76, 0xcf, 0xb9, 0x03, 0x5d, 0x7e, 0xf5, 0xa8, 0xf1,
-	0x9e, 0x61, 0x34, 0x56, 0xfb, 0xc2, 0xd2, 0xb4, 0x0c, 0x7b, 0x30, 0xfd, 0xcd, 0x2e, 0xf6, 0x9b,
-	0xbc, 0xc9, 0x72, 0x07, 0xd8, 0x8b, 0xff, 0xee, 0xaf, 0xef, 0x56, 0x5d, 0xd1, 0x68, 0xf6, 0xb2,
-	0xe7, 0x76, 0x70, 0xd0, 0xe8, 0x92, 0x0e, 0xa5, 0xae, 0xdf, 0xfc, 0x98, 0xe7, 0x18, 0xa4, 0x6b,
-	0x89, 0x65, 0xb1, 0x2b, 0x03, 0x94, 0x79, 0xf8, 0x10, 0x20, 0x85, 0x85, 0xc8, 0x64, 0x12, 0xea,
-	0x63, 0x0e, 0x73, 0x0c, 0x5b, 0xaf, 0xdb, 0x53, 0xad, 0xcd, 0xff, 0x05, 0x00, 0x00, 0xff, 0xff,
-	0x7e, 0x3e, 0xeb, 0xcf, 0x2e, 0x03, 0x00, 0x00,
+	// 663 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x54, 0xdf, 0x6e, 0xd3, 0x3e,
+	0x14, 0x5e, 0xba, 0x6e, 0x6b, 0xbd, 0xfe, 0x7e, 0xe9, 0xc2, 0x18, 0xd9, 0xc4, 0x50, 0x55, 0x90,
+	0xe8, 0x0d, 0xad, 0xb4, 0x89, 0x0b, 0x24, 0x6e, 0xb6, 0x69, 0xa3, 0x0c, 0xc1, 0x90, 0x37, 0x0d,
+	0xc4, 0x8d, 0xe5, 0x25, 0x27, 0x6d, 0x84, 0x63, 0x67, 0xb6, 0xb3, 0x69, 0xaf, 0x03, 0xcf, 0xc0,
+	0x0b, 0xf0, 0x06, 0xdc, 0xf3, 0x30, 0x28, 0x4e, 0xda, 0xa4, 0x7f, 0x56, 0x09, 0x71, 0x17, 0x1f,
+	0x7f, 0xe7, 0x3b, 0xdf, 0xf9, 0x4e, 0x7c, 0xd0, 0x73, 0x0d, 0x4a, 0xc7, 0x8c, 0x72, 0xd5, 0x53,
+	0x22, 0x91, 0x1e, 0x10, 0x2d, 0x01, 0x48, 0x1a, 0x25, 0x9e, 0xe0, 0x41, 0x38, 0xe8, 0xc6, 0x52,
+	0x68, 0xe1, 0xd4, 0xc7, 0xc0, 0x9d, 0xad, 0x22, 0xc7, 0x13, 0x51, 0x24, 0x78, 0x06, 0x69, 0xb7,
+	0x10, 0xba, 0x00, 0xa5, 0xdf, 0x48, 0x91, 0xc4, 0xca, 0x71, 0x50, 0x95, 0xd3, 0x08, 0x5c, 0xab,
+	0xb5, 0xdc, 0xa9, 0x63, 0xf3, 0xdd, 0xfe, 0x56, 0x41, 0xf6, 0x79, 0x72, 0xa5, 0x25, 0xf5, 0x74,
+	0x78, 0x03, 0x38, 0x61, 0xe0, 0x74, 0x50, 0xd3, 0x0f, 0x15, 0xbd, 0x62, 0x40, 0x86, 0xb7, 0xa6,
+	0xb0, 0x72, 0xad, 0x96, 0xd5, 0xa9, 0xe1, 0xff, 0xf3, 0x78, 0xff, 0x36, 0xa5, 0x55, 0x65, 0xe4,
+	0x4d, 0x94, 0x23, 0x2b, 0x13, 0xc8, 0xcb, 0x28, 0x43, 0x7e, 0x46, 0x4f, 0x04, 0x67, 0x77, 0xe4,
+	0x2b, 0x40, 0x4c, 0x28, 0x63, 0x44, 0x25, 0xa1, 0x06, 0x45, 0x42, 0x4e, 0x06, 0x46, 0x9d, 0xbb,
+	0xdc, 0xb2, 0x3a, 0xeb, 0x7b, 0x0f, 0xbb, 0xe3, 0x56, 0xba, 0x85, 0x74, 0xbc, 0x9d, 0x26, 0xbf,
+	0x03, 0x88, 0x0f, 0x18, 0x3b, 0x37, 0x99, 0x6f, 0x79, 0xde, 0x15, 0x45, 0x4f, 0x0b, 0x66, 0xc1,
+	0x21, 0x63, 0x26, 0x81, 0x14, 0x11, 0x01, 0xea, 0x0d, 0x33, 0x7e, 0xb7, 0xba, 0x88, 0x7e, 0x77,
+	0x44, 0x7f, 0xc6, 0xc1, 0xd0, 0x9f, 0x48, 0x11, 0x1d, 0x53, 0x6f, 0x68, 0xee, 0xdb, 0x3f, 0x2c,
+	0xd4, 0x38, 0xf0, 0xfd, 0x70, 0xec, 0xd0, 0x7b, 0xe4, 0x52, 0xdf, 0x9f, 0xdf, 0x87, 0xb5, 0xa8,
+	0xd0, 0x26, 0xf5, 0xfd, 0xd9, 0x16, 0x2e, 0xd1, 0x6e, 0x4a, 0x77, 0xbf, 0xf8, 0xca, 0x22, 0xce,
+	0x54, 0xca, 0x7c, 0xdd, 0xbf, 0x2b, 0xc8, 0x4e, 0x81, 0x18, 0x94, 0x96, 0xa1, 0xa7, 0x43, 0xc1,
+	0xff, 0x71, 0xb8, 0xcb, 0x73, 0x87, 0xbb, 0x8f, 0xb6, 0x46, 0x48, 0x2e, 0x38, 0xd1, 0x54, 0xe9,
+	0x1c, 0xbf, 0x62, 0xf0, 0x0f, 0xf2, 0xdb, 0x0f, 0x82, 0x5f, 0x50, 0xa5, 0xb3, 0xa4, 0x17, 0xc8,
+	0xf1, 0xae, 0x89, 0x19, 0x9d, 0xf9, 0xb7, 0xb3, 0x4e, 0xd7, 0x5a, 0x56, 0xa7, 0xde, 0x5f, 0xc2,
+	0xb6, 0x77, 0x7d, 0xc6, 0xd9, 0xdd, 0xb8, 0xcd, 0x1c, 0x4e, 0x99, 0x12, 0x65, 0x78, 0xad, 0x80,
+	0x1f, 0x30, 0x25, 0x0a, 0x78, 0x1f, 0x6d, 0x1a, 0x76, 0x10, 0x41, 0x09, 0xaf, 0xdc, 0xfa, 0x02,
+	0x27, 0xfb, 0x4b, 0x78, 0x23, 0x2d, 0x0b, 0x22, 0x28, 0x82, 0x87, 0x0d, 0x84, 0x8e, 0xae, 0xd3,
+	0xf3, 0xa7, 0x21, 0xf0, 0xd3, 0x6a, 0xad, 0xda, 0x5c, 0x39, 0xad, 0xd6, 0x56, 0x9b, 0x6b, 0xed,
+	0xef, 0x16, 0xda, 0x3e, 0x37, 0x4f, 0xf4, 0x42, 0x02, 0x4c, 0x1b, 0xfd, 0x0a, 0x35, 0x82, 0x90,
+	0x01, 0x89, 0xa9, 0xd6, 0x20, 0x79, 0xfe, 0x7f, 0x6f, 0x95, 0x2a, 0x9f, 0x84, 0x0c, 0x3e, 0x66,
+	0xb7, 0x78, 0x3d, 0x28, 0x0e, 0xce, 0x31, 0x6a, 0x1a, 0xcd, 0xb2, 0xa0, 0xcb, 0x7f, 0x81, 0x9d,
+	0x29, 0xe1, 0xa5, 0x82, 0xd8, 0xd6, 0x93, 0x81, 0xd3, 0x6a, 0xcd, 0x6a, 0x56, 0xda, 0xbf, 0x2c,
+	0x64, 0xe7, 0x2a, 0xd3, 0xfb, 0x84, 0x81, 0x9a, 0xd1, 0x66, 0xfd, 0x95, 0x36, 0x55, 0xec, 0x0b,
+	0x22, 0x13, 0x06, 0x73, 0xb4, 0x4d, 0xad, 0x14, 0x6c, 0xab, 0xa9, 0x1d, 0xf3, 0x1a, 0xfd, 0x47,
+	0xf3, 0x17, 0x95, 0x71, 0x64, 0xf6, 0x3c, 0x2a, 0x71, 0x94, 0x5f, 0x1c, 0x6e, 0xd0, 0xd2, 0xa9,
+	0xfd, 0xd3, 0x42, 0x1b, 0x93, 0xce, 0x1f, 0x05, 0x03, 0x07, 0xd0, 0xe3, 0x99, 0x8d, 0x59, 0xb6,
+	0x30, 0xdd, 0x7b, 0xeb, 0x7b, 0xcf, 0xca, 0x32, 0xef, 0x9b, 0x1e, 0xde, 0x56, 0xf7, 0x0e, 0xf6,
+	0x04, 0x6d, 0x8c, 0xca, 0x98, 0x0a, 0xa9, 0xa3, 0x6e, 0xc5, 0x70, 0xef, 0xcc, 0x72, 0x8f, 0x3c,
+	0xc7, 0xb6, 0x9a, 0x0c, 0x1c, 0xbe, 0xfc, 0xb2, 0x3f, 0x10, 0x5d, 0x6f, 0x28, 0x45, 0x14, 0x26,
+	0x51, 0x57, 0xc8, 0x41, 0x6f, 0x74, 0x10, 0xaa, 0x17, 0xf2, 0x40, 0xd2, 0x9e, 0x59, 0xe3, 0xbd,
+	0x81, 0xe8, 0x8d, 0x79, 0xaf, 0x56, 0x4d, 0x6c, 0xff, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0xad,
+	0x79, 0x36, 0xba, 0x28, 0x06, 0x00, 0x00,
 }
diff --git a/src/testplans/source_tree_test_config.proto b/src/testplans/source_tree_test_config.proto
index c8776bf..0254369 100644
--- a/src/testplans/source_tree_test_config.proto
+++ b/src/testplans/source_tree_test_config.proto
@@ -15,6 +15,43 @@
   repeated string name = 1;
 }
 
+// A description of how to remove test suites from the default set of test
+// suites in Chrome OS CQ, as a result of source configuration.
+// See http://go/cq-source-config
+message SubtractiveRule {
+  // Whether to disable hardware test suites.
+  bool disable_hw_tests = 1;
+  // Whether to disable virtual machine test suites.
+  bool disable_vm_tests = 2;
+  // Prunes away all default suites except those in the provided test groups.
+  // e.g. setting this to "bluetooth" will have CQ run only those test suites
+  // in the "bluetooth" test group.
+  TestGroups only_keep_all_suites_in_groups = 3;
+  // Prunes away all default suites except one in each provided test group.
+  // e.g. can be used to ensure a test suite is launched on each of the
+  // supported Chrome OS processor architectures.
+  TestGroups only_keep_one_suite_from_each_group = 4;
+}
+
+// A descriptive of how to add extra test suites to a CQ run based on source
+// configuration. An AdditiveRule will trigger so long as any file matches the
+// relevant source pattern. AdditiveRules combine as a union of additional test
+// suites to run. disable_by_default=True test suites are particularly relevant
+// to AdditiveRules, since AdditiveRules are the only thing that will include
+// them.
+message AdditiveRule {
+  // Adds on all test suites in the provided groups, even if they have
+  // disable_by_default=True.
+  // e.g. could be used to trigger wificell testing (which is expensive) based
+  // on the presence of files in wifi-related directories.
+  TestGroups add_all_suites_in_groups = 1;
+  // Adds on one test suite from each of the provided groups, even if that suite
+  // has disable_by_default=True.
+  // e.g. could be used to trigger wificell testing (which is expensive) on one
+  // board in each Chrome OS processor architecture.
+  TestGroups add_one_suite_from_each_group = 2;
+}
+
 // Testing restrictions to apply to a source tree.
 message TestRestriction {
   // Whether to disable hardware test suites.
@@ -39,6 +76,8 @@
     TestGroups cq_oneof_test_groups = 9;
   }
 
+
+
   reserved 4, 6;
 }
 
@@ -53,9 +92,28 @@
   reserved 1;
 }
 
+// A set of test subtractions and additions to perform for a source tree.
+// See http://go/cq-source-config for documentation.
+message SourceTestRules {
+  // A file pattern, representing a segment of Chrome OS code.
+  FilePattern file_pattern = 1;
+
+  // A subtractive rule to apply to this file pattern. See the relevant message
+  // documentation.
+  SubtractiveRule subtractive_rule = 2;
+
+  // An additive rule to apply to this file pattern. See the relevant message
+  // documentation.
+  AdditiveRule additive_rule = 3;
+}
+
 // Configures test restrictions for all relevant source trees.
 // This is the root message.
 message SourceTreeTestCfg {
   // (Source tree, test restriction) pairs.
+  // Will be removed soon in favor of source_test_rules.
   repeated SourceTreeTestRestriction source_tree_test_restriction = 1;
+
+  // Rules for how to test changes to specified source trees in the codebase.
+  repeated SourceTestRules source_test_rules = 2;
 }