Repository: armadaproject/armada
Pull request #3552 (2024-04-29 17:16:38)

Move  Submit Check To Scheduler
Previously submission checking was performed in the Armada API. As submission checking required proving that the job could be job could be scheduled, it required  non trivial amounts of the scheduler logic to be executed,  which became more and more undesirable as the scheduler became more complex.

This change there moves the submission check into the scheduler. As a result, the submission check is now carried out asynchronously with a `JobFailed` message being produced if the job cannot be scheduled.  As before,the submission check is only carried out the first time the job is seen and is not continuously re-evaluated.

Note that this PR moves the submission check over pretty much as-is.  There is therefore some scope to further consolidate the submission check logic with that in SchedulingAlgo.




Patch:
diff --git a/client/DotNet/Armada.Client/ClientGenerated.cs b/client/DotNet/Armada.Client/ClientGenerated.cs
index b323d013..b5225faf 100644
--- a/client/DotNet/Armada.Client/ClientGenerated.cs
+++ b/client/DotNet/Armada.Client/ClientGenerated.cs
@@ -1295,6 +1295,9 @@ namespace GResearch.Armada.Client
         [System.Runtime.Serialization.EnumMember(Value = @"DeadlineExceeded")]
         DeadlineExceeded = 3,
     
+        [System.Runtime.Serialization.EnumMember(Value = @"Rejected")]
+        Rejected = 4,
+    
     }
     
     [System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "10.0.27.0 (Newtonsoft.Json v12.0.0.0)")]
diff --git a/config/armada/config.yaml b/config/armada/config.yaml
index 282c12c3..343bce6d 100644
--- a/config/armada/config.yaml
+++ b/config/armada/config.yaml
@@ -34,42 +34,6 @@ eventsApiRedis:
   password: ""
   db: 1
   poolSize: 1000
-# Scheduling config used by the job submitChecker. Only needs the following subset of config options.
-# You may also wish to configure indexedNodeLabels and indexedTaints to speed up checking.
-#
-# This config must be consistent with the scheduling config used by the scheduler.
-# You may want to insert the scheduling config used for the scheduler automatically, e.g., using PyYAML, to guarantee consistency.
-scheduling:
-  supportedResourceTypes:
-    - name: memory
-      resolution: "1"
-    - name: cpu
-      resolution: "1m"
-    - name: ephemeral-storage
-      resolution: "1"
-    - name: nvidia.com/gpu
-      resolution: "1"
-  executorTimeout: "60m"
-  executorUpdateFrequency: "1m"
-  priorityClasses:
-    armada-default:
-      priority: 1000
-      preemptible: false
-      maximumResourceFractionPerQueue:
-        memory: 1.0
-        cpu: 1.0
-    armada-preemptible:
-      priority: 1000
-      preemptible: true
-  indexedResources:
-    - name: "nvidia.com/gpu"
-      resolution: "1"
-    - name: "cpu"
-      resolution: "100m"
-    - name: "memory"
-      resolution: "100Mi"
-    - name: "ephemeral-storage"
-      resolution: "1Gi"
 submission:
   allowedPriorityClassNames:
     armada-default: true
diff --git a/config/scheduler/config.yaml b/config/scheduler/config.yaml
index 79783393..b4f50889 100644
--- a/config/scheduler/config.yaml
+++ b/config/scheduler/config.yaml
@@ -6,6 +6,7 @@ databaseFetchSize: 1000
 pulsarSendTimeout: 5s
 internedStringsCacheSize: 100000
 queueRefreshPeriod: 1s
+disableSubmitCheck: false
 metrics:
   port: 9000
   refreshInterval: 30s
diff --git a/internal/armada/configuration/types.go b/internal/armada/configuration/types.go
index 467e3c3d..9e51c3d2 100644
--- a/internal/armada/configuration/types.go
+++ b/internal/armada/configuration/types.go
@@ -51,8 +51,6 @@ type ArmadaConfig struct {
 
 	// Config relating to job submission.
 	Submission SubmissionConfig
-	// Scheduling config used by the submitChecker.
-	Scheduling SchedulingConfig
 }
 
 type PulsarConfig struct {
diff --git a/internal/armada/mocks/generate.go b/internal/armada/mocks/generate.go
index 8a53285b..8b04b1fa 100644
--- a/internal/armada/mocks/generate.go
+++ b/internal/armada/mocks/generate.go
@@ -1,7 +1,6 @@
 package mocks
 
 // Mock implementations used by tests
-//go:generate mockgen -destination=./mock_submitchecker.go -package=mocks "github.com/armadaproject/armada/internal/scheduler" SubmitScheduleChecker
 //go:generate mockgen -destination=./mock_respository.go -package=mocks "github.com/armadaproject/armada/internal/armada/repository" QueueRepository,JobRepository
 //go:generate mockgen -destination=./mock_deduplicator.go -package=mocks "github.com/armadaproject/armada/internal/armada/submit" Deduplicator,Publisher
 //go:generate mockgen -destination=./mock_authorizer.go -package=mocks "github.com/armadaproject/armada/internal/armada/server" ActionAuthorizer
diff --git a/internal/armada/mocks/mock_submitchecker.go b/internal/armada/mocks/mock_submitchecker.go
deleted file mode 100644
index 442280dc..00000000
--- a/internal/armada/mocks/mock_submitchecker.go
+++ /dev/null
@@ -1,66 +0,0 @@
-// Code generated by MockGen. DO NOT EDIT.
-// Source: github.com/armadaproject/armada/internal/scheduler (interfaces: SubmitScheduleChecker)
-
-// Package mocks is a generated GoMock package.
-package mocks
-
-import (
-	reflect "reflect"
-
-	jobdb "github.com/armadaproject/armada/internal/scheduler/jobdb"
-	armadaevents "github.com/armadaproject/armada/pkg/armadaevents"
-	gomock "github.com/golang/mock/gomock"
-)
-
-// MockSubmitScheduleChecker is a mock of SubmitScheduleChecker interface.
-type MockSubmitScheduleChecker struct {
-	ctrl     *gomock.Controller
-	recorder *MockSubmitScheduleCheckerMockRecorder
-}
-
-// MockSubmitScheduleCheckerMockRecorder is the mock recorder for MockSubmitScheduleChecker.
-type MockSubmitScheduleCheckerMockRecorder struct {
-	mock *MockSubmitScheduleChecker
-}
-
-// NewMockSubmitScheduleChecker creates a new mock instance.
-func NewMockSubmitScheduleChecker(ctrl *gomock.Controller) *MockSubmitScheduleChecker {
-	mock := &MockSubmitScheduleChecker{ctrl: ctrl}
-	mock.recorder = &MockSubmitScheduleCheckerMockRecorder{mock}
-	return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use.
-func (m *MockSubmitScheduleChecker) EXPECT() *MockSubmitScheduleCheckerMockRecorder {
-	return m.recorder
-}
-
-// CheckApiJobs mocks base method.
-func (m *MockSubmitScheduleChecker) CheckApiJobs(arg0 *armadaevents.EventSequence, arg1 string) (bool, string) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "CheckApiJobs", arg0, arg1)
-	ret0, _ := ret[0].(bool)
-	ret1, _ := ret[1].(string)
-	return ret0, ret1
-}
-
-// CheckApiJobs indicates an expected call of CheckApiJobs.
-func (mr *MockSubmitScheduleCheckerMockRecorder) CheckApiJobs(arg0, arg1 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckApiJobs", reflect.TypeOf((*MockSubmitScheduleChecker)(nil).CheckApiJobs), arg0, arg1)
-}
-
-// CheckJobDbJobs mocks base method.
-func (m *MockSubmitScheduleChecker) CheckJobDbJobs(arg0 []*jobdb.Job) (bool, string) {
-	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "CheckJobDbJobs", arg0)
-	ret0, _ := ret[0].(bool)
-	ret1, _ := ret[1].(string)
-	return ret0, ret1
-}
-
-// CheckJobDbJobs indicates an expected call of CheckJobDbJobs.
-func (mr *MockSubmitScheduleCheckerMockRecorder) CheckJobDbJobs(arg0 interface{}) *gomock.Call {
-	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckJobDbJobs", reflect.TypeOf((*MockSubmitScheduleChecker)(nil).CheckJobDbJobs), arg0)
-}
diff --git a/internal/armada/repository/apimessages/conversions.go b/internal/armada/repository/apimessages/conversions.go
index ddcd00c7..2976b168 100644
--- a/internal/armada/repository/apimessages/conversions.go
+++ b/internal/armada/repository/apimessages/conversions.go
@@ -57,6 +57,7 @@ func FromEventSequence(es *armadaevents.EventSequence) ([]*api.EventMessage, err
 			*armadaevents.EventSequence_Event_CancelJobSet,
 			*armadaevents.EventSequence_Event_JobRunSucceeded,
 			*armadaevents.EventSequence_Event_JobRequeued,
+			*armadaevents.EventSequence_Event_JobValidated,
 			*armadaevents.EventSequence_Event_PartitionMarker:
 			// These events have no api analog right now, so we ignore
 			log.Debugf("ignoring event type %T", esEvent)
@@ -436,6 +437,20 @@ func FromInternalJobErrors(queueName string, jobSetName string, time time.Time,
 				},
 			}
 			events = append(events, event)
+		case *armadaevents.Error_JobRejected:
+			event := &api.EventMessage{
+				Events: &api.EventMessage_Failed{
+					Failed: &api.JobFailedEvent{
+						JobId:    jobId,
+						JobSetId: jobSetName,
+						Queue:    queueName,
+						Created:  time,
+						Reason:   reason.JobRejected.Message,
+						Cause:    api.Cause_Rejected,
+					},
+				},
+			}
+			events = append(events, event)
 		default:
 			log.Warnf("unknown error %T for job %s", reason, jobId)
 			event := &api.EventMessage{
diff --git a/internal/armada/server.go b/internal/armada/server.go
index 06d9e6c9..81d045c6 100644
--- a/internal/armada/server.go
+++ b/internal/armada/server.go
@@ -30,9 +30,6 @@ import (
 	"github.com/armadaproject/armada/internal/common/health"
 	"github.com/armadaproject/armada/internal/common/pgkeyvalue"
 	"github.com/armadaproject/armada/internal/common/pulsarutils"
-	"github.com/armadaproject/armada/internal/scheduler"
-	schedulerdb "github.com/armadaproject/armada/internal/scheduler/database"
-	"github.com/armadaproject/armada/internal/scheduler/internaltypes"
 	"github.com/armadaproject/armada/internal/scheduler/reports"
 	"github.com/armadaproject/armada/internal/scheduler/schedulerobjects"
 	"github.com/armadaproject/armada/pkg/api"
@@ -143,23 +140,6 @@ func Serve(ctx *armadacontext.Context, config *configuration.ArmadaConfig, healt
 	}
 	defer dbPool.Close()
 
-	// Executor Repositories for pulsar scheduler
-	pulsarExecutorRepo := schedulerdb.NewRedisExecutorRepository(db, "pulsar")
-
-	resourceListFactory, err := internaltypes.MakeResourceListFactory(config.Scheduling.SupportedResourceTypes)
-	if err != nil {
-		return errors.WithMessage(err, "Error with the .scheduling.supportedResourceTypes field in config")
-	}
-	ctx.Infof("Supported resource types: %s", resourceListFactory.SummaryString())
-	submitChecker := scheduler.NewSubmitChecker(
-		30*time.Minute,
-		config.Scheduling,
-		pulsarExecutorRepo,
-		resourceListFactory,
-	)
-	services = append(services, func() error {
-		return submitChecker.Run(ctx)
-	})
 	serverId := uuid.New()
 	var pulsarClient pulsar.Client
 	// API endpoints that generate Pulsar messages.
@@ -190,14 +170,13 @@ func Serve(ctx *armadacontext.Context, config *configuration.ArmadaConfig, healt
 		return store.PeriodicCleanup(ctx, time.Hour, 14*24*time.Hour)
 	})
 
-	pulsarSubmitServer := submit.NewServer(
+	submitServer := submit.NewServer(
 		publisher,
 		queueRepository,
 		queueCache,
 		jobRepository,
 		config.Submission,
 		submit.NewDeduplicator(store),
-		submitChecker,
 		authorizer,
 		config.RequireQueueAndJobSet)
 
@@ -237,7 +216,7 @@ func Serve(ctx *armadacontext.Context, config *configuration.ArmadaConfig, healt
 		jobRepository,
 	)
 
-	api.RegisterSubmitServer(grpcServer, pulsarSubmitServer)
+	api.RegisterSubmitServer(grpcServer, submitServer)
 	api.RegisterEventServer(grpcServer, eventServer)
 
 	schedulerobjects.RegisterSchedulerReportingServer(grpcServer, schedulingReportsServer)
diff --git a/internal/armada/submit/submit.go b/internal/armada/submit/submit.go
index f7e39771..0c360065 100644
--- a/internal/armada/submit/submit.go
+++ b/internal/armada/submit/submit.go
@@ -26,7 +26,6 @@ import (
 	"github.com/armadaproject/armada/internal/common/pulsarutils"
 	armadaslices "github.com/armadaproject/armada/internal/common/slices"
 	"github.com/armadaproject/armada/internal/common/util"
-	"github.com/armadaproject/armada/internal/scheduler"
 	"github.com/armadaproject/armada/internal/scheduler/schedulerobjects"
 	"github.com/armadaproject/armada/pkg/api"
 	"github.com/armadaproject/armada/pkg/armadaevents"
@@ -42,7 +41,6 @@ type Server struct {
 	jobRepository         repository.JobRepository
 	submissionConfig      configuration.SubmissionConfig
 	deduplicator          Deduplicator
-	submitChecker         scheduler.SubmitScheduleChecker
 	authorizer            server.ActionAuthorizer
 	requireQueueAndJobSet bool
 	// Below are used only for testing
@@ -57,7 +55,6 @@ func NewServer(
 	jobRepository repository.JobRepository,
 	submissionConfig configuration.SubmissionConfig,
 	deduplicator Deduplicator,
-	submitChecker scheduler.SubmitScheduleChecker,
 	authorizer server.ActionAuthorizer,
 	requireQueueAndJobSet bool,
 ) *Server {
@@ -68,7 +65,6 @@ func NewServer(
 		jobRepository:         jobRepository,
 		submissionConfig:      submissionConfig,
 		deduplicator:          deduplicator,
-		submitChecker:         submitChecker,
 		authorizer:            authorizer,
 		requireQueueAndJobSet: requireQueueAndJobSet,
 		clock:                 clock.RealClock{},
@@ -154,9 +150,6 @@ func (s *Server) SubmitJobs(grpcCtx context.Context, req *api.JobSubmitRequest)
 		Groups:     groups,
 		Events:     submitMsgs,
 	}
-	if canSchedule, reason := s.submitChecker.CheckApiJobs(es, s.submissionConfig.DefaultPriorityClassName); !canSchedule {
-		return nil, status.Errorf(codes.InvalidArgument, "at least one job or gang is unschedulable:\n%s", reason)
-	}
 
 	pulsarJobDetails := armadaslices.Map(
 		jobResponses,
diff --git a/internal/lookoutingesterv2/instructions/instructions.go b/internal/lookoutingesterv2/instructions/instructions.go
index ffe2f03b..e0817be8 100644
--- a/internal/lookoutingesterv2/instructions/instructions.go
+++ b/internal/lookoutingesterv2/instructions/instructions.go
@@ -120,6 +120,7 @@ func (c *InstructionConverter) convertSequence(
 		case *armadaevents.EventSequence_Event_ResourceUtilisation:
 		case *armadaevents.EventSequence_Event_StandaloneIngressInfo:
 		case *armadaevents.EventSequence_Event_PartitionMarker:
+		case *armadaevents.EventSequence_Event_JobValidated:
 			log.Debugf("Ignoring event type %T", event.GetEvent())
 		default:
 			log.Warnf("Ignoring unknown event type %T", event.GetEvent())
diff --git a/internal/scheduler/configuration/configuration.go b/internal/scheduler/configuration/configuration.go
index 682a4b70..264c766f 100644
--- a/internal/scheduler/configuration/configuration.go
+++ b/internal/scheduler/configuration/configuration.go
@@ -60,6 +60,8 @@ type Configuration struct {
 	PulsarSendTimeout time.Duration `validate:"required"`
 	// Frequency at which queues will be fetched from the API
 	QueueRefreshPeriod time.Duration `validate:"required"`
+	// If true then submit checks will be skipped
+	DisableSubmitCheck bool
 }
 
 func (c Configuration) Validate() error {
diff --git a/internal/scheduler/database/job_repository.go b/internal/scheduler/database/job_repository.go
index 630b41ef..9fadd2d8 100644
--- a/internal/scheduler/database/job_repository.go
+++ b/internal/scheduler/database/job_repository.go
@@ -160,6 +160,7 @@ func (r *PostgresJobRepository) FetchJobUpdates(ctx *armadacontext.Context, jobS
 				Queue:                   row.Queue,
 				Priority:                row.Priority,
 				Submitted:               row.Submitted,
+				Validated:               row.Validated,
 				Queued:                  row.Queued,
 				QueuedVersion:           row.QueuedVersion,
 				CancelRequested:         row.CancelRequested,
diff --git a/internal/scheduler/database/migrations/009_add_validated.sql b/internal/scheduler/database/migrations/009_add_validated.sql
new file mode 100644
index 00000000..e4813d05
--- /dev/null
+++ b/internal/scheduler/database/migrations/009_add_validated.sql
@@ -0,0 +1,1 @@
+ALTER TABLE jobs ADD COLUMN validated boolean NOT NULL DEFAULT false;
diff --git a/internal/scheduler/database/models.go b/internal/scheduler/database/models.go
index a48323bc..43875861 100644
--- a/internal/scheduler/database/models.go
+++ b/internal/scheduler/database/models.go
@@ -36,6 +36,7 @@ type Job struct {
 	SchedulingInfoVersion   int32     `db:"scheduling_info_version"`
 	Serial                  int64     `db:"serial"`
 	LastModified            time.Time `db:"last_modified"`
+	Validated               bool      `db:"validated"`
 }
 
 type JobRunError struct {
diff --git a/internal/scheduler/database/query.sql.go b/internal/scheduler/database/query.sql.go
index c2a904c0..d9675115 100644
--- a/internal/scheduler/database/query.sql.go
+++ b/internal/scheduler/database/query.sql.go
@@ -174,6 +174,15 @@ func (q *Queries) MarkJobsFailedById(ctx context.Context, jobIds []string) error
 	return err
 }
 
+const markJobsSubmitCheckedById = `-- name: MarkJobsSubmitCheckedById :exec
+UPDATE jobs SET validated = true WHERE job_id = ANY($1::text[])
+`
+
+func (q *Queries) MarkJobsSubmitCheckedById(ctx context.Context, jobIds []string) error {
+	_, err := q.db.Exec(ctx, markJobsSubmitCheckedById, jobIds)
+	return err
+}
+
 const markJobsSucceededById = `-- name: MarkJobsSucceededById :exec
 UPDATE jobs SET succeeded = true WHERE job_id = ANY($1::text[])
 `
@@ -393,7 +402,7 @@ func (q *Queries) SelectJobsForExecutor(ctx context.Context, arg SelectJobsForEx
 }
 
 const selectNewJobs = `-- name: SelectNewJobs :many
-SELECT job_id, job_set, queue, user_id, submitted, groups, priority, queued, queued_version, cancel_requested, cancelled, cancel_by_jobset_requested, succeeded, failed, submit_message, scheduling_info, scheduling_info_version, serial, last_modified FROM jobs WHERE serial > $1 ORDER BY serial LIMIT $2
+SELECT job_id, job_set, queue, user_id, submitted, groups, priority, queued, queued_version, cancel_requested, cancelled, cancel_by_jobset_requested, succeeded, failed, submit_message, scheduling_info, scheduling_info_version, serial, last_modified, validated FROM jobs WHERE serial > $1 ORDER BY serial LIMIT $2
 `
 
 type SelectNewJobsParams struct {
@@ -430,6 +439,7 @@ func (q *Queries) SelectNewJobs(ctx context.Context, arg SelectNewJobsParams) ([
 			&i.SchedulingInfoVersion,
 			&i.Serial,
 			&i.LastModified,
+			&i.Validated,
 		); err != nil {
 			return nil, err
 		}
@@ -577,7 +587,7 @@ func (q *Queries) SelectRunErrorsById(ctx context.Context, runIds []uuid.UUID) (
 }
 
 const selectUpdatedJobs = `-- name: SelectUpdatedJobs :many
-SELECT job_id, job_set, queue, priority, submitted, queued, queued_version, cancel_requested, cancel_by_jobset_requested, cancelled, succeeded, failed, scheduling_info, scheduling_info_version, serial FROM jobs WHERE serial > $1 ORDER BY serial LIMIT $2
+SELECT job_id, job_set, queue, priority, submitted, queued, queued_version, validated, cancel_requested, cancel_by_jobset_requested, cancelled, succeeded, failed, scheduling_info, scheduling_info_version, serial FROM jobs WHERE serial > $1 ORDER BY serial LIMIT $2
 `
 
 type SelectUpdatedJobsParams struct {
@@ -593,6 +603,7 @@ type SelectUpdatedJobsRow struct {
 	Submitted               int64  `db:"submitted"`
 	Queued                  bool   `db:"queued"`
 	QueuedVersion           int32  `db:"queued_version"`
+	Validated               bool   `db:"validated"`
 	CancelRequested         bool   `db:"cancel_requested"`
 	CancelByJobsetRequested bool   `db:"cancel_by_jobset_requested"`
 	Cancelled               bool   `db:"cancelled"`
@@ -620,6 +631,7 @@ func (q *Queries) SelectUpdatedJobs(ctx context.Context, arg SelectUpdatedJobsPa
 			&i.Submitted,
 			&i.Queued,
 			&i.QueuedVersion,
+			&i.Validated,
 			&i.CancelRequested,
 			&i.CancelByJobsetRequested,
 			&i.Cancelled,
diff --git a/internal/scheduler/database/query/query.sql b/internal/scheduler/database/query/query.sql
index 4b3dc424..6b582ccb 100644
--- a/internal/scheduler/database/query/query.sql
+++ b/internal/scheduler/database/query/query.sql
@@ -5,7 +5,7 @@ SELECT * FROM jobs WHERE serial > $1 ORDER BY serial LIMIT $2;
 SELECT job_id FROM jobs;
 
 -- name: SelectUpdatedJobs :many
-SELECT job_id, job_set, queue, priority, submitted, queued, queued_version, cancel_requested, cancel_by_jobset_requested, cancelled, succeeded, failed, scheduling_info, scheduling_info_version, serial FROM jobs WHERE serial > $1 ORDER BY serial LIMIT $2;
+SELECT job_id, job_set, queue, priority, submitted, queued, queued_version, validated, cancel_requested, cancel_by_jobset_requested, cancelled, succeeded, failed, scheduling_info, scheduling_info_version, serial FROM jobs WHERE serial > $1 ORDER BY serial LIMIT $2;
 
 -- name: UpdateJobPriorityByJobSet :exec
 UPDATE jobs SET priority = $1 WHERE job_set = $2 and queue = $3;
@@ -113,3 +113,6 @@ UPDATE runs SET running_timestamp = $1 WHERE run_id = $2;
 -- name: SetTerminatedTime :exec
 UPDATE runs SET terminated_timestamp = $1 WHERE run_id = $2;
 
+-- name: MarkJobsSubmitCheckedById :exec
+UPDATE jobs SET validated = true WHERE job_id = ANY(sqlc.arg(job_ids)::text[]);
+
diff --git a/internal/scheduler/jobdb/comparison.go b/internal/scheduler/jobdb/comparison.go
index 94ba5939..2c2a07dc 100644
--- a/internal/scheduler/jobdb/comparison.go
+++ b/internal/scheduler/jobdb/comparison.go
@@ -7,8 +7,21 @@ import (
 type (
 	JobPriorityComparer struct{}
 	JobQueueTtlComparer struct{}
+	JobIdHasher         struct{}
 )
 
+func (JobIdHasher) Hash(j *Job) uint32 {
+	var hash uint32
+	for _, c := range j.id {
+		hash = 31*hash + uint32(c)
+	}
+	return hash
+}
+
+func (JobIdHasher) Equal(a, b *Job) bool {
+	return a == b
+}
+
 // Compare jobs by their remaining queue time before expiry
 // Invariants:
 //   - Job.queueTtl must be > 0
diff --git a/internal/scheduler/jobdb/job.go b/internal/scheduler/jobdb/job.go
index a4f5db00..490c3ffa 100644
--- a/internal/scheduler/jobdb/job.go
+++ b/internal/scheduler/jobdb/job.go
@@ -38,6 +38,9 @@ type Job struct {
 	submittedTime int64
 	// Hash of the scheduling requirements of the job.
 	schedulingKey schedulerobjects.SchedulingKey
+	// True if the job has been validated by the scheduler.
+	// Any job that fails validation will be rejected.
+	validated bool
 	// True if the job is currently queued.
 	// If this is set then the job will not be considered for scheduling.
 	queued bool
@@ -314,6 +317,9 @@ func (job *Job) Equal(other *Job) bool {
 	if job.queuedVersion != other.queuedVersion {
 		return false
 	}
+	if job.validated != other.validated {
+		return false
+	}
 	if job.cancelRequested != other.cancelRequested {
 		return false
 	}
@@ -734,6 +740,18 @@ func (job *Job) WithCreated(created int64) *Job {
 	return j
 }
 
+// WithValidated returns a copy of the job with the validated updated.
+func (job *Job) WithValidated(validated bool) *Job {
+	j := copyJob(*job)
+	j.validated = validated
+	return j
+}
+
+// Validated returns true if the job has been validated
+func (job *Job) Validated() bool {
+	return job.validated
+}
+
 // WithJobSchedulingInfo returns a copy of the job with the job scheduling info updated.
 func (job *Job) WithJobSchedulingInfo(jobSchedulingInfo *schedulerobjects.JobSchedulingInfo) *Job {
 	j := copyJob(*job)
diff --git a/internal/scheduler/jobdb/jobdb.go b/internal/scheduler/jobdb/jobdb.go
index 073096fa..89518f99 100644
--- a/internal/scheduler/jobdb/jobdb.go
+++ b/internal/scheduler/jobdb/jobdb.go
@@ -26,6 +26,7 @@ type JobDb struct {
 	jobsByRunId     *immutable.Map[uuid.UUID, string]
 	jobsByQueue     map[string]immutable.SortedSet[*Job]
 	queuedJobsByTtl *immutable.SortedSet[*Job]
+	unvalidatedJobs *immutable.Set[*Job]
 	// Configured priority classes.
 	priorityClasses map[string]types.PriorityClass
 	// Priority class assigned to jobs with a priorityClassName not in jobDb.priorityClasses.
@@ -75,10 +76,12 @@ func NewJobDbWithSchedulingKeyGenerator(
 		// TODO(albin): Return an error instead.
 		panic(fmt.Sprintf("unknown default priority class %s", defaultPriorityClassName))
 	}
+	unvalidatedJobs := immutable.NewSet[*Job](JobIdHasher{})
 	return &JobDb{
 		jobsById:               immutable.NewMap[string, *Job](nil),
 		jobsByRunId:            immutable.NewMap[uuid.UUID, string](&UUIDHasher{}),
 		jobsByQueue:            map[string]immutable.SortedSet[*Job]{},
+		unvalidatedJobs:        &unvalidatedJobs,
 		queuedJobsByTtl:        &emptyQueuedJobsByTtl,
 		priorityClasses:        priorityClasses,
 		defaultPriorityClass:   defaultPriorityClass,
@@ -104,6 +107,7 @@ func (jobDb *JobDb) Clone() *JobDb {
 		jobsByRunId:            jobDb.jobsByRunId,
 		jobsByQueue:            maps.Clone(jobDb.jobsByQueue),
 		queuedJobsByTtl:        jobDb.queuedJobsByTtl,
+		unvalidatedJobs:        jobDb.unvalidatedJobs,
 		priorityClasses:        jobDb.priorityClasses,
 		defaultPriorityClass:   jobDb.defaultPriorityClass,
 		schedulingKeyGenerator: jobDb.schedulingKeyGenerator,
@@ -125,6 +129,7 @@ func (jobDb *JobDb) NewJob(
 	cancelByJobSetRequested bool,
 	cancelled bool,
 	created int64,
+	validated bool,
 ) *Job {
 	priorityClass, ok := jobDb.priorityClasses[schedulingInfo.PriorityClassName]
 	if !ok {
@@ -145,6 +150,7 @@ func (jobDb *JobDb) NewJob(
 		cancelRequested:         cancelRequested,
 		cancelByJobSetRequested: cancelByJobSetRequested,
 		cancelled:               cancelled,
+		validated:               validated,
 		runsById:                map[uuid.UUID]*JobRun{},
 	}
 	job.ensureJobSchedulingInfoFieldsInitialised()
@@ -179,6 +185,7 @@ func (jobDb *JobDb) ReadTxn() *Txn {
 		jobsByRunId:     jobDb.jobsByRunId,
 		jobsByQueue:     jobDb.jobsByQueue,
 		queuedJobsByTtl: jobDb.queuedJobsByTtl,
+		unvalidatedJobs: jobDb.unvalidatedJobs,
 		active:          true,
 		jobDb:           jobDb,
 	}
@@ -197,6 +204,7 @@ func (jobDb *JobDb) WriteTxn() *Txn {
 		jobsByRunId:     jobDb.jobsByRunId,
 		jobsByQueue:     maps.Clone(jobDb.jobsByQueue),
 		queuedJobsByTtl: jobDb.queuedJobsByTtl,
+		unvalidatedJobs: jobDb.unvalidatedJobs,
 		active:          true,
 		jobDb:           jobDb,
 	}
@@ -218,6 +226,8 @@ type Txn struct {
 	// Queued jobs for each queue ordered by remaining time-to-live.
 	// TODO: The ordering is wrong. Since we call time.Now() in the compare function.
 	queuedJobsByTtl *immutable.SortedSet[*Job]
+	// Jobs that require submit checking
+	unvalidatedJobs *immutable.Set[*Job]
 	// The jobDb from which this transaction was created.
 	jobDb *JobDb
 	// Set to false when this transaction is either committed or aborted.
@@ -235,6 +245,8 @@ func (txn *Txn) Commit() {
 	txn.jobDb.jobsByRunId = txn.jobsByRunId
 	txn.jobDb.jobsByQueue = txn.jobsByQueue
 	txn.jobDb.queuedJobsByTtl = txn.queuedJobsByTtl
+	txn.jobDb.unvalidatedJobs = txn.unvalidatedJobs
+
 	txn.active = false
 }
 
@@ -340,9 +352,8 @@ func (txn *Txn) Upsert(jobs []*Job) error {
 
 	hasJobs := txn.jobsById.Len() > 0
 
-	// First, delete any jobs to be upserted from the set of queued jobs.
-	// This to ensure jobs that are no longer queued do not appear in this set.
-	// Jobs that are still queued will be re-inserted later.
+	// First, delete any jobs to be upserted from the sets of queued and unvalidated jobs
+	// We will replace these jobs later if they are still queued
 	if hasJobs {
 		for _, job := range jobs {
 			existingJob, ok := txn.jobsById.Get(job.id)
@@ -351,16 +362,20 @@ func (txn *Txn) Upsert(jobs []*Job) error {
 				if ok {
 					txn.jobsByQueue[existingJob.queue] = existingQueue.Delete(existingJob)
 				}
-
 				newQueuedJobsByTtl := txn.queuedJobsByTtl.Delete(existingJob)
 				txn.queuedJobsByTtl = &newQueuedJobsByTtl
+
+				if !existingJob.Validated() {
+					newUnvalidatedJobs := txn.unvalidatedJobs.Delete(existingJob)
+					txn.unvalidatedJobs = &newUnvalidatedJobs
+				}
 			}
 		}
 	}
 
 	// Now need to insert jobs, runs and queuedJobs. This can be done in parallel.
 	wg := sync.WaitGroup{}
-	wg.Add(3)
+	wg.Add(4)
 
 	// jobs
 	go func() {
@@ -419,6 +434,18 @@ func (txn *Txn) Upsert(jobs []*Job) error {
 			}
 		}
 	}()
+
+	// Unvalidated jobs
+	go func() {
+		defer wg.Done()
+		for _, job := range jobs {
+			if !job.Validated() {
+				unvalidatedJobs := txn.unvalidatedJobs.Add(job)
+				txn.unvalidatedJobs = &unvalidatedJobs
+			}
+		}
+	}()
+
 	wg.Wait()
 	return nil
 }
@@ -461,8 +488,12 @@ func (txn *Txn) QueuedJobsByTtl() *immutable.SortedSetIterator[*Job] {
 	return txn.queuedJobsByTtl.Iterator()
 }
 
+// UnvalidatedJobs returns an iterator for jobs ordered by queue ttl time - the closest to expiry first
+func (txn *Txn) UnvalidatedJobs() *immutable.SetIterator[*Job] {
+	return txn.unvalidatedJobs.Iterator()
+}
+
 // GetAll returns all jobs in the database.
-// The Jobs returned by this function *must not* be subsequently modified
 func (txn *Txn) GetAll() []*Job {
 	allJobs := make([]*Job, 0, txn.jobsById.Len())
 	iter := txn.jobsById.Iterator()
@@ -505,6 +536,8 @@ func (txn *Txn) delete(jobId string) {
 			newQueuedJobsByExpiry := txn.queuedJobsByTtl.Delete(job)
 			txn.queuedJobsByTtl = &newQueuedJobsByExpiry
 		}
+		newUnvalidatedJobs := txn.unvalidatedJobs.Delete(job)
+		txn.unvalidatedJobs = &newUnvalidatedJobs
 	}
 }
 
diff --git a/internal/scheduler/jobdb/reconciliation.go b/internal/scheduler/jobdb/reconciliation.go
index 7052f3ce..af891ff2 100644
--- a/internal/scheduler/jobdb/reconciliation.go
+++ b/internal/scheduler/jobdb/reconciliation.go
@@ -112,6 +112,9 @@ func (jobDb *JobDb) reconcileJobDifferences(job *Job, jobRepoJob *database.Job,
 	} else if job != nil && jobRepoJob == nil {
 		// No direct updates to the job; just process any updated runs below.
 	} else if job != nil && jobRepoJob != nil {
+		if jobRepoJob.Validated && !job.Validated() {
+			job = job.WithValidated(true)
+		}
 		if jobRepoJob.CancelRequested && !job.CancelRequested() {
 			job = job.WithCancelRequested(true)
 		}
@@ -267,6 +270,7 @@ func (jobDb *JobDb) schedulerJobFromDatabaseJob(dbJob *database.Job) (*Job, erro
 		dbJob.CancelByJobsetRequested,
 		dbJob.Cancelled,
 		dbJob.Submitted,
+		dbJob.Validated,
 	)
 	if dbJob.Failed {
 		// TODO(albin): Let's make this an argument to NewJob. Even better: have the state as an enum argument.
diff --git a/internal/scheduler/pool_assigner.go b/internal/scheduler/pool_assigner.go
index 1f9a8c00..a3b2d09f 100644
--- a/internal/scheduler/pool_assigner.go
+++ b/internal/scheduler/pool_assigner.go
@@ -55,7 +55,7 @@ func NewPoolAssigner(executorTimeout time.Duration,
 	executorRepository database.ExecutorRepository,
 	resourceListFactory *internaltypes.ResourceListFactory,
 ) (*DefaultPoolAssigner, error) {
-	poolCache, err := lru.New(maxJobSchedulingResults)
+	poolCache, err := lru.New(10000)
 	if err != nil {
 		return nil, errors.Wrap(err, "error  creating PoolAssigner pool cache")
 	}
diff --git a/internal/scheduler/scheduler.go b/internal/scheduler/scheduler.go
index eeb68939..c0a9aa82 100644
--- a/internal/scheduler/scheduler.go
+++ b/internal/scheduler/scheduler.go
@@ -57,7 +57,7 @@ type Scheduler struct {
 	// The label used when setting node anti affinities.
 	nodeIdLabel string
 	// If an executor fails to report in for this amount of time,
-	// all jobs assigne to that executor are cancelled.
+	// all jobs assigned to that executor are cancelled.
 	executorTimeout time.Duration
 	// The time the previous scheduling round ended
 	previousSchedulingRoundEnd time.Time
@@ -321,6 +321,13 @@ func (s *Scheduler) cycle(ctx *armadacontext.Context, updateAll bool, leaderToke
 	}
 	ctx.Infof("Finished generating updates messages, generating %d events", len(events))
 
+	// Validate that any new jobs can be scheduled
+	validationEvents, err := s.submitCheck(ctx, txn)
+	if err != nil {
+		return overallSchedulerResult, err
+	}
+	events = append(events, validationEvents...)
+
 	// Expire any jobs running on clusters that haven't heartbeated within the configured deadline.
 	ctx.Info("Looking for jobs to expire")
 	expirationEvents, err := s.expireJobsIfNecessary(ctx, txn)
@@ -485,13 +492,21 @@ func (s *Scheduler) createSchedulingInfoWithNodeAntiAffinityForAttemptedRuns(job
 	return newSchedulingInfo, nil
 }
 
-func (s *Scheduler) addNodeAntiAffinitiesForAttemptedRunsIfSchedulable(job *jobdb.Job) (*jobdb.Job, bool, error) {
+func (s *Scheduler) addNodeAntiAffinitiesForAttemptedRunsIfSchedulable(ctx *armadacontext.Context, job *jobdb.Job) (*jobdb.Job, bool, error) {
 	schedulingInfoWithNodeAntiAffinity, err := s.createSchedulingInfoWithNodeAntiAffinityForAttemptedRuns(job)
 	if err != nil {
 		return nil, false, err
 	}
 	job = job.WithJobSchedulingInfo(schedulingInfoWithNodeAntiAffinity)
-	isSchedulable, _ := s.submitChecker.CheckJobDbJobs([]*jobdb.Job{job})
+	results, err := s.submitChecker.Check(ctx, []*jobdb.Job{job})
+	if err != nil {
+		return nil, false, err
+	}
+	result, ok := results[job.Id()]
+	isSchedulable := false
+	if ok {
+		isSchedulable = result.isSchedulable
+	}
 	return job, isSchedulable, nil
 }
 
@@ -656,11 +671,11 @@ func AppendEventSequencesFromUnschedulableJobs(eventSequences []*armadaevents.Ev
 
 // generateUpdateMessages generates EventSequences representing the state changes on updated jobs.
 // If there are no state changes then an empty slice will be returned.
-func (s *Scheduler) generateUpdateMessages(_ *armadacontext.Context, txn *jobdb.Txn, updatedJobs []*jobdb.Job, jobRunErrors map[uuid.UUID]*armadaevents.Error) ([]*armadaevents.EventSequence, error) {
+func (s *Scheduler) generateUpdateMessages(ctx *armadacontext.Context, txn *jobdb.Txn, updatedJobs []*jobdb.Job, jobRunErrors map[uuid.UUID]*armadaevents.Error) ([]*armadaevents.EventSequence, error) {
 	// Generate any eventSequences that came out of synchronising the db state.
 	var events []*armadaevents.EventSequence
 	for _, job := range updatedJobs {
-		jobEvents, err := s.generateUpdateMessagesFromJob(job, jobRunErrors, txn)
+		jobEvents, err := s.generateUpdateMessagesFromJob(ctx, job, jobRunErrors, txn)
 		if err != nil {
 			return nil, err
 		}
@@ -673,7 +688,7 @@ func (s *Scheduler) generateUpdateMessages(_ *armadacontext.Context, txn *jobdb.
 
 // generateUpdateMessages generates an EventSequence representing the state changes for a single job.
 // If there are no state changes it returns nil.
-func (s *Scheduler) generateUpdateMessagesFromJob(job *jobdb.Job, jobRunErrors map[uuid.UUID]*armadaevents.Error, txn *jobdb.Txn) (*armadaevents.EventSequence, error) {
+func (s *Scheduler) generateUpdateMessagesFromJob(ctx *armadacontext.Context, job *jobdb.Job, jobRunErrors map[uuid.UUID]*armadaevents.Error, txn *jobdb.Txn) (*armadaevents.EventSequence, error) {
 	var events []*armadaevents.EventSequence_Event
 
 	// Is the job already in a terminal state? If so then don't send any more messages
@@ -776,7 +791,7 @@ func (s *Scheduler) generateUpdateMessagesFromJob(job *jobdb.Job, jobRunErrors m
 			requeueJob := !failFast && lastRun.Returned() && job.NumAttempts() < s.maxAttemptedRuns
 
 			if requeueJob && lastRun.RunAttempted() {
-				jobWithAntiAffinity, schedulable, err := s.addNodeAntiAffinitiesForAttemptedRunsIfSchedulable(job)
+				jobWithAntiAffinity, schedulable, err := s.addNodeAntiAffinitiesForAttemptedRunsIfSchedulable(ctx, job)
 				if err != nil {
 					return nil, errors.Errorf("unable to set node anti-affinity for job %s because %s", job.Id(), err)
 				} else {
@@ -1008,6 +1023,82 @@ func (s *Scheduler) cancelQueuedJobsIfExpired(txn *jobdb.Txn) ([]*armadaevents.E
 	return events, nil
 }
 
+func (s *Scheduler) submitCheck(ctx *armadacontext.Context, txn *jobdb.Txn) ([]*armadaevents.EventSequence, error) {
+	jobsToCheck := make([]*jobdb.Job, 0)
+
+	it := txn.UnvalidatedJobs()
+
+	for job, _ := it.Next(); job != nil; job, _ = it.Next() {
+		// Don't check jobs that are terminal
+		if job.InTerminalState() {
+			continue
+		}
+		jobsToCheck = append(jobsToCheck, job)
+	}
+
+	results, err := s.submitChecker.Check(ctx, jobsToCheck)
+	if err != nil {
+		return nil, err
+	}
+
+	events := make([]*armadaevents.EventSequence, 0)
+	jobsToUpdate := make([]*jobdb.Job, 0)
+	for _, job := range jobsToCheck {
+		result := results[job.Id()]
+
+		jobId, err := armadaevents.ProtoUuidFromUlidString(job.Id())
+		if err != nil {
+			return nil, err
+		}
+
+		es := &armadaevents.EventSequence{
+			Queue:      job.Queue(),
+			JobSetName: job.Jobset(),
+			Events: []*armadaevents.EventSequence_Event{
+				{
+					Created: s.now(),
+				},
+			},
+		}
+
+		if result.isSchedulable {
+			job = job.WithValidated(true)
+			jobsToUpdate = append(jobsToUpdate, job)
+
+			es.Events[0].Event = &armadaevents.EventSequence_Event_JobValidated{
+				JobValidated: &armadaevents.JobValidated{
+					JobId: jobId,
+				},
+			}
+		} else {
+			job = job.WithFailed(true).WithQueued(false)
+			jobsToUpdate = append(jobsToUpdate, job)
+
+			es.Events[0].Event = &armadaevents.EventSequence_Event_JobErrors{
+				JobErrors: &armadaevents.JobErrors{
+					JobId: jobId,
+					Errors: []*armadaevents.Error{
+						{
+							Terminal: true,
+							Reason: &armadaevents.Error_JobRejected{
+								JobRejected: &armadaevents.JobRejected{
+									Message: result.reason,
+								},
+							},
+						},
+					},
+				},
+			}
+		}
+		events = append(events, es)
+	}
+
+	if err := txn.Upsert(jobsToUpdate); err != nil {
+		return nil, err
+	}
+	return events, nil
+}
+
 // now is a convenience function for generating a pointer to a time.Time (as required by armadaevents).
 // It exists because Go won't let you do &s.clock.Now().
 func (s *Scheduler) now() *time.Time {
@@ -1024,7 +1115,6 @@ func (s *Scheduler) initialise(ctx *armadacontext.Context) error {
 		case <-ctx.Done():
 			return nil
 		default:
-			// TODO(albin): This doesn't need to be separate; we'd anyway load everything in the first scheduling cycle.
 			if _, _, err := s.syncState(ctx); err != nil {
 				logging.WithStacktrace(ctx, err).Error("failed to initialise; trying again in 1 second")
 				time.Sleep(1 * time.Second)
diff --git a/internal/scheduler/schedulerapp.go b/internal/scheduler/schedulerapp.go
index baaf79dd..be0e839a 100644
--- a/internal/scheduler/schedulerapp.go
+++ b/internal/scheduler/schedulerapp.go
@@ -222,17 +222,23 @@ func Run(config schedulerconfig.Configuration) error {
 	// ////////////////////////////////////////////////////////////////////////
 	ctx.Infof("setting up scheduling loop")
 
-	submitChecker := NewSubmitChecker(
-		30*time.Minute,
-		config.Scheduling,
-		executorRepository,
-		resourceListFactory,
-	)
-	services = append(services, func() error {
-		return submitChecker.Run(ctx)
-	})
-	if err != nil {
-		return errors.WithMessage(err, "error creating submit checker")
+	var submitChecker SubmitScheduleChecker
+	if !config.DisableSubmitCheck {
+		submitCheckerImpl := NewSubmitChecker(
+			config.Scheduling,
+			executorRepository,
+			resourceListFactory,
+		)
+		services = append(services, func() error {
+			return submitCheckerImpl.Run(ctx)
+		})
+		if err != nil {
+			return errors.WithMessage(err, "error creating submit checker")
+		}
+		submitChecker = submitCheckerImpl
+	} else {
+		ctx.Infof("DisableSubmitCheckis true, will use a dummy submit check")
+		submitChecker = &DummySubmitChecker{}
 	}
 
 	// Setup failure estimation and quarantining.
diff --git a/internal/scheduler/simulator/simulator.go b/internal/scheduler/simulator/simulator.go
index 7212a8ac..385ddde9 100644
--- a/internal/scheduler/simulator/simulator.go
+++ b/internal/scheduler/simulator/simulator.go
@@ -688,6 +688,7 @@ func (s *Simulator) handleSubmitJob(txn *jobdb.Txn, e *armadaevents.SubmitJob, t
 		false,
 		false,
 		s.logicalJobCreatedTimestamp.Add(1),
+		false,
 	)
 	if err := txn.Upsert([]*jobdb.Job{job}); err != nil {
 		return nil, false, err
diff --git a/internal/scheduler/submitcheck.go b/internal/scheduler/submitcheck.go
index bd4b428a..fb0ab804 100644
--- a/internal/scheduler/submitcheck.go
+++ b/internal/scheduler/submitcheck.go
@@ -3,12 +3,10 @@ package scheduler
 import (
 	"fmt"
 	"strings"
-	"sync"
+	"sync/atomic"
 	"time"
 
 	lru "github.com/hashicorp/golang-lru"
-	"github.com/pkg/errors"
-	"golang.org/x/exp/maps"
 	"k8s.io/apimachinery/pkg/util/clock"
 
 	"github.com/armadaproject/armada/internal/armada/configuration"
@@ -16,86 +14,65 @@ import (
 	"github.com/armadaproject/armada/internal/common/logging"
 	armadaslices "github.com/armadaproject/armada/internal/common/slices"
 	"github.com/armadaproject/armada/internal/common/stringinterner"
-	"github.com/armadaproject/armada/internal/common/types"
-	"github.com/armadaproject/armada/internal/scheduler/adapters"
 	schedulercontext "github.com/armadaproject/armada/internal/scheduler/context"
 	"github.com/armadaproject/armada/internal/scheduler/database"
 	"github.com/armadaproject/armada/internal/scheduler/internaltypes"
 	"github.com/armadaproject/armada/internal/scheduler/jobdb"
 	"github.com/armadaproject/armada/internal/scheduler/nodedb"
 	"github.com/armadaproject/armada/internal/scheduler/schedulerobjects"
-	"github.com/armadaproject/armada/pkg/armadaevents"
 )
 
-type minimalExecutor struct {
-	nodeDb     *nodedb.NodeDb
-	updateTime time.Time
-}
-
 type schedulingResult struct {
 	isSchedulable bool
 	reason        string
 }
 
-const maxJobSchedulingResults = 10000
+type executorState struct {
+	executorsById             map[string]*nodedb.NodeDb
+	jobSchedulingResultsCache *lru.Cache
+}
 
 type SubmitScheduleChecker interface {
-	CheckApiJobs(es *armadaevents.EventSequence, defaultPriorityClass string) (bool, string)
-	CheckJobDbJobs(jobs []*jobdb.Job) (bool, string)
+	Check(ctx *armadacontext.Context, jobs []*jobdb.Job) (map[string]schedulingResult, error)
+}
+
+// DummySubmitChecker  is a  SubmitScheduleChecker that allows every job
+type DummySubmitChecker struct{}
+
+func (srv *DummySubmitChecker) Check(_ *armadacontext.Context, jobs []*jobdb.Job) (map[string]schedulingResult, error) {
+	results := make(map[string]schedulingResult, len(jobs))
+	for _, job := range jobs {
+		results[job.Id()] = schedulingResult{isSchedulable: true}
+	}
+	return results, nil
 }
 
 type SubmitChecker struct {
-	executorTimeout           time.Duration
-	priorityClasses           map[string]types.PriorityClass
-	executorById              map[string]minimalExecutor
-	priorities                []int32
-	indexedResources          []configuration.ResourceType
-	indexedTaints             []string
-	indexedNodeLabels         []string
-	wellKnownNodeTypes        []configuration.WellKnownNodeType
-	executorRepository        database.ExecutorRepository
-	clock                     clock.Clock
-	mu                        sync.Mutex
-	schedulingKeyGenerator    *schedulerobjects.SchedulingKeyGenerator
-	jobSchedulingResultsCache *lru.Cache
-	ExecutorUpdateFrequency   time.Duration
-	resourceListFactory       *internaltypes.ResourceListFactory
+	schedulingConfig       configuration.SchedulingConfig
+	executorRepository     database.ExecutorRepository
+	schedulingKeyGenerator *schedulerobjects.SchedulingKeyGenerator
+	resourceListFactory    *internaltypes.ResourceListFactory
+	state                  atomic.Pointer[executorState]
+	clock                  clock.Clock // can  be  overridden for testing
 }
 
 func NewSubmitChecker(
-	executorTimeout time.Duration,
 	schedulingConfig configuration.SchedulingConfig,
 	executorRepository database.ExecutorRepository,
 	resourceListFactory *internaltypes.ResourceListFactory,
 ) *SubmitChecker {
-	jobSchedulingResultsCache, err := lru.New(maxJobSchedulingResults)
-	if err != nil {
-		panic(errors.WithStack(err))
-	}
 	return &SubmitChecker{
-		executorTimeout:           executorTimeout,
-		priorityClasses:           schedulingConfig.PriorityClasses,
-		executorById:              map[string]minimalExecutor{},
-		priorities:                types.AllowedPriorities(schedulingConfig.PriorityClasses),
-		indexedResources:          schedulingConfig.IndexedResources,
-		indexedTaints:             schedulingConfig.IndexedTaints,
-		indexedNodeLabels:         schedulingConfig.IndexedNodeLabels,
-		wellKnownNodeTypes:        schedulingConfig.WellKnownNodeTypes,
-		executorRepository:        executorRepository,
-		clock:                     clock.RealClock{},
-		schedulingKeyGenerator:    schedulerobjects.NewSchedulingKeyGenerator(),
-		jobSchedulingResultsCache: jobSchedulingResultsCache,
-		ExecutorUpdateFrequency:   schedulingConfig.ExecutorUpdateFrequency,
-		resourceListFactory:       resourceListFactory,
+		schedulingConfig:       schedulingConfig,
+		executorRepository:     executorRepository,
+		resourceListFactory:    resourceListFactory,
+		schedulingKeyGenerator: schedulerobjects.NewSchedulingKeyGenerator(),
+		clock:                  clock.RealClock{},
 	}
 }
 
-var stringInterner = stringinterner.New(1000)
-
 func (srv *SubmitChecker) Run(ctx *armadacontext.Context) error {
 	srv.updateExecutors(ctx)
-
-	ticker := time.NewTicker(srv.ExecutorUpdateFrequency)
+	ticker := time.NewTicker(srv.schedulingConfig.ExecutorUpdateFrequency)
 	for {
 		select {
 		case <-ctx.Done():
@@ -114,76 +91,47 @@ func (srv *SubmitChecker) updateExecutors(ctx *armadacontext.Context) {
 			Error("Error fetching executors")
 		return
 	}
+	jobSchedulingResultsCache, err := lru.New(10000)
+	if err != nil {
+		// This should never happen as lru.New only returns an error if it is initialised with a negative size
+		panic(err)
+	}
+
+	executorsById := map[string]*nodedb.NodeDb{}
 	for _, executor := range executors {
-		nodeDb, err := srv.constructNodeDb(executor.Nodes)
+		nodeDb, err := srv.constructNodeDb(executor)
 		if err == nil {
-			srv.mu.Lock()
-			srv.executorById[executor.Id] = minimalExecutor{
-				nodeDb:     nodeDb,
-				updateTime: executor.LastUpdateTime,
-			}
-			srv.mu.Unlock()
-			if err != nil {
-				logging.
-					WithStacktrace(ctx, err).
-					Errorf("Error constructing node db for executor %s", executor.Id)
-			}
+			executorsById[executor.Id] = nodeDb
 		} else {
 			logging.
 				WithStacktrace(ctx, err).
-				Warnf("Error clearing nodedb for executor %s", executor.Id)
+				Warnf("Error constructing nodedb for executor: %s", executor.Id)
 		}
 	}
-
-	// Reset cache as the executors may have updated, changing what can be scheduled.
-	// Create a new schedulingKeyGenerator to get a new initial state.
-	srv.schedulingKeyGenerator = schedulerobjects.NewSchedulingKeyGenerator()
-	srv.jobSchedulingResultsCache.Purge()
-}
-
-func (srv *SubmitChecker) CheckJobDbJobs(jobs []*jobdb.Job) (bool, string) {
-	return srv.check(schedulercontext.JobSchedulingContextsFromJobs(srv.priorityClasses, jobs))
+	srv.state.Store(&executorState{
+		executorsById:             executorsById,
+		jobSchedulingResultsCache: jobSchedulingResultsCache,
+	})
 }
 
-func (srv *SubmitChecker) CheckApiJobs(es *armadaevents.EventSequence, defaultPriorityClass string) (bool, string) {
-	jobDb := jobdb.NewJobDb(srv.priorityClasses, defaultPriorityClass, stringInterner)
-	jobs := make([]*jobdb.Job, 0, len(es.Events))
-	for _, event := range es.Events {
-		submitMsg := event.GetSubmitJob()
-		if submitMsg != nil {
-			schedInfo, err := adapters.SchedulingInfoFromSubmitJob(submitMsg, time.Now(), srv.priorityClasses)
-			if err != nil {
-				return false, err.Error()
-			}
-			job := jobDb.NewJob(
-				armadaevents.MustUlidStringFromProtoUuid(submitMsg.JobId),
-				es.JobSetName,
-				es.Queue,
-				submitMsg.Priority,
-				schedInfo,
-				true,
-				0,
-				false,
-				false,
-				false,
-				0)
-			jobs = append(jobs, job)
-		}
+func (srv *SubmitChecker) Check(ctx *armadacontext.Context, jobs []*jobdb.Job) (map[string]schedulingResult, error) {
+	start := time.Now()
+	state := srv.state.Load()
+	if state == nil {
+		return nil, fmt.Errorf("executor state not loaded")
 	}
-	return srv.check(schedulercontext.JobSchedulingContextsFromJobs(srv.priorityClasses, jobs))
-}
 
-func (srv *SubmitChecker) check(jctxs []*schedulercontext.JobSchedulingContext) (bool, string) {
+	jobContexts := schedulercontext.JobSchedulingContextsFromJobs(srv.schedulingConfig.PriorityClasses, jobs)
+	results := make(map[string]schedulingResult, len(jobs))
+
 	// First, check if all jobs can be scheduled individually.
-	for i, jctx := range jctxs {
-		schedulingResult := srv.getIndividualSchedulingResult(jctx)
-		if !schedulingResult.isSchedulable {
-			return schedulingResult.isSchedulable, fmt.Sprintf("%d-th job unschedulable:\n%s", i, schedulingResult.reason)
-		}
+	for _, jctx := range jobContexts {
+		results[jctx.JobId] = srv.getIndividualSchedulingResult(jctx, state)
 	}
+
 	// Then, check if all gangs can be scheduled.
 	for gangId, jctxs := range armadaslices.GroupByFunc(
-		jctxs,
+		jobContexts,
 		func(jctx *schedulercontext.JobSchedulingContext) string {
 			return jctx.GangInfo.Id
 		},
@@ -192,22 +140,23 @@ func (srv *SubmitChecker) check(jctxs []*schedulercontext.JobSchedulingContext)
 			continue
 		}
 		gctx := schedulercontext.NewGangSchedulingContext(jctxs)
-		if schedulingResult := srv.getSchedulingResult(gctx); !schedulingResult.isSchedulable {
-			return schedulingResult.isSchedulable, fmt.Sprintf("gang %s is unschedulable:\n%s", gangId, schedulingResult.reason)
+		if result := srv.getSchedulingResult(gctx, state); !result.isSchedulable {
+			for _, jctx := range gctx.JobSchedulingContexts {
+				results[jctx.JobId] = result
+			}
 		}
 	}
-	return true, ""
+	ctx.Infof("Checked %d jobs in %s", len(jobs), time.Since(start))
+	return results, nil
 }
 
-func (srv *SubmitChecker) getIndividualSchedulingResult(jctx *schedulercontext.JobSchedulingContext) schedulingResult {
+func (srv *SubmitChecker) getIndividualSchedulingResult(jctx *schedulercontext.JobSchedulingContext, state *executorState) schedulingResult {
 	schedulingKey, ok := jctx.Job.SchedulingKey()
 	if !ok {
-		srv.mu.Lock()
 		schedulingKey = jobdb.SchedulingKeyFromJob(srv.schedulingKeyGenerator, jctx.Job)
-		srv.mu.Unlock()
 	}
 
-	if obj, ok := srv.jobSchedulingResultsCache.Get(schedulingKey); ok {
+	if obj, ok := state.jobSchedulingResultsCache.Get(schedulingKey); ok {
 		return obj.(schedulingResult)
 	}
 
@@ -219,15 +168,19 @@ func (srv *SubmitChecker) getIndividualSchedulingResult(jctx *schedulercontext.J
 	}()
 
 	gctx := schedulercontext.NewGangSchedulingContext([]*schedulercontext.JobSchedulingContext{jctx})
-	result := srv.getSchedulingResult(gctx)
+	result := srv.getSchedulingResult(gctx, state)
 
-	srv.jobSchedulingResultsCache.Add(schedulingKey, result)
+	state.jobSchedulingResultsCache.Add(schedulingKey, result)
 
 	return result
 }
 
 // Check if a set of jobs can be scheduled onto some cluster.
-func (srv *SubmitChecker) getSchedulingResult(gctx *schedulercontext.GangSchedulingContext) schedulingResult {
+// TODO: there are a number of things this won't catch:
+//   - Minimum Job Size
+//   - Node Uniformity Label (although it will work if this is per cluster)
+//   - Gang jobs that will use more thanthe allowed capacity limit
+func (srv *SubmitChecker) getSchedulingResult(gctx *schedulercontext.GangSchedulingContext, state *executorState) schedulingResult {
 	// Skip submit checks if this batch contains less than the min cardinality jobs.
 	// Reason:
 	//  - We need to support submitting gang jobs across batches and allow for gang jobs to queue until min cardinality is satisfied.
@@ -236,29 +189,12 @@ func (srv *SubmitChecker) getSchedulingResult(gctx *schedulercontext.GangSchedul
 	if len(gctx.JobSchedulingContexts) < gctx.GangInfo.MinimumCardinality {
 		return schedulingResult{isSchedulable: true, reason: ""}
 	}
-
-	// Make a shallow copy to avoid holding the lock and
-	// preventing updating NodeDbs while checking if jobs can be scheduled
-	srv.mu.Lock()
-	executorById := maps.Clone(srv.executorById)
-	srv.mu.Unlock()
-	executorById = srv.filterStaleExecutors(executorById)
-	if len(executorById) == 0 {
-		return schedulingResult{isSchedulable: false, reason: "no executor clusters available"}
-	}
-
-	isSchedulable := false
 	var sb strings.Builder
-	for id, executor := range executorById {
-		nodeDb := executor.nodeDb
+	for id, nodeDb := range state.executorsById {
 		txn := nodeDb.Txn(true)
-		// TODO: This doesn't account for per-queue limits or the NodeUniformityLabel.
-		// We should create a GangScheduler for this instead.
 		ok, err := nodeDb.ScheduleManyWithTxn(txn, gctx)
 		txn.Abort()
 
-		isSchedulable = isSchedulable || ok
-
 		sb.WriteString(id)
 		if err != nil {
 			sb.WriteString(err.Error())
@@ -266,6 +202,10 @@ func (srv *SubmitChecker) getSchedulingResult(gctx *schedulercontext.GangSchedul
 			continue
 		}
 
+		if ok {
+			return schedulingResult{isSchedulable: true}
+		}
+
 		numSuccessfullyScheduled := 0
 		for _, jctx := range gctx.JobSchedulingContexts {
 			if jctx.PodSchedulingContext.IsSuccessful() {
@@ -292,28 +232,18 @@ func (srv *SubmitChecker) getSchedulingResult(gctx *schedulercontext.GangSchedul
 			)
 		}
 	}
-	return schedulingResult{isSchedulable: isSchedulable, reason: sb.String()}
-}
-
-func (srv *SubmitChecker) filterStaleExecutors(executorsById map[string]minimalExecutor) map[string]minimalExecutor {
-	rv := make(map[string]minimalExecutor)
-	for id, executor := range executorsById {
-		if srv.clock.Since(executor.updateTime) < srv.executorTimeout {
-			rv[id] = executor
-		}
-	}
-	return rv
+	return schedulingResult{isSchedulable: false, reason: sb.String()}
 }
 
-func (srv *SubmitChecker) constructNodeDb(nodes []*schedulerobjects.Node) (*nodedb.NodeDb, error) {
+func (srv *SubmitChecker) constructNodeDb(executor *schedulerobjects.Executor) (*nodedb.NodeDb, error) {
 	nodeDb, err := nodedb.NewNodeDb(
-		srv.priorityClasses,
+		srv.schedulingConfig.PriorityClasses,
 		0,
-		srv.indexedResources,
-		srv.indexedTaints,
-		srv.indexedNodeLabels,
-		srv.wellKnownNodeTypes,
-		stringinterner.New(512),
+		srv.schedulingConfig.IndexedResources,
+		srv.schedulingConfig.IndexedTaints,
+		srv.schedulingConfig.IndexedNodeLabels,
+		srv.schedulingConfig.WellKnownNodeTypes,
+		stringinterner.New(10000),
 		srv.resourceListFactory,
 	)
 	if err != nil {
@@ -321,15 +251,12 @@ func (srv *SubmitChecker) constructNodeDb(nodes []*schedulerobjects.Node) (*node
 	}
 	txn := nodeDb.Txn(true)
 	defer txn.Abort()
-	for _, node := range nodes {
+	for _, node := range executor.Nodes {
 		if err := nodeDb.CreateAndInsertWithJobDbJobsWithTxn(txn, nil, node); err != nil {
 			return nil, err
 		}
 	}
 	txn.Commit()
-	if err != nil {
-		return nil, err
-	}
 	err = nodeDb.ClearAllocated()
 	if err != nil {
 		return nil, err
diff --git a/internal/scheduleringester/dbops.go b/internal/scheduleringester/dbops.go
index 47105c06..e5330c0f 100644
--- a/internal/scheduleringester/dbops.go
+++ b/internal/scheduleringester/dbops.go
@@ -134,6 +134,7 @@ type (
 	MarkRunsPending                map[uuid.UUID]time.Time
 	MarkRunsPreempted              map[uuid.UUID]time.Time
 	InsertJobRunErrors             map[uuid.UUID]*schedulerdb.JobRunError
+	MarkJobsValidated              map[string]bool
 	InsertPartitionMarker          struct {
 		markers []*schedulerdb.Marker
 	}
@@ -253,6 +254,10 @@ func (a InsertJobRunErrors) Merge(b DbOperation) bool {
 	return mergeInMap(a, b)
 }
 
+func (a MarkJobsValidated) Merge(b DbOperation) bool {
+	return mergeInMap(a, b)
+}
+
 func (a *InsertPartitionMarker) Merge(b DbOperation) bool {
 	switch op := b.(type) {
 	case *InsertPartitionMarker:
@@ -404,6 +409,10 @@ func (a InsertJobRunErrors) CanBeAppliedBefore(_ DbOperation) bool {
 	return true
 }
 
+func (a MarkJobsValidated) CanBeAppliedBefore(b DbOperation) bool {
+	return !definesJob(a, b)
+}
+
 // definesJobInSet returns true if b is an InsertJobs operation
 // that inserts at least one job in any of the job sets that make
 // up the keys of a.
diff --git a/internal/scheduleringester/instructions.go b/internal/scheduleringester/instructions.go
index e22dcac5..b732235f 100644
--- a/internal/scheduleringester/instructions.go
+++ b/internal/scheduleringester/instructions.go
@@ -113,6 +113,8 @@ func (c *InstructionConverter) dbOperationsFromEventSequence(es *armadaevents.Ev
 			operationsFromEvent, err = c.handleJobRunPreempted(event.GetJobRunPreempted(), eventTime)
 		case *armadaevents.EventSequence_Event_JobRunAssigned:
 			operationsFromEvent, err = c.handleJobRunAssigned(event.GetJobRunAssigned(), eventTime)
+		case *armadaevents.EventSequence_Event_JobValidated:
+			operationsFromEvent, err = c.handleJobValidated(event.GetJobValidated())
 		case *armadaevents.EventSequence_Event_ReprioritisedJob,
 			*armadaevents.EventSequence_Event_JobDuplicateDetected,
 			*armadaevents.EventSequence_Event_ResourceUtilisation,
@@ -400,6 +402,16 @@ func (c *InstructionConverter) handlePartitionMarker(pm *armadaevents.PartitionM
 	}}, nil
 }
 
+func (c *InstructionConverter) handleJobValidated(checked *armadaevents.JobValidated) ([]DbOperation, error) {
+	jobId, err := armadaevents.UlidStringFromProtoUuid(checked.GetJobId())
+	if err != nil {
+		return nil, err
+	}
+	return []DbOperation{
+		MarkJobsValidated{jobId: true},
+	}, nil
+}
+
 // schedulingInfoFromSubmitJob returns a minimal representation of a job containing only the info needed by the scheduler.
 func (c *InstructionConverter) schedulingInfoFromSubmitJob(submitJob *armadaevents.SubmitJob, submitTime time.Time) (*schedulerobjects.JobSchedulingInfo, error) {
 	return SchedulingInfoFromSubmitJob(submitJob, submitTime, c.priorityClasses)
diff --git a/internal/scheduleringester/schedulerdb.go b/internal/scheduleringester/schedulerdb.go
index 5deb71c8..3573304b 100644
--- a/internal/scheduleringester/schedulerdb.go
+++ b/internal/scheduleringester/schedulerdb.go
@@ -334,6 +334,12 @@ func (s *SchedulerDb) WriteDbOp(ctx *armadacontext.Context, tx pgx.Tx, op DbOper
 			}
 		}
 		return nil
+	case MarkJobsValidated:
+		jobIds := maps.Keys(o)
+		err := queries.MarkJobsSubmitCheckedById(ctx, jobIds)
+		if err != nil {
+			return errors.WithStack(err)
+		}
 	default:
 		return errors.Errorf("received unexpected op %+v", op)
 	}
diff --git a/pkg/api/api.swagger.go b/pkg/api/api.swagger.go
index 7822f967..312b1d9e 100644
--- a/pkg/api/api.swagger.go
+++ b/pkg/api/api.swagger.go
@@ -624,7 +624,8 @@ func SwaggerJsonTemplate() string {
 		"        \"Error\",\n" +
 		"        \"Evicted\",\n" +
 		"        \"OOM\",\n" +
-		"        \"DeadlineExceeded\"\n" +
+		"        \"DeadlineExceeded\",\n" +
+		"        \"Rejected\"\n" +
 		"      ]\n" +
 		"    },\n" +
 		"    \"apiContainerStatus\": {\n" +
diff --git a/pkg/api/api.swagger.json b/pkg/api/api.swagger.json
index 7900e81c..ecd4fc4c 100644
--- a/pkg/api/api.swagger.json
+++ b/pkg/api/api.swagger.json
@@ -613,7 +613,8 @@
         "Error",
         "Evicted",
         "OOM",
-        "DeadlineExceeded"
+        "DeadlineExceeded",
+        "Rejected"
       ]
     },
     "apiContainerStatus": {
diff --git a/pkg/api/event.pb.go b/pkg/api/event.pb.go
index a0e531e6..be7ef004 100644
--- a/pkg/api/event.pb.go
+++ b/pkg/api/event.pb.go
@@ -45,6 +45,7 @@ const (
 	Cause_Evicted          Cause = 1
 	Cause_OOM              Cause = 2
 	Cause_DeadlineExceeded Cause = 3
+	Cause_Rejected         Cause = 4
 )
 
 var Cause_name = map[int32]string{
@@ -52,6 +53,7 @@ var Cause_name = map[int32]string{
 	1: "Evicted",
 	2: "OOM",
 	3: "DeadlineExceeded",
+	4: "Rejected",
 }
 
 var Cause_value = map[string]int32{
@@ -59,6 +61,7 @@ var Cause_value = map[string]int32{
 	"Evicted":          1,
 	"OOM":              2,
 	"DeadlineExceeded": 3,
+	"Rejected":         4,
 }
 
 func (x Cause) String() string {
@@ -2821,167 +2824,167 @@ func init() {
 func init() { proto.RegisterFile("pkg/api/event.proto", fileDescriptor_7758595c3bb8cf56) }
 
 var fileDescriptor_7758595c3bb8cf56 = []byte{
-	// 2546 bytes of a gzipped FileDescriptorProto
+	// 2553 bytes of a gzipped FileDescriptorProto
 	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xec, 0x5b, 0x4d, 0x6c, 0x1b, 0xc7,
-	0xf5, 0xd7, 0x52, 0xe2, 0xd7, 0x50, 0x9f, 0xa3, 0x0f, 0xaf, 0x69, 0x5b, 0x14, 0x18, 0xe0, 0x1f,
-	0xc5, 0x88, 0xc9, 0xfc, 0xe5, 0xa4, 0x08, 0x8c, 0xa0, 0x85, 0x29, 0xcb, 0x89, 0x84, 0x38, 0x76,
-	0x28, 0x1b, 0x69, 0x8b, 0x00, 0xcc, 0x72, 0x77, 0x44, 0xad, 0x44, 0xee, 0x6c, 0xf6, 0xc3, 0xb6,
-	0x62, 0x04, 0x08, 0x5a, 0xb4, 0xc8, 0xa5, 0x68, 0x8a, 0xf6, 0x9e, 0x00, 0xbd, 0xf5, 0xd4, 0x4b,
-	0xaf, 0x3d, 0x14, 0x3d, 0xa4, 0x37, 0x17, 0xbd, 0xe4, 0xc4, 0xb6, 0x76, 0x52, 0x14, 0x3c, 0xf4,
-	0xde, 0x5b, 0x31, 0x6f, 0x66, 0xb9, 0x33, 0x2b, 0x1a, 0x92, 0x99, 0x26, 0x35, 0x08, 0x5e, 0x12,
-	0xf3, 0xf7, 0x66, 0xde, 0xbc, 0x79, 0xf3, 0x7b, 0x33, 0x6f, 0xf6, 0x8d, 0xd0, 0xa2, 0x7b, 0xd8,
-	0xaa, 0x1a, 0xae, 0x5d, 0x25, 0x77, 0x89, 0x13, 0x54, 0x5c, 0x8f, 0x06, 0x14, 0x4f, 0x1a, 0xae,
-	0x5d, 0x2c, 0xb5, 0x28, 0x6d, 0xb5, 0x49, 0x15, 0xa0, 0x66, 0xb8, 0x57, 0x0d, 0xec, 0x0e, 0xf1,
-	0x03, 0xa3, 0xe3, 0xf2, 0x56, 0xc5, 0xa5, 0xa8, 0xab, 0x1f, 0x36, 0x3b, 0x76, 0x90, 0x44, 0xf7,
-	0x89, 0xd1, 0x0e, 0xf6, 0x05, 0x7a, 0x2e, 0xa9, 0x8c, 0x74, 0xdc, 0xe0, 0x48, 0x08, 0x2f, 0xb5,
-	0xec, 0x60, 0x3f, 0x6c, 0x56, 0x4c, 0xda, 0xa9, 0xb6, 0x68, 0x8b, 0xc6, 0xad, 0xd8, 0x2f, 0xf8,
-	0x01, 0xff, 0x12, 0xcd, 0xcf, 0x0b, 0x5d, 0x6c, 0x10, 0xc3, 0x71, 0x68, 0x60, 0x04, 0x36, 0x75,
-	0x7c, 0x21, 0x7d, 0xf9, 0xf0, 0x55, 0xbf, 0x62, 0x53, 0x26, 0xed, 0x18, 0xe6, 0xbe, 0xed, 0x10,
-	0xef, 0xa8, 0x1a, 0xd9, 0xe4, 0x11, 0x9f, 0x86, 0x9e, 0x49, 0xaa, 0x2d, 0xe2, 0x10, 0xcf, 0x08,
-	0x88, 0xc5, 0x7b, 0x95, 0x7f, 0x95, 0x42, 0x0b, 0x3b, 0xb4, 0xb9, 0x0b, 0x33, 0x09, 0x88, 0xb5,
-	0xc5, 0xbc, 0x81, 0x2f, 0xa2, 0xcc, 0x01, 0x6d, 0x36, 0x6c, 0x4b, 0xd7, 0xd6, 0xb4, 0xf5, 0x7c,
-	0x6d, 0xb1, 0xd7, 0x2d, 0xcd, 0x1d, 0xd0, 0xe6, 0xb6, 0xf5, 0x22, 0xed, 0xd8, 0x01, 0xcc, 0xa1,
-	0x9e, 0x06, 0x00, 0xbf, 0x8c, 0x10, 0x6b, 0xeb, 0x93, 0x80, 0xb5, 0x4f, 0x41, 0xfb, 0x95, 0x5e,
-	0xb7, 0x84, 0x0f, 0x68, 0x73, 0x97, 0x04, 0x4a, 0x97, 0x5c, 0x84, 0xe1, 0x17, 0x50, 0xfa, 0xfd,
-	0x90, 0x84, 0x44, 0x9f, 0x8c, 0x07, 0x00, 0x40, 0x1e, 0x00, 0x00, 0xbc, 0x8d, 0xb2, 0xa6, 0x47,
-	0x98, 0xcd, 0xfa, 0xd4, 0x9a, 0xb6, 0x5e, 0xd8, 0x28, 0x56, 0xb8, 0x23, 0x2a, 0x91, 0xbb, 0x2a,
-	0xb7, 0xa3, 0x15, 0xaa, 0x2d, 0x7e, 0xde, 0x2d, 0x4d, 0xf4, 0xba, 0xa5, 0xa8, 0xcb, 0x27, 0x7f,
-	0x2d, 0x69, 0xf5, 0xe8, 0x07, 0x7e, 0x1e, 0x4d, 0x1e, 0xd0, 0xa6, 0x9e, 0x06, 0x35, 0xb9, 0x8a,
-	0xe1, 0xda, 0x95, 0x1d, 0xda, 0xac, 0x15, 0x44, 0x27, 0x26, 0xac, 0xb3, 0xff, 0x94, 0xff, 0xa9,
-	0xa1, 0xd9, 0x1d, 0xda, 0x7c, 0x9b, 0x19, 0x30, 0xda, 0x3e, 0x29, 0xff, 0x2e, 0x85, 0x56, 0x76,
-	0x68, 0xf3, 0x5a, 0xe8, 0xb6, 0x6d, 0xd3, 0x08, 0xc8, 0x75, 0x1a, 0x3a, 0x23, 0x4e, 0x83, 0x4d,
-	0x34, 0x47, 0x3d, 0xbb, 0x65, 0x3b, 0x46, 0xbb, 0x21, 0x26, 0x98, 0x86, 0xf1, 0xcf, 0xf5, 0xba,
-	0xa5, 0x33, 0x91, 0x68, 0x27, 0x31, 0xd1, 0x19, 0x45, 0x50, 0xfe, 0x2c, 0x05, 0x14, 0x79, 0x93,
-	0x18, 0xfe, 0xa8, 0x87, 0xcd, 0x77, 0x10, 0x32, 0xdb, 0xa1, 0x1f, 0x10, 0x2f, 0x76, 0xd5, 0x99,
-	0x5e, 0xb7, 0xb4, 0x28, 0x50, 0xc5, 0xd8, 0x7c, 0x1f, 0x2c, 0xff, 0x7c, 0x0a, 0x2d, 0x47, 0x2e,
-	0xaa, 0x93, 0x20, 0xf4, 0x9c, 0xb1, 0xa7, 0x06, 0x7a, 0x0a, 0xbf, 0x88, 0x32, 0x1e, 0x31, 0x7c,
-	0xea, 0xe8, 0x19, 0xe8, 0xb3, 0xd4, 0xeb, 0x96, 0xe6, 0x39, 0x22, 0x75, 0x10, 0x6d, 0xf0, 0xf7,
-	0xd0, 0xcc, 0x61, 0xd8, 0x24, 0x9e, 0x43, 0x02, 0xe2, 0xb3, 0x81, 0xb2, 0xd0, 0xa9, 0xd8, 0xeb,
-	0x96, 0x56, 0x62, 0x81, 0x32, 0xd6, 0xb4, 0x8c, 0x33, 0x33, 0x5d, 0x6a, 0x35, 0x9c, 0xb0, 0xd3,
-	0x24, 0x9e, 0x9e, 0x5b, 0xd3, 0xd6, 0xd3, 0xdc, 0x4c, 0x97, 0x5a, 0x6f, 0x01, 0x28, 0x9b, 0xd9,
-	0x07, 0xd9, 0xc0, 0x5e, 0xe8, 0x34, 0x8c, 0x00, 0x44, 0xc4, 0xd2, 0xf3, 0x6b, 0xda, 0x7a, 0x8e,
-	0x0f, 0xec, 0x85, 0xce, 0xd5, 0x08, 0x97, 0x07, 0x96, 0xf1, 0xf2, 0xbf, 0x34, 0xb4, 0x14, 0x31,
-	0x62, 0xeb, 0xbe, 0x6b, 0x7b, 0xa3, 0xbe, 0xbb, 0xfe, 0x6c, 0x0a, 0xcd, 0xed, 0xd0, 0xe6, 0x2d,
-	0xe2, 0x58, 0xb6, 0xd3, 0x1a, 0x93, 0x7f, 0x10, 0xf9, 0x8f, 0xd1, 0x39, 0xf3, 0xb5, 0xe8, 0x9c,
-	0x3d, 0x35, 0x9d, 0x5f, 0x42, 0x39, 0xe8, 0x67, 0x74, 0x08, 0x04, 0x41, 0xbe, 0xb6, 0xdc, 0xeb,
-	0x96, 0x16, 0x58, 0x03, 0xa3, 0x23, 0xfb, 0x2a, 0x2b, 0x20, 0x66, 0x6a, 0xd4, 0xc3, 0x77, 0x0d,
-	0x93, 0x40, 0x00, 0x08, 0x53, 0x45, 0x1b, 0xc0, 0x65, 0x53, 0x65, 0xbc, 0xfc, 0x07, 0xce, 0x87,
-	0x7a, 0xe8, 0x38, 0x63, 0x3e, 0x7c, 0x53, 0x7c, 0xb8, 0x8c, 0xf2, 0x0e, 0xb5, 0x08, 0x5f, 0xd8,
-	0x6c, 0xec, 0x23, 0x06, 0x26, 0x56, 0x36, 0x17, 0x61, 0x43, 0xef, 0x89, 0x32, 0x89, 0xf2, 0xc3,
-	0x91, 0x08, 0x3d, 0x25, 0x89, 0x7e, 0x9b, 0x41, 0x8b, 0x2c, 0x09, 0x71, 0x5a, 0x1e, 0xf1, 0xfd,
-	0x6d, 0x67, 0x8f, 0x8e, 0x89, 0x34, 0x5a, 0x44, 0x42, 0xc3, 0x11, 0xa9, 0xf0, 0x74, 0x44, 0xc2,
-	0x0f, 0xd0, 0x82, 0xcd, 0x49, 0xd4, 0x30, 0x2c, 0x8b, 0xfd, 0x9f, 0xf8, 0x7a, 0x7e, 0x6d, 0x72,
-	0xbd, 0xb0, 0x51, 0x89, 0x6e, 0x47, 0x49, 0x96, 0x55, 0x04, 0x70, 0x35, 0xea, 0xb0, 0xe5, 0x04,
-	0xde, 0x51, 0x6d, 0xb5, 0xd7, 0x2d, 0x15, 0xed, 0x84, 0x48, 0x1a, 0x78, 0x3e, 0x29, 0x2b, 0x1e,
-	0xa2, 0xe5, 0x81, 0xaa, 0xf0, 0x73, 0x68, 0xf2, 0x90, 0x1c, 0x01, 0x87, 0xd3, 0xb5, 0x85, 0x5e,
-	0xb7, 0x34, 0x73, 0x48, 0x8e, 0x24, 0x55, 0x4c, 0xca, 0x98, 0x78, 0xd7, 0x68, 0x87, 0x44, 0x50,
-	0x17, 0x98, 0x08, 0x80, 0xcc, 0x44, 0x00, 0xae, 0xa4, 0x5e, 0xd5, 0xca, 0xff, 0x9e, 0x42, 0xfa,
-	0x0e, 0x6d, 0xde, 0x71, 0x8c, 0x66, 0x9b, 0xdc, 0xa6, 0xbb, 0xe6, 0x3e, 0xb1, 0xc2, 0x36, 0x19,
-	0xc7, 0xcd, 0x33, 0x90, 0x8d, 0x2a, 0x51, 0x96, 0x1b, 0x2a, 0xca, 0xf2, 0xcf, 0x70, 0x94, 0x95,
-	0x1f, 0x66, 0xe1, 0xa6, 0x78, 0xdd, 0xb0, 0xdb, 0xe3, 0xfb, 0xcf, 0x7f, 0x83, 0x71, 0xef, 0x22,
-	0x44, 0xee, 0xdb, 0x41, 0xc3, 0xa4, 0x16, 0xf1, 0xf5, 0x2c, 0xec, 0x57, 0xe5, 0x68, 0xbf, 0x92,
-	0xdc, 0x5c, 0xd9, 0xba, 0x6f, 0x07, 0x9b, 0xac, 0x11, 0xdf, 0xa3, 0xce, 0x32, 0x4b, 0x48, 0x84,
-	0xc5, 0x8a, 0x75, 0xad, 0x9e, 0xef, 0xc3, 0xc7, 0xf9, 0x9c, 0xfb, 0x3a, 0x7c, 0xce, 0x0f, 0xc5,
-	0x67, 0x34, 0x14, 0x9f, 0x67, 0x86, 0xe3, 0xf3, 0xec, 0x53, 0x9e, 0x1a, 0x16, 0xc2, 0x26, 0x75,
-	0x02, 0xc3, 0x76, 0x88, 0xd7, 0xf0, 0x03, 0x23, 0x08, 0xd9, 0xb1, 0x51, 0x80, 0x65, 0x58, 0x82,
-	0x65, 0xd8, 0x8c, 0xc4, 0xbb, 0x20, 0xad, 0x95, 0x7a, 0xdd, 0xd2, 0x39, 0x53, 0x05, 0x95, 0xd3,
-	0x61, 0xe1, 0x98, 0x10, 0xbf, 0x82, 0xd2, 0xa6, 0x11, 0xfa, 0x44, 0x9f, 0x5e, 0xd3, 0xd6, 0x67,
-	0x37, 0x10, 0x57, 0xcc, 0x10, 0x4e, 0x66, 0x10, 0xca, 0x64, 0x06, 0xa0, 0x68, 0xa1, 0x59, 0x75,
-	0xd5, 0xe5, 0xe3, 0x24, 0x7f, 0xba, 0xe3, 0x24, 0x7d, 0xe2, 0x71, 0xf2, 0xeb, 0x14, 0xc2, 0xec,
-	0x5a, 0xe7, 0x11, 0x26, 0x1a, 0xf9, 0x4c, 0xfe, 0x15, 0x94, 0xf7, 0xc8, 0xfb, 0x21, 0xf1, 0x03,
-	0xea, 0xc9, 0x51, 0xdd, 0x07, 0x65, 0x6e, 0xf6, 0xc1, 0xf2, 0x57, 0x93, 0xf0, 0x71, 0x59, 0x78,
-	0x69, 0xbc, 0xf7, 0x0d, 0xde, 0xfb, 0x2e, 0xa2, 0x8c, 0x17, 0x3a, 0x71, 0x7a, 0x0a, 0xe6, 0x7a,
-	0xa1, 0xa3, 0xfa, 0x03, 0x00, 0xbc, 0x8d, 0x16, 0x5c, 0xc1, 0xb9, 0xbb, 0x24, 0xfa, 0x76, 0xc9,
-	0xcf, 0xdb, 0x0b, 0xbd, 0x6e, 0xe9, 0x6c, 0x2c, 0x4c, 0x7e, 0xbd, 0x9c, 0x4b, 0x88, 0x12, 0xaa,
-	0x84, 0x05, 0xb9, 0x41, 0xaa, 0xea, 0x09, 0x5b, 0xe6, 0x12, 0xa2, 0xf2, 0x16, 0xe4, 0x56, 0xd2,
-	0xc6, 0xbb, 0x49, 0x3b, 0x2e, 0x64, 0x74, 0xb0, 0x16, 0x50, 0x61, 0x81, 0xc5, 0x9e, 0xe6, 0x93,
-	0x03, 0x40, 0x9e, 0x1c, 0x00, 0xe5, 0x3f, 0x4e, 0x89, 0x5a, 0x84, 0x69, 0x12, 0x62, 0x8d, 0xe9,
-	0x32, 0xbe, 0x1d, 0x0f, 0x75, 0x3b, 0xfe, 0x34, 0x0f, 0xb7, 0xe3, 0x3b, 0x81, 0xdd, 0xb6, 0x7d,
-	0x28, 0x91, 0x8d, 0x89, 0xf4, 0x8d, 0x10, 0xe9, 0x63, 0x0d, 0x2d, 0xdf, 0x30, 0xee, 0xd7, 0x45,
-	0x6d, 0xd1, 0xbf, 0x4e, 0xbd, 0x5b, 0xc4, 0xb3, 0xa9, 0x25, 0x52, 0xb2, 0xcb, 0x51, 0x4a, 0x96,
-	0x5c, 0x8a, 0xca, 0xc0, 0x5e, 0x3c, 0x47, 0xbb, 0x20, 0xe6, 0x3a, 0x58, 0x73, 0x7d, 0x30, 0x3c,
-	0xea, 0x57, 0x08, 0xfc, 0x53, 0x0d, 0xad, 0x04, 0x34, 0x30, 0xda, 0x0d, 0x33, 0xec, 0x84, 0x6d,
-	0x03, 0xf6, 0xec, 0xd0, 0x37, 0x5a, 0x2c, 0x3d, 0x62, 0xbe, 0xde, 0x78, 0xa2, 0xaf, 0x6f, 0xb3,
-	0x6e, 0x9b, 0xfd, 0x5e, 0x77, 0x58, 0x27, 0xee, 0xea, 0xf3, 0xc2, 0xd5, 0x4b, 0xc1, 0x80, 0x26,
-	0xf5, 0x81, 0x68, 0xf1, 0x33, 0x0d, 0x15, 0x9f, 0xbc, 0x7a, 0xa7, 0xcb, 0xb5, 0x7e, 0x20, 0xe7,
-	0x5a, 0x85, 0x8d, 0x4a, 0x85, 0x57, 0xae, 0x2b, 0x72, 0xe5, 0xba, 0xe2, 0x1e, 0xb6, 0x60, 0x4a,
-	0x51, 0xe5, 0xba, 0xf2, 0x76, 0x68, 0x38, 0x81, 0x1d, 0x1c, 0x9d, 0x94, 0x9b, 0x15, 0x3f, 0xd5,
-	0xd0, 0xd9, 0x27, 0x4e, 0xfa, 0x59, 0xb0, 0xb0, 0xfc, 0x15, 0x2f, 0xb9, 0xd6, 0x89, 0xeb, 0xd9,
-	0xd4, 0xb3, 0x03, 0xfb, 0x83, 0x91, 0xcf, 0x20, 0x5f, 0x43, 0xd3, 0x0e, 0xb9, 0xd7, 0x10, 0x13,
-	0x3e, 0x82, 0x6d, 0x4a, 0x83, 0x0b, 0xd9, 0xb2, 0x43, 0xee, 0xdd, 0x12, 0xb0, 0x64, 0x42, 0x41,
-	0x82, 0xd5, 0xfc, 0x33, 0x73, 0xea, 0xfc, 0xf3, 0xcb, 0x14, 0xd4, 0x1f, 0x25, 0x3f, 0x8f, 0x7a,
-	0x52, 0xf1, 0x3f, 0x71, 0xf3, 0x9f, 0xf9, 0x65, 0x68, 0xd3, 0x70, 0x4c, 0xd2, 0x6e, 0x8f, 0x2f,
-	0x43, 0x03, 0xbd, 0xf4, 0x74, 0x9f, 0x38, 0xca, 0x0f, 0xf9, 0xbb, 0x1c, 0xe1, 0xd3, 0x51, 0xa7,
-	0xed, 0xb7, 0xe2, 0xd2, 0xdf, 0x4f, 0x01, 0x4d, 0x6f, 0x13, 0xaf, 0x63, 0x3b, 0xc6, 0xf8, 0x3a,
-	0xfa, 0x2c, 0x57, 0x63, 0xbf, 0x9d, 0xab, 0x82, 0x44, 0xa0, 0xdc, 0x29, 0x08, 0xf4, 0xa7, 0x14,
-	0xd4, 0x6e, 0xef, 0xb8, 0xd6, 0xe8, 0xb3, 0x67, 0xc8, 0x88, 0x14, 0x0f, 0xec, 0x32, 0x27, 0x3e,
-	0xb0, 0xfb, 0x68, 0x0e, 0x4d, 0x83, 0x07, 0x6f, 0x10, 0x9f, 0x25, 0x67, 0xf8, 0x26, 0xca, 0xfb,
-	0xd1, 0x23, 0x44, 0xf0, 0x65, 0x61, 0x63, 0x25, 0xea, 0xaf, 0xbe, 0x4e, 0xe4, 0x86, 0xf4, 0x1b,
-	0xc7, 0x86, 0xbc, 0x31, 0x51, 0x8f, 0x75, 0xe0, 0x4d, 0x94, 0x01, 0xaf, 0x58, 0x22, 0x89, 0x5b,
-	0x8c, 0xb4, 0x49, 0x8f, 0xfa, 0xf8, 0x82, 0xf3, 0x66, 0x8a, 0x1e, 0xd1, 0x15, 0x5b, 0x68, 0xce,
-	0x8a, 0x1e, 0xc6, 0x35, 0xf6, 0x68, 0xe8, 0x58, 0xfa, 0x3c, 0x68, 0x3b, 0x17, 0x69, 0x1b, 0xf0,
-	0x6e, 0xae, 0x76, 0xbe, 0xd7, 0x2d, 0xe9, 0x96, 0x22, 0x50, 0xb4, 0xcf, 0xaa, 0x32, 0x66, 0x6a,
-	0x1b, 0x9e, 0x91, 0xc1, 0x1a, 0x4b, 0xa6, 0x4a, 0x8f, 0xcb, 0xb8, 0xa9, 0xbc, 0x99, 0x6a, 0x2a,
-	0xc7, 0xf0, 0x7b, 0x68, 0x16, 0xfe, 0xd5, 0xf0, 0xc4, 0x4b, 0xab, 0x3e, 0x07, 0x64, 0x65, 0xca,
-	0x33, 0x2c, 0xfe, 0xde, 0xad, 0x2d, 0xe3, 0x8a, 0xea, 0x19, 0x45, 0x84, 0xdf, 0x45, 0x1c, 0x68,
-	0x10, 0xfe, 0x72, 0x47, 0xbc, 0xa3, 0x3c, 0xab, 0x0c, 0x20, 0xbf, 0xea, 0xe1, 0x91, 0xd8, 0x96,
-	0x60, 0x45, 0xfd, 0xb4, 0x2c, 0xc1, 0xaf, 0xa3, 0xac, 0xcb, 0x5f, 0xc9, 0x08, 0xfa, 0x2c, 0x45,
-	0x7a, 0xe5, 0xc7, 0x33, 0x62, 0x4f, 0xe0, 0x88, 0xa2, 0x2d, 0xea, 0xcd, 0x14, 0x79, 0xfc, 0x79,
-	0x05, 0x6c, 0x3e, 0x92, 0x22, 0xf9, 0xd5, 0x05, 0x57, 0x24, 0x1a, 0xaa, 0x8a, 0x04, 0x88, 0x3b,
-	0x08, 0x87, 0x50, 0x2f, 0x6c, 0x04, 0xb4, 0xe1, 0x8b, 0x8a, 0x21, 0xec, 0x14, 0x85, 0x8d, 0x0b,
-	0xfd, 0xfb, 0xd6, 0xa0, 0x8a, 0x22, 0xaf, 0x86, 0x86, 0x09, 0x91, 0x32, 0xca, 0x7c, 0x52, 0xca,
-	0x58, 0xb0, 0x07, 0x9f, 0xd0, 0x60, 0xf7, 0x93, 0x58, 0x20, 0x7d, 0x58, 0xe3, 0x2c, 0xe0, 0xcd,
-	0x54, 0x16, 0x70, 0x8c, 0x87, 0x91, 0xf8, 0x7e, 0x06, 0xdb, 0xa1, 0x12, 0x46, 0xf2, 0x87, 0xb5,
-	0x28, 0x8c, 0x04, 0x96, 0x0c, 0x23, 0x01, 0xe3, 0x06, 0x9a, 0xf1, 0xe4, 0xfc, 0x19, 0xae, 0xae,
-	0x12, 0xab, 0x8e, 0x27, 0xd7, 0x9c, 0x55, 0x4a, 0x27, 0x95, 0x55, 0x8a, 0x08, 0xef, 0x22, 0x64,
-	0xf6, 0x33, 0x47, 0xf8, 0xd8, 0x5f, 0xd8, 0x38, 0x13, 0x69, 0x4f, 0xe4, 0x94, 0x35, 0x9d, 0x5d,
-	0x57, 0xe3, 0xe6, 0x8a, 0x5e, 0x49, 0x0d, 0x73, 0x83, 0x19, 0xa5, 0x4e, 0x50, 0x16, 0x91, 0xdc,
-	0xa0, 0xe6, 0x54, 0xe2, 0x4c, 0x8c, 0x30, 0xd5, 0x0d, 0x7d, 0x98, 0x59, 0x19, 0xf4, 0x13, 0x07,
-	0xa8, 0x98, 0x48, 0x56, 0x26, 0x52, 0x0a, 0x6e, 0x65, 0xdc, 0x5c, 0xb5, 0x32, 0xc6, 0xf1, 0x3b,
-	0xa8, 0x10, 0xc6, 0xd7, 0x75, 0x7d, 0x0e, 0xb4, 0xea, 0x4f, 0xba, 0xc9, 0xf3, 0x34, 0x5e, 0xea,
-	0xa0, 0xe8, 0x95, 0x35, 0xe1, 0xef, 0xa3, 0xe9, 0xa8, 0xae, 0x6f, 0x3b, 0x7b, 0x54, 0x5f, 0x50,
-	0x35, 0x27, 0x4b, 0xfa, 0x5c, 0xb3, 0x1d, 0xa3, 0xaa, 0x66, 0x49, 0x80, 0x4d, 0x34, 0xeb, 0x29,
-	0xd7, 0x56, 0x1d, 0xab, 0xfb, 0xe1, 0x80, 0x4b, 0x2d, 0xdf, 0x0f, 0xd5, 0x6e, 0xea, 0x7e, 0xa8,
-	0xca, 0x58, 0x04, 0x87, 0xfc, 0x90, 0xd5, 0x17, 0xd5, 0x08, 0x96, 0xcf, 0x5e, 0x1e, 0xc1, 0xa2,
-	0xa1, 0x1a, 0xc1, 0x02, 0xc4, 0x87, 0x48, 0xc4, 0x4a, 0xfc, 0x41, 0x5a, 0x5f, 0x52, 0xe3, 0x77,
-	0xe0, 0x57, 0x6b, 0x1e, 0xbf, 0xc9, 0xae, 0x6a, 0xfc, 0x26, 0xa5, 0x8c, 0x73, 0x6e, 0x54, 0xe9,
-	0xd0, 0x97, 0x55, 0xce, 0xa9, 0x25, 0x10, 0x91, 0x0e, 0x45, 0x98, 0xca, 0xb9, 0x3e, 0xcc, 0x38,
-	0xe7, 0xf6, 0x0b, 0x4c, 0xfa, 0x8a, 0xca, 0xb9, 0x44, 0xe9, 0x89, 0x73, 0x2e, 0x6e, 0xae, 0x72,
-	0x2e, 0xc6, 0x6b, 0x39, 0x94, 0x81, 0xaf, 0xed, 0x7e, 0xf9, 0xc7, 0x29, 0x34, 0x97, 0x28, 0xd4,
-	0xe1, 0xff, 0x43, 0x53, 0x90, 0x7f, 0xf1, 0x64, 0x06, 0xf7, 0xba, 0xa5, 0x59, 0x47, 0x4d, 0xbe,
-	0x40, 0x8e, 0x37, 0x50, 0x2e, 0x2a, 0x98, 0x8a, 0x8a, 0x19, 0x24, 0x32, 0x11, 0x26, 0x27, 0x32,
-	0x11, 0x86, 0xab, 0x28, 0xdb, 0xe1, 0x87, 0xbd, 0x48, 0x65, 0x60, 0xfd, 0x04, 0x24, 0xa7, 0x77,
-	0x02, 0x92, 0xb2, 0xb3, 0xa9, 0x53, 0x14, 0x85, 0xfb, 0xf5, 0xc2, 0xf4, 0xd3, 0xd4, 0x0b, 0xcb,
-	0x1f, 0x20, 0x0c, 0x0e, 0xdc, 0x0d, 0x3c, 0x62, 0x74, 0xa2, 0x6c, 0x64, 0x0d, 0xa5, 0xfa, 0x29,
-	0xdd, 0x7c, 0xaf, 0x5b, 0x9a, 0xb6, 0xe5, 0xe4, 0x2c, 0x65, 0x5b, 0xb8, 0x16, 0xcf, 0x86, 0xe7,
-	0x17, 0x0b, 0x30, 0xa0, 0x9c, 0xd3, 0x9c, 0x34, 0xc1, 0xf2, 0x4f, 0x26, 0xd1, 0xcc, 0x0e, 0xe4,
-	0x79, 0x75, 0x9e, 0x41, 0x9d, 0x62, 0xdc, 0x17, 0x50, 0xfa, 0x9e, 0x11, 0x98, 0xfb, 0x30, 0x6a,
-	0x8e, 0x4f, 0x0d, 0x00, 0x79, 0x6a, 0x00, 0xe0, 0x4d, 0x34, 0xb7, 0xe7, 0xd1, 0x4e, 0x43, 0x0c,
-	0xc7, 0x92, 0xce, 0xc9, 0xf8, 0x99, 0x3b, 0x13, 0x09, 0x43, 0xd5, 0x67, 0xee, 0x8a, 0x20, 0x4e,
-	0x3f, 0xa7, 0x4e, 0x4c, 0x3f, 0xaf, 0xa1, 0x59, 0xe2, 0x79, 0xd4, 0xdb, 0xde, 0xbb, 0x61, 0xfb,
-	0x3e, 0xe3, 0x6c, 0x1a, 0x6c, 0x84, 0xf0, 0x57, 0x25, 0x52, 0xe7, 0x44, 0x1f, 0xfc, 0x1a, 0x9a,
-	0xde, 0xa3, 0x9e, 0x49, 0x1a, 0x6d, 0xd2, 0x32, 0xcc, 0x23, 0x48, 0x06, 0x72, 0x7c, 0x87, 0x02,
-	0xfc, 0x4d, 0x80, 0xe5, 0x4f, 0x18, 0x12, 0x8c, 0x2f, 0xa3, 0x3c, 0xef, 0xed, 0x90, 0x7b, 0x70,
-	0xfc, 0xe7, 0x38, 0x33, 0x01, 0x7c, 0x8b, 0xdc, 0x93, 0x99, 0x19, 0x61, 0xe5, 0x5f, 0xa4, 0xd0,
-	0xf4, 0x3b, 0xcc, 0x65, 0xd1, 0x32, 0xf4, 0x27, 0xad, 0x9d, 0x38, 0xe9, 0xe1, 0x92, 0xfa, 0x4b,
-	0x28, 0x0b, 0x4b, 0xd3, 0x5f, 0x12, 0x7e, 0xae, 0x7b, 0xb4, 0xa3, 0x74, 0xc8, 0x70, 0xe4, 0x98,
-	0x4f, 0xa6, 0x86, 0xf7, 0x49, 0xfa, 0x74, 0x3e, 0xb9, 0xf8, 0x5d, 0x94, 0x86, 0xe0, 0xc1, 0x79,
-	0x94, 0xde, 0x62, 0x2b, 0x34, 0x3f, 0x81, 0x0b, 0x28, 0xbb, 0x75, 0xd7, 0x36, 0x03, 0x62, 0xcd,
-	0x6b, 0x38, 0x8b, 0x26, 0x6f, 0xde, 0xbc, 0x31, 0x9f, 0xc2, 0x4b, 0x68, 0xfe, 0x1a, 0x31, 0xac,
-	0xb6, 0xed, 0x90, 0xad, 0xfb, 0x3c, 0x6b, 0x98, 0x9f, 0xdc, 0xf8, 0x87, 0x86, 0xd2, 0xfc, 0x8a,
-	0x44, 0xd0, 0xdc, 0xeb, 0x24, 0xe0, 0x3c, 0x07, 0xc4, 0xc7, 0xb8, 0x9f, 0x92, 0xf4, 0xa9, 0x5f,
-	0x3c, 0x13, 0xc7, 0x8f, 0x12, 0x8b, 0xe5, 0xe7, 0x7e, 0xf4, 0x97, 0x2f, 0x7f, 0x99, 0xba, 0x50,
-	0xd6, 0xab, 0x77, 0xff, 0xbf, 0x7a, 0x40, 0x9b, 0x97, 0x7c, 0x12, 0x54, 0x1f, 0x80, 0xf7, 0x3f,
-	0xac, 0x3e, 0xb0, 0xad, 0x0f, 0xaf, 0x68, 0x17, 0x5f, 0xd2, 0xf0, 0x15, 0x94, 0x86, 0x35, 0xc4,
-	0x3c, 0x10, 0xe5, 0xf5, 0x7c, 0xb2, 0xee, 0xc9, 0x8f, 0x53, 0x1a, 0xf4, 0xcd, 0xbc, 0x01, 0x7f,
-	0xb5, 0x85, 0x57, 0x8e, 0xdd, 0x98, 0xb6, 0x98, 0x63, 0x8a, 0xfc, 0xec, 0xe4, 0x8d, 0x36, 0xf7,
-	0x89, 0x79, 0x58, 0x27, 0xbe, 0x4b, 0x1d, 0x9f, 0xd4, 0xde, 0xfb, 0xe2, 0xef, 0xab, 0x13, 0x1f,
-	0x3d, 0x5a, 0xd5, 0x3e, 0x7f, 0xb4, 0xaa, 0x3d, 0x7c, 0xb4, 0xaa, 0xfd, 0xed, 0xd1, 0xaa, 0xf6,
-	0xc9, 0xe3, 0xd5, 0x89, 0x87, 0x8f, 0x57, 0x27, 0xbe, 0x78, 0xbc, 0x3a, 0xf1, 0xc3, 0xe7, 0xa5,
-	0x3f, 0xf3, 0x32, 0xbc, 0x8e, 0x61, 0x19, 0xae, 0x47, 0x0f, 0x88, 0x19, 0x88, 0x5f, 0xd1, 0x5f,
-	0x69, 0xfd, 0x26, 0xb5, 0x74, 0x15, 0x80, 0x5b, 0x5c, 0x5c, 0xd9, 0xa6, 0x95, 0xab, 0xae, 0xdd,
-	0xcc, 0x80, 0x2d, 0x97, 0xff, 0x13, 0x00, 0x00, 0xff, 0xff, 0xb8, 0x2f, 0x05, 0xeb, 0xb3, 0x36,
-	0x00, 0x00,
+	0x15, 0xd6, 0x52, 0xe2, 0xdf, 0x50, 0xbf, 0xa3, 0x1f, 0xaf, 0xe9, 0x58, 0x14, 0x18, 0xa0, 0x51,
+	0x8c, 0x98, 0x4c, 0xe5, 0xa4, 0x08, 0x8c, 0x00, 0x85, 0x29, 0xcb, 0x89, 0x04, 0x3b, 0x76, 0x28,
+	0x1b, 0x69, 0x8b, 0x00, 0xcc, 0x72, 0x77, 0x44, 0xad, 0x44, 0xee, 0x6c, 0xf6, 0xc7, 0xb6, 0x62,
+	0x04, 0x08, 0x5a, 0xb4, 0xc8, 0xa5, 0x68, 0x8a, 0xf6, 0x9e, 0x00, 0xbd, 0xf5, 0xd4, 0x4b, 0xaf,
+	0x3d, 0x14, 0x3d, 0xa4, 0x37, 0x17, 0xbd, 0xe4, 0xc4, 0xb6, 0x76, 0x52, 0x14, 0x3c, 0xf4, 0xde,
+	0x5b, 0x31, 0x6f, 0x66, 0xb9, 0x33, 0x2b, 0x1a, 0x92, 0x99, 0x26, 0x35, 0x08, 0x5e, 0x12, 0xf3,
+	0x7b, 0x33, 0x6f, 0xde, 0xbc, 0xf9, 0xde, 0xcc, 0x9b, 0x7d, 0x23, 0xb4, 0xe8, 0x1e, 0xb6, 0xaa,
+	0x86, 0x6b, 0x57, 0xc9, 0x5d, 0xe2, 0x04, 0x15, 0xd7, 0xa3, 0x01, 0xc5, 0x93, 0x86, 0x6b, 0x17,
+	0x4b, 0x2d, 0x4a, 0x5b, 0x6d, 0x52, 0x05, 0xa8, 0x19, 0xee, 0x55, 0x03, 0xbb, 0x43, 0xfc, 0xc0,
+	0xe8, 0xb8, 0xbc, 0x55, 0x71, 0x29, 0xea, 0xea, 0x87, 0xcd, 0x8e, 0x1d, 0x24, 0xd1, 0x7d, 0x62,
+	0xb4, 0x83, 0x7d, 0x81, 0x9e, 0x4b, 0x2a, 0x23, 0x1d, 0x37, 0x38, 0x12, 0xc2, 0x8b, 0x2d, 0x3b,
+	0xd8, 0x0f, 0x9b, 0x15, 0x93, 0x76, 0xaa, 0x2d, 0xda, 0xa2, 0x71, 0x2b, 0xf6, 0x0b, 0x7e, 0xc0,
+	0xbf, 0x44, 0xf3, 0xe7, 0x84, 0x2e, 0x36, 0x88, 0xe1, 0x38, 0x34, 0x30, 0x02, 0x9b, 0x3a, 0xbe,
+	0x90, 0xbe, 0x72, 0xf8, 0x9a, 0x5f, 0xb1, 0x29, 0x93, 0x76, 0x0c, 0x73, 0xdf, 0x76, 0x88, 0x77,
+	0x54, 0x8d, 0x6c, 0xf2, 0x88, 0x4f, 0x43, 0xcf, 0x24, 0xd5, 0x16, 0x71, 0x88, 0x67, 0x04, 0xc4,
+	0xe2, 0xbd, 0xca, 0xbf, 0x4e, 0xa1, 0x85, 0x1d, 0xda, 0xdc, 0x85, 0x99, 0x04, 0xc4, 0xda, 0x62,
+	0xde, 0xc0, 0x17, 0x50, 0xe6, 0x80, 0x36, 0x1b, 0xb6, 0xa5, 0x6b, 0x6b, 0xda, 0x7a, 0xbe, 0xb6,
+	0xd8, 0xeb, 0x96, 0xe6, 0x0e, 0x68, 0x73, 0xdb, 0x7a, 0x89, 0x76, 0xec, 0x00, 0xe6, 0x50, 0x4f,
+	0x03, 0x80, 0x5f, 0x41, 0x88, 0xb5, 0xf5, 0x49, 0xc0, 0xda, 0xa7, 0xa0, 0xfd, 0x4a, 0xaf, 0x5b,
+	0xc2, 0x07, 0xb4, 0xb9, 0x4b, 0x02, 0xa5, 0x4b, 0x2e, 0xc2, 0xf0, 0x8b, 0x28, 0xfd, 0x7e, 0x48,
+	0x42, 0xa2, 0x4f, 0xc6, 0x03, 0x00, 0x20, 0x0f, 0x00, 0x00, 0xde, 0x46, 0x59, 0xd3, 0x23, 0xcc,
+	0x66, 0x7d, 0x6a, 0x4d, 0x5b, 0x2f, 0x6c, 0x14, 0x2b, 0xdc, 0x11, 0x95, 0xc8, 0x5d, 0x95, 0xdb,
+	0xd1, 0x0a, 0xd5, 0x16, 0x3f, 0xef, 0x96, 0x26, 0x7a, 0xdd, 0x52, 0xd4, 0xe5, 0x93, 0xbf, 0x95,
+	0xb4, 0x7a, 0xf4, 0x03, 0xbf, 0x80, 0x26, 0x0f, 0x68, 0x53, 0x4f, 0x83, 0x9a, 0x5c, 0xc5, 0x70,
+	0xed, 0xca, 0x0e, 0x6d, 0xd6, 0x0a, 0xa2, 0x13, 0x13, 0xd6, 0xd9, 0x7f, 0xca, 0xff, 0xd2, 0xd0,
+	0xec, 0x0e, 0x6d, 0xbe, 0xcd, 0x0c, 0x18, 0x6d, 0x9f, 0x94, 0x7f, 0x9f, 0x42, 0x2b, 0x3b, 0xb4,
+	0x79, 0x35, 0x74, 0xdb, 0xb6, 0x69, 0x04, 0xe4, 0x1a, 0x0d, 0x9d, 0x11, 0xa7, 0xc1, 0x26, 0x9a,
+	0xa3, 0x9e, 0xdd, 0xb2, 0x1d, 0xa3, 0xdd, 0x10, 0x13, 0x4c, 0xc3, 0xf8, 0xe7, 0x7a, 0xdd, 0xd2,
+	0x99, 0x48, 0xb4, 0x93, 0x98, 0xe8, 0x8c, 0x22, 0x28, 0x7f, 0x96, 0x02, 0x8a, 0x5c, 0x27, 0x86,
+	0x3f, 0xea, 0x61, 0xf3, 0x3d, 0x84, 0xcc, 0x76, 0xe8, 0x07, 0xc4, 0x8b, 0x5d, 0x75, 0xa6, 0xd7,
+	0x2d, 0x2d, 0x0a, 0x54, 0x31, 0x36, 0xdf, 0x07, 0xcb, 0xbf, 0x98, 0x42, 0xcb, 0x91, 0x8b, 0xea,
+	0x24, 0x08, 0x3d, 0x67, 0xec, 0xa9, 0x81, 0x9e, 0xc2, 0x2f, 0xa1, 0x8c, 0x47, 0x0c, 0x9f, 0x3a,
+	0x7a, 0x06, 0xfa, 0x2c, 0xf5, 0xba, 0xa5, 0x79, 0x8e, 0x48, 0x1d, 0x44, 0x1b, 0xfc, 0x7d, 0x34,
+	0x73, 0x18, 0x36, 0x89, 0xe7, 0x90, 0x80, 0xf8, 0x6c, 0xa0, 0x2c, 0x74, 0x2a, 0xf6, 0xba, 0xa5,
+	0x95, 0x58, 0xa0, 0x8c, 0x35, 0x2d, 0xe3, 0xcc, 0x4c, 0x97, 0x5a, 0x0d, 0x27, 0xec, 0x34, 0x89,
+	0xa7, 0xe7, 0xd6, 0xb4, 0xf5, 0x34, 0x37, 0xd3, 0xa5, 0xd6, 0x5b, 0x00, 0xca, 0x66, 0xf6, 0x41,
+	0x36, 0xb0, 0x17, 0x3a, 0x0d, 0x23, 0x00, 0x11, 0xb1, 0xf4, 0xfc, 0x9a, 0xb6, 0x9e, 0xe3, 0x03,
+	0x7b, 0xa1, 0x73, 0x25, 0xc2, 0xe5, 0x81, 0x65, 0xbc, 0xfc, 0x6f, 0x0d, 0x2d, 0x45, 0x8c, 0xd8,
+	0xba, 0xef, 0xda, 0xde, 0xa8, 0xef, 0xae, 0x3f, 0x9f, 0x42, 0x73, 0x3b, 0xb4, 0x79, 0x8b, 0x38,
+	0x96, 0xed, 0xb4, 0xc6, 0xe4, 0x1f, 0x44, 0xfe, 0x63, 0x74, 0xce, 0x7c, 0x2d, 0x3a, 0x67, 0x4f,
+	0x4d, 0xe7, 0x97, 0x51, 0x0e, 0xfa, 0x19, 0x1d, 0x02, 0x41, 0x90, 0xaf, 0x2d, 0xf7, 0xba, 0xa5,
+	0x05, 0xd6, 0xc0, 0xe8, 0xc8, 0xbe, 0xca, 0x0a, 0x88, 0x99, 0x1a, 0xf5, 0xf0, 0x5d, 0xc3, 0x24,
+	0x10, 0x00, 0xc2, 0x54, 0xd1, 0x06, 0x70, 0xd9, 0x54, 0x19, 0x2f, 0xff, 0x91, 0xf3, 0xa1, 0x1e,
+	0x3a, 0xce, 0x98, 0x0f, 0xdf, 0x14, 0x1f, 0x2e, 0xa1, 0xbc, 0x43, 0x2d, 0xc2, 0x17, 0x36, 0x1b,
+	0xfb, 0x88, 0x81, 0x89, 0x95, 0xcd, 0x45, 0xd8, 0xd0, 0x7b, 0xa2, 0x4c, 0xa2, 0xfc, 0x70, 0x24,
+	0x42, 0x4f, 0x49, 0xa2, 0xdf, 0x65, 0xd0, 0x22, 0x4b, 0x42, 0x9c, 0x96, 0x47, 0x7c, 0x7f, 0xdb,
+	0xd9, 0xa3, 0x63, 0x22, 0x8d, 0x16, 0x91, 0xd0, 0x70, 0x44, 0x2a, 0x3c, 0x1d, 0x91, 0xf0, 0x03,
+	0xb4, 0x60, 0x73, 0x12, 0x35, 0x0c, 0xcb, 0x62, 0xff, 0x27, 0xbe, 0x9e, 0x5f, 0x9b, 0x5c, 0x2f,
+	0x6c, 0x54, 0xa2, 0xdb, 0x51, 0x92, 0x65, 0x15, 0x01, 0x5c, 0x89, 0x3a, 0x6c, 0x39, 0x81, 0x77,
+	0x54, 0x5b, 0xed, 0x75, 0x4b, 0x45, 0x3b, 0x21, 0x92, 0x06, 0x9e, 0x4f, 0xca, 0x8a, 0x87, 0x68,
+	0x79, 0xa0, 0x2a, 0xfc, 0x3c, 0x9a, 0x3c, 0x24, 0x47, 0xc0, 0xe1, 0x74, 0x6d, 0xa1, 0xd7, 0x2d,
+	0xcd, 0x1c, 0x92, 0x23, 0x49, 0x15, 0x93, 0x32, 0x26, 0xde, 0x35, 0xda, 0x21, 0x11, 0xd4, 0x05,
+	0x26, 0x02, 0x20, 0x33, 0x11, 0x80, 0xcb, 0xa9, 0xd7, 0xb4, 0xf2, 0x7f, 0xa6, 0x90, 0xbe, 0x43,
+	0x9b, 0x77, 0x1c, 0xa3, 0xd9, 0x26, 0xb7, 0xe9, 0xae, 0xb9, 0x4f, 0xac, 0xb0, 0x4d, 0xc6, 0x71,
+	0xf3, 0x0c, 0x64, 0xa3, 0x4a, 0x94, 0xe5, 0x86, 0x8a, 0xb2, 0xfc, 0x33, 0x1c, 0x65, 0xe5, 0x87,
+	0x59, 0xb8, 0x29, 0x5e, 0x33, 0xec, 0xf6, 0xf8, 0xfe, 0xf3, 0xbf, 0x60, 0xdc, 0xbb, 0x08, 0x91,
+	0xfb, 0x76, 0xd0, 0x30, 0xa9, 0x45, 0x7c, 0x3d, 0x0b, 0xfb, 0x55, 0x39, 0xda, 0xaf, 0x24, 0x37,
+	0x57, 0xb6, 0xee, 0xdb, 0xc1, 0x26, 0x6b, 0xc4, 0xf7, 0xa8, 0xb3, 0xcc, 0x12, 0x12, 0x61, 0xb1,
+	0x62, 0x5d, 0xab, 0xe7, 0xfb, 0xf0, 0x71, 0x3e, 0xe7, 0xbe, 0x0e, 0x9f, 0xf3, 0x43, 0xf1, 0x19,
+	0x0d, 0xc5, 0xe7, 0x99, 0xe1, 0xf8, 0x3c, 0xfb, 0x94, 0xa7, 0x86, 0x85, 0xb0, 0x49, 0x9d, 0xc0,
+	0xb0, 0x1d, 0xe2, 0x35, 0xfc, 0xc0, 0x08, 0x42, 0x76, 0x6c, 0x14, 0x60, 0x19, 0x96, 0x60, 0x19,
+	0x36, 0x23, 0xf1, 0x2e, 0x48, 0x6b, 0xa5, 0x5e, 0xb7, 0x74, 0xce, 0x54, 0x41, 0xe5, 0x74, 0x58,
+	0x38, 0x26, 0xc4, 0xaf, 0xa2, 0xb4, 0x69, 0x84, 0x3e, 0xd1, 0xa7, 0xd7, 0xb4, 0xf5, 0xd9, 0x0d,
+	0xc4, 0x15, 0x33, 0x84, 0x93, 0x19, 0x84, 0x32, 0x99, 0x01, 0x28, 0x5a, 0x68, 0x56, 0x5d, 0x75,
+	0xf9, 0x38, 0xc9, 0x9f, 0xee, 0x38, 0x49, 0x9f, 0x78, 0x9c, 0xfc, 0x26, 0x85, 0x30, 0xbb, 0xd6,
+	0x79, 0x84, 0x89, 0x46, 0x3e, 0x93, 0x7f, 0x15, 0xe5, 0x3d, 0xf2, 0x7e, 0x48, 0xfc, 0x80, 0x7a,
+	0x72, 0x54, 0xf7, 0x41, 0x99, 0x9b, 0x7d, 0xb0, 0xfc, 0xd5, 0x24, 0x7c, 0x5c, 0x16, 0x5e, 0x1a,
+	0xef, 0x7d, 0x83, 0xf7, 0xbe, 0x0b, 0x28, 0xe3, 0x85, 0x4e, 0x9c, 0x9e, 0x82, 0xb9, 0x5e, 0xe8,
+	0xa8, 0xfe, 0x00, 0x00, 0x6f, 0xa3, 0x05, 0x57, 0x70, 0xee, 0x2e, 0x89, 0xbe, 0x5d, 0xf2, 0xf3,
+	0xf6, 0x7c, 0xaf, 0x5b, 0x3a, 0x1b, 0x0b, 0x93, 0x5f, 0x2f, 0xe7, 0x12, 0xa2, 0x84, 0x2a, 0x61,
+	0x41, 0x6e, 0x90, 0xaa, 0x7a, 0xc2, 0x96, 0xb9, 0x84, 0xa8, 0xbc, 0x05, 0xb9, 0x95, 0xb4, 0xf1,
+	0x6e, 0xd2, 0x8e, 0x0b, 0x19, 0x1d, 0xac, 0x05, 0x54, 0x58, 0x60, 0xb1, 0xa7, 0xf9, 0xe4, 0x00,
+	0x90, 0x27, 0x07, 0x40, 0xf9, 0x4f, 0x53, 0xa2, 0x16, 0x61, 0x9a, 0x84, 0x58, 0x63, 0xba, 0x8c,
+	0x6f, 0xc7, 0x43, 0xdd, 0x8e, 0x3f, 0xcd, 0xc3, 0xed, 0xf8, 0x4e, 0x60, 0xb7, 0x6d, 0x1f, 0x4a,
+	0x64, 0x63, 0x22, 0x7d, 0x23, 0x44, 0xfa, 0x58, 0x43, 0xcb, 0x37, 0x8c, 0xfb, 0x75, 0x51, 0x5b,
+	0xf4, 0xaf, 0x51, 0xef, 0x16, 0xf1, 0x6c, 0x6a, 0x89, 0x94, 0xec, 0x52, 0x94, 0x92, 0x25, 0x97,
+	0xa2, 0x32, 0xb0, 0x17, 0xcf, 0xd1, 0xce, 0x8b, 0xb9, 0x0e, 0xd6, 0x5c, 0x1f, 0x0c, 0x8f, 0xfa,
+	0x15, 0x02, 0xff, 0x4c, 0x43, 0x2b, 0x01, 0x0d, 0x8c, 0x76, 0xc3, 0x0c, 0x3b, 0x61, 0xdb, 0x80,
+	0x3d, 0x3b, 0xf4, 0x8d, 0x16, 0x4b, 0x8f, 0x98, 0xaf, 0x37, 0x9e, 0xe8, 0xeb, 0xdb, 0xac, 0xdb,
+	0x66, 0xbf, 0xd7, 0x1d, 0xd6, 0x89, 0xbb, 0xfa, 0x39, 0xe1, 0xea, 0xa5, 0x60, 0x40, 0x93, 0xfa,
+	0x40, 0xb4, 0xf8, 0x99, 0x86, 0x8a, 0x4f, 0x5e, 0xbd, 0xd3, 0xe5, 0x5a, 0x3f, 0x94, 0x73, 0xad,
+	0xc2, 0x46, 0xa5, 0xc2, 0x2b, 0xd7, 0x15, 0xb9, 0x72, 0x5d, 0x71, 0x0f, 0x5b, 0x30, 0xa5, 0xa8,
+	0x72, 0x5d, 0x79, 0x3b, 0x34, 0x9c, 0xc0, 0x0e, 0x8e, 0x4e, 0xca, 0xcd, 0x8a, 0x9f, 0x6a, 0xe8,
+	0xec, 0x13, 0x27, 0xfd, 0x2c, 0x58, 0x58, 0xfe, 0x8a, 0x97, 0x5c, 0xeb, 0xc4, 0xf5, 0x6c, 0xea,
+	0xd9, 0x81, 0xfd, 0xc1, 0xc8, 0x67, 0x90, 0xaf, 0xa3, 0x69, 0x87, 0xdc, 0x6b, 0x88, 0x09, 0x1f,
+	0xc1, 0x36, 0xa5, 0xc1, 0x85, 0x6c, 0xd9, 0x21, 0xf7, 0x6e, 0x09, 0x58, 0x32, 0xa1, 0x20, 0xc1,
+	0x6a, 0xfe, 0x99, 0x39, 0x75, 0xfe, 0xf9, 0x65, 0x0a, 0xea, 0x8f, 0x92, 0x9f, 0x47, 0x3d, 0xa9,
+	0xf8, 0xbf, 0xb8, 0xf9, 0x2f, 0xfc, 0x32, 0xb4, 0x69, 0x38, 0x26, 0x69, 0xb7, 0xc7, 0x97, 0xa1,
+	0x81, 0x5e, 0x7a, 0xba, 0x4f, 0x1c, 0xe5, 0x87, 0xfc, 0x5d, 0x8e, 0xf0, 0xe9, 0xa8, 0xd3, 0xf6,
+	0x5b, 0x71, 0xe9, 0x1f, 0xa6, 0x80, 0xa6, 0xb7, 0x89, 0xd7, 0xb1, 0x1d, 0x63, 0x7c, 0x1d, 0x7d,
+	0x96, 0xab, 0xb1, 0xdf, 0xce, 0x55, 0x41, 0x22, 0x50, 0xee, 0x14, 0x04, 0xfa, 0x73, 0x0a, 0x6a,
+	0xb7, 0x77, 0x5c, 0x6b, 0xf4, 0xd9, 0x33, 0x64, 0x44, 0x8a, 0x07, 0x76, 0x99, 0x13, 0x1f, 0xd8,
+	0x7d, 0x34, 0x87, 0xa6, 0xc1, 0x83, 0x37, 0x88, 0xcf, 0x92, 0x33, 0x7c, 0x13, 0xe5, 0xfd, 0xe8,
+	0x11, 0x22, 0xf8, 0xb2, 0xb0, 0xb1, 0x12, 0xf5, 0x57, 0x5f, 0x27, 0x72, 0x43, 0xfa, 0x8d, 0x63,
+	0x43, 0xde, 0x9c, 0xa8, 0xc7, 0x3a, 0xf0, 0x26, 0xca, 0x80, 0x57, 0x2c, 0x91, 0xc4, 0x2d, 0x46,
+	0xda, 0xa4, 0x47, 0x7d, 0x7c, 0xc1, 0x79, 0x33, 0x45, 0x8f, 0xe8, 0x8a, 0x2d, 0x34, 0x67, 0x45,
+	0x0f, 0xe3, 0x1a, 0x7b, 0x34, 0x74, 0x2c, 0x7d, 0x1e, 0xb4, 0x9d, 0x8b, 0xb4, 0x0d, 0x78, 0x37,
+	0x57, 0x7b, 0xae, 0xd7, 0x2d, 0xe9, 0x96, 0x22, 0x50, 0xb4, 0xcf, 0xaa, 0x32, 0x66, 0x6a, 0x1b,
+	0x9e, 0x91, 0xc1, 0x1a, 0x4b, 0xa6, 0x4a, 0x8f, 0xcb, 0xb8, 0xa9, 0xbc, 0x99, 0x6a, 0x2a, 0xc7,
+	0xf0, 0x7b, 0x68, 0x16, 0xfe, 0xd5, 0xf0, 0xc4, 0x4b, 0xab, 0x3e, 0x07, 0x64, 0x65, 0xca, 0x33,
+	0x2c, 0xfe, 0xde, 0xad, 0x2d, 0xe3, 0x8a, 0xea, 0x19, 0x45, 0x84, 0xdf, 0x45, 0x1c, 0x68, 0x10,
+	0xfe, 0x72, 0x47, 0xbc, 0xa3, 0x3c, 0xab, 0x0c, 0x20, 0xbf, 0xea, 0xe1, 0x91, 0xd8, 0x96, 0x60,
+	0x45, 0xfd, 0xb4, 0x2c, 0xc1, 0x6f, 0xa0, 0xac, 0xcb, 0x5f, 0xc9, 0x08, 0xfa, 0x2c, 0x45, 0x7a,
+	0xe5, 0xc7, 0x33, 0x62, 0x4f, 0xe0, 0x88, 0xa2, 0x2d, 0xea, 0xcd, 0x14, 0x79, 0xfc, 0x79, 0x05,
+	0x6c, 0x3e, 0x92, 0x22, 0xf9, 0xd5, 0x05, 0x57, 0x24, 0x1a, 0xaa, 0x8a, 0x04, 0x88, 0x3b, 0x08,
+	0x87, 0x50, 0x2f, 0x6c, 0x04, 0xb4, 0xe1, 0x8b, 0x8a, 0x21, 0xec, 0x14, 0x85, 0x8d, 0xf3, 0xfd,
+	0xfb, 0xd6, 0xa0, 0x8a, 0x22, 0xaf, 0x86, 0x86, 0x09, 0x91, 0x32, 0xca, 0x7c, 0x52, 0xca, 0x58,
+	0xb0, 0x07, 0x9f, 0xd0, 0x60, 0xf7, 0x93, 0x58, 0x20, 0x7d, 0x58, 0xe3, 0x2c, 0xe0, 0xcd, 0x54,
+	0x16, 0x70, 0x8c, 0x87, 0x91, 0xf8, 0x7e, 0x06, 0xdb, 0xa1, 0x12, 0x46, 0xf2, 0x87, 0xb5, 0x28,
+	0x8c, 0x04, 0x96, 0x0c, 0x23, 0x01, 0xe3, 0x06, 0x9a, 0xf1, 0xe4, 0xfc, 0x19, 0xae, 0xae, 0x12,
+	0xab, 0x8e, 0x27, 0xd7, 0x9c, 0x55, 0x4a, 0x27, 0x95, 0x55, 0x8a, 0x08, 0xef, 0x22, 0x64, 0xf6,
+	0x33, 0x47, 0xf8, 0xd8, 0x5f, 0xd8, 0x38, 0x13, 0x69, 0x4f, 0xe4, 0x94, 0x35, 0x9d, 0x5d, 0x57,
+	0xe3, 0xe6, 0x8a, 0x5e, 0x49, 0x0d, 0x73, 0x83, 0x19, 0xa5, 0x4e, 0x50, 0x16, 0x91, 0xdc, 0xa0,
+	0xe6, 0x54, 0xe2, 0x4c, 0x8c, 0x30, 0xd5, 0x0d, 0x7d, 0x98, 0x59, 0x19, 0xf4, 0x13, 0x07, 0xa8,
+	0x98, 0x48, 0x56, 0x26, 0x52, 0x0a, 0x6e, 0x65, 0xdc, 0x5c, 0xb5, 0x32, 0xc6, 0xf1, 0x3b, 0xa8,
+	0x10, 0xc6, 0xd7, 0x75, 0x7d, 0x0e, 0xb4, 0xea, 0x4f, 0xba, 0xc9, 0xf3, 0x34, 0x5e, 0xea, 0xa0,
+	0xe8, 0x95, 0x35, 0xe1, 0x1f, 0xa0, 0xe9, 0xa8, 0xae, 0x6f, 0x3b, 0x7b, 0x54, 0x5f, 0x50, 0x35,
+	0x27, 0x4b, 0xfa, 0x5c, 0xb3, 0x1d, 0xa3, 0xaa, 0x66, 0x49, 0x80, 0x4d, 0x34, 0xeb, 0x29, 0xd7,
+	0x56, 0x1d, 0xab, 0xfb, 0xe1, 0x80, 0x4b, 0x2d, 0xdf, 0x0f, 0xd5, 0x6e, 0xea, 0x7e, 0xa8, 0xca,
+	0x58, 0x04, 0x87, 0xfc, 0x90, 0xd5, 0x17, 0xd5, 0x08, 0x96, 0xcf, 0x5e, 0x1e, 0xc1, 0xa2, 0xa1,
+	0x1a, 0xc1, 0x02, 0xc4, 0x87, 0x48, 0xc4, 0x4a, 0xfc, 0x41, 0x5a, 0x5f, 0x52, 0xe3, 0x77, 0xe0,
+	0x57, 0x6b, 0x1e, 0xbf, 0xc9, 0xae, 0x6a, 0xfc, 0x26, 0xa5, 0x8c, 0x73, 0x6e, 0x54, 0xe9, 0xd0,
+	0x97, 0x55, 0xce, 0xa9, 0x25, 0x10, 0x91, 0x0e, 0x45, 0x98, 0xca, 0xb9, 0x3e, 0xcc, 0x38, 0xe7,
+	0xf6, 0x0b, 0x4c, 0xfa, 0x8a, 0xca, 0xb9, 0x44, 0xe9, 0x89, 0x73, 0x2e, 0x6e, 0xae, 0x72, 0x2e,
+	0xc6, 0x6b, 0x39, 0x94, 0x81, 0xaf, 0xed, 0x7e, 0xf9, 0x27, 0x29, 0x34, 0x97, 0x28, 0xd4, 0xe1,
+	0xef, 0xa0, 0x29, 0xc8, 0xbf, 0x78, 0x32, 0x83, 0x7b, 0xdd, 0xd2, 0xac, 0xa3, 0x26, 0x5f, 0x20,
+	0xc7, 0x1b, 0x28, 0x17, 0x15, 0x4c, 0x45, 0xc5, 0x0c, 0x12, 0x99, 0x08, 0x93, 0x13, 0x99, 0x08,
+	0xc3, 0x55, 0x94, 0xed, 0xf0, 0xc3, 0x5e, 0xa4, 0x32, 0xb0, 0x7e, 0x02, 0x92, 0xd3, 0x3b, 0x01,
+	0x49, 0xd9, 0xd9, 0xd4, 0x29, 0x8a, 0xc2, 0xfd, 0x7a, 0x61, 0xfa, 0x69, 0xea, 0x85, 0xe5, 0x0f,
+	0x10, 0x06, 0x07, 0xee, 0x06, 0x1e, 0x31, 0x3a, 0x51, 0x36, 0xb2, 0x86, 0x52, 0xfd, 0x94, 0x6e,
+	0xbe, 0xd7, 0x2d, 0x4d, 0xdb, 0x72, 0x72, 0x96, 0xb2, 0x2d, 0x5c, 0x8b, 0x67, 0xc3, 0xf3, 0x8b,
+	0x05, 0x18, 0x50, 0xce, 0x69, 0x4e, 0x9a, 0x60, 0xf9, 0xa7, 0x93, 0x68, 0x66, 0x07, 0xf2, 0xbc,
+	0x3a, 0xcf, 0xa0, 0x4e, 0x31, 0xee, 0x8b, 0x28, 0x7d, 0xcf, 0x08, 0xcc, 0x7d, 0x18, 0x35, 0xc7,
+	0xa7, 0x06, 0x80, 0x3c, 0x35, 0x00, 0xf0, 0x26, 0x9a, 0xdb, 0xf3, 0x68, 0xa7, 0x21, 0x86, 0x63,
+	0x49, 0xe7, 0x64, 0xfc, 0xcc, 0x9d, 0x89, 0x84, 0xa1, 0xea, 0x33, 0x77, 0x45, 0x10, 0xa7, 0x9f,
+	0x53, 0x27, 0xa6, 0x9f, 0x57, 0xd1, 0x2c, 0xf1, 0x3c, 0xea, 0x6d, 0xef, 0xdd, 0xb0, 0x7d, 0x9f,
+	0x71, 0x36, 0x0d, 0x36, 0x42, 0xf8, 0xab, 0x12, 0xa9, 0x73, 0xa2, 0x0f, 0x7e, 0x1d, 0x4d, 0xef,
+	0x51, 0xcf, 0x24, 0x8d, 0x36, 0x69, 0x19, 0xe6, 0x11, 0x24, 0x03, 0x39, 0xbe, 0x43, 0x01, 0x7e,
+	0x1d, 0x60, 0xf9, 0x13, 0x86, 0x04, 0xe3, 0x4b, 0x28, 0xcf, 0x7b, 0x3b, 0xe4, 0x1e, 0x1c, 0xff,
+	0x39, 0xce, 0x4c, 0x00, 0xdf, 0x22, 0xf7, 0x64, 0x66, 0x46, 0x58, 0xf9, 0x97, 0x29, 0x34, 0xfd,
+	0x0e, 0x73, 0x59, 0xb4, 0x0c, 0xfd, 0x49, 0x6b, 0x27, 0x4e, 0x7a, 0xb8, 0xa4, 0xfe, 0x22, 0xca,
+	0xc2, 0xd2, 0xf4, 0x97, 0x84, 0x9f, 0xeb, 0x1e, 0xed, 0x28, 0x1d, 0x32, 0x1c, 0x39, 0xe6, 0x93,
+	0xa9, 0xe1, 0x7d, 0x92, 0x3e, 0x9d, 0x4f, 0x2e, 0x5c, 0x47, 0x69, 0x08, 0x1e, 0x9c, 0x47, 0xe9,
+	0x2d, 0xb6, 0x42, 0xf3, 0x13, 0xb8, 0x80, 0xb2, 0x5b, 0x77, 0x6d, 0x33, 0x20, 0xd6, 0xbc, 0x86,
+	0xb3, 0x68, 0xf2, 0xe6, 0xcd, 0x1b, 0xf3, 0x29, 0xbc, 0x84, 0xe6, 0xaf, 0x12, 0xc3, 0x6a, 0xdb,
+	0x0e, 0xd9, 0xba, 0xcf, 0xb3, 0x86, 0xf9, 0x49, 0x3c, 0x8d, 0x72, 0x75, 0x72, 0x40, 0xa0, 0xf1,
+	0xd4, 0xc6, 0x3f, 0x35, 0x94, 0xe6, 0x17, 0x26, 0x82, 0xe6, 0xde, 0x20, 0x01, 0x67, 0x3d, 0x20,
+	0x3e, 0xc6, 0xfd, 0x04, 0xa5, 0x1f, 0x08, 0xc5, 0x33, 0x71, 0x34, 0x29, 0x91, 0x59, 0x7e, 0xfe,
+	0xc7, 0x7f, 0xfd, 0xf2, 0x57, 0xa9, 0xf3, 0x65, 0xbd, 0x7a, 0xf7, 0xbb, 0xd5, 0x03, 0xda, 0xbc,
+	0xe8, 0x93, 0xa0, 0xfa, 0x00, 0xd6, 0xe2, 0xc3, 0xea, 0x03, 0xdb, 0xfa, 0xf0, 0xb2, 0x76, 0xe1,
+	0x65, 0x0d, 0x5f, 0x46, 0x69, 0x58, 0x51, 0xcc, 0xc3, 0x52, 0x5e, 0xdd, 0x27, 0xeb, 0x9e, 0xfc,
+	0x38, 0xa5, 0x41, 0xdf, 0xcc, 0x9b, 0xf0, 0x37, 0x5c, 0x78, 0xe5, 0xd8, 0xfd, 0x69, 0x8b, 0xb9,
+	0xa9, 0xc8, 0x4f, 0x52, 0xde, 0x68, 0x73, 0x9f, 0x98, 0x87, 0x75, 0xe2, 0xbb, 0xd4, 0xf1, 0x49,
+	0xed, 0xbd, 0x2f, 0xfe, 0xb1, 0x3a, 0xf1, 0xd1, 0xa3, 0x55, 0xed, 0xf3, 0x47, 0xab, 0xda, 0xc3,
+	0x47, 0xab, 0xda, 0xdf, 0x1f, 0xad, 0x6a, 0x9f, 0x3c, 0x5e, 0x9d, 0x78, 0xf8, 0x78, 0x75, 0xe2,
+	0x8b, 0xc7, 0xab, 0x13, 0x3f, 0x7a, 0x41, 0xfa, 0xa3, 0x2f, 0xc3, 0xeb, 0x18, 0x96, 0xe1, 0x7a,
+	0x94, 0x39, 0x4a, 0xfc, 0x8a, 0xfe, 0x66, 0xeb, 0xb7, 0xa9, 0xa5, 0x2b, 0x00, 0xdc, 0xe2, 0xe2,
+	0xca, 0x36, 0xad, 0x5c, 0x71, 0xed, 0x66, 0x06, 0x6c, 0xb9, 0xf4, 0xdf, 0x00, 0x00, 0x00, 0xff,
+	0xff, 0xde, 0x9d, 0x6d, 0xce, 0xc1, 0x36, 0x00, 0x00,
 }
 
 // Reference imports to suppress errors if they are not otherwise used.
diff --git a/pkg/api/event.proto b/pkg/api/event.proto
index 97a5f771..b6d3de88 100644
--- a/pkg/api/event.proto
+++ b/pkg/api/event.proto
@@ -277,6 +277,7 @@ enum Cause {
     Evicted = 1;
     OOM = 2;
     DeadlineExceeded = 3;
+    Rejected  = 4;
 }
 
 message ContainerStatus {
diff --git a/pkg/armadaevents/events.pb.go b/pkg/armadaevents/events.pb.go
index 948774a1..18d64476 100644
--- a/pkg/armadaevents/events.pb.go
+++ b/pkg/armadaevents/events.pb.go
@@ -206,6 +206,7 @@ type EventSequence_Event struct {
 	//	*EventSequence_Event_JobRequeued
 	//	*EventSequence_Event_JobPreemptionRequested
 	//	*EventSequence_Event_JobRunCancelled
+	//	*EventSequence_Event_JobValidated
 	Event isEventSequence_Event_Event `protobuf_oneof:"event"`
 }
 
@@ -317,6 +318,9 @@ type EventSequence_Event_JobPreemptionRequested struct {
 type EventSequence_Event_JobRunCancelled struct {
 	JobRunCancelled *JobRunCancelled `protobuf:"bytes,24,opt,name=jobRunCancelled,proto3,oneof" json:"jobRunCancelled,omitempty"`
 }
+type EventSequence_Event_JobValidated struct {
+	JobValidated *JobValidated `protobuf:"bytes,25,opt,name=jobValidated,proto3,oneof" json:"jobValidated,omitempty"`
+}
 
 func (*EventSequence_Event_SubmitJob) isEventSequence_Event_Event()                 {}
 func (*EventSequence_Event_ReprioritiseJob) isEventSequence_Event_Event()           {}
@@ -341,6 +345,7 @@ func (*EventSequence_Event_JobRunPreemptionRequested) isEventSequence_Event_Even
 func (*EventSequence_Event_JobRequeued) isEventSequence_Event_Event()               {}
 func (*EventSequence_Event_JobPreemptionRequested) isEventSequence_Event_Event()    {}
 func (*EventSequence_Event_JobRunCancelled) isEventSequence_Event_Event()           {}
+func (*EventSequence_Event_JobValidated) isEventSequence_Event_Event()              {}
 
 func (m *EventSequence_Event) GetEvent() isEventSequence_Event_Event {
 	if m != nil {
@@ -517,6 +522,13 @@ func (m *EventSequence_Event) GetJobRunCancelled() *JobRunCancelled {
 	return nil
 }
 
+func (m *EventSequence_Event) GetJobValidated() *JobValidated {
+	if x, ok := m.GetEvent().(*EventSequence_Event_JobValidated); ok {
+		return x.JobValidated
+	}
+	return nil
+}
+
 // XXX_OneofWrappers is for the internal use of the proto package.
 func (*EventSequence_Event) XXX_OneofWrappers() []interface{} {
 	return []interface{}{
@@ -543,6 +555,7 @@ func (*EventSequence_Event) XXX_OneofWrappers() []interface{} {
 		(*EventSequence_Event_JobRequeued)(nil),
 		(*EventSequence_Event_JobPreemptionRequested)(nil),
 		(*EventSequence_Event_JobRunCancelled)(nil),
+		(*EventSequence_Event_JobValidated)(nil),
 	}
 }
 
@@ -2436,6 +2449,7 @@ type Error struct {
 	//	*Error_PodTerminated
 	//	*Error_JobRunPreemptedError
 	//	*Error_GangJobUnschedulable
+	//	*Error_JobRejected
 	Reason isError_Reason `protobuf_oneof:"reason"`
 }
 
@@ -2511,6 +2525,9 @@ type Error_JobRunPreemptedError struct {
 type Error_GangJobUnschedulable struct {
 	GangJobUnschedulable *GangJobUnschedulable `protobuf:"bytes,12,opt,name=gangJobUnschedulable,proto3,oneof" json:"gangJobUnschedulable,omitempty"`
 }
+type Error_JobRejected struct {
+	JobRejected *JobRejected `protobuf:"bytes,13,opt,name=jobRejected,proto3,oneof" json:"jobRejected,omitempty"`
+}
 
 func (*Error_KubernetesError) isError_Reason()      {}
 func (*Error_ContainerError) isError_Reason()       {}
@@ -2523,6 +2540,7 @@ func (*Error_PodLeaseReturned) isError_Reason()     {}
 func (*Error_PodTerminated) isError_Reason()        {}
 func (*Error_JobRunPreemptedError) isError_Reason() {}
 func (*Error_GangJobUnschedulable) isError_Reason() {}
+func (*Error_JobRejected) isError_Reason()          {}
 
 func (m *Error) GetReason() isError_Reason {
 	if m != nil {
@@ -2615,6 +2633,13 @@ func (m *Error) GetGangJobUnschedulable() *GangJobUnschedulable {
 	return nil
 }
 
+func (m *Error) GetJobRejected() *JobRejected {
+	if x, ok := m.GetReason().(*Error_JobRejected); ok {
+		return x.JobRejected
+	}
+	return nil
+}
+
 // XXX_OneofWrappers is for the internal use of the proto package.
 func (*Error) XXX_OneofWrappers() []interface{} {
 	return []interface{}{
@@ -2629,6 +2654,7 @@ func (*Error) XXX_OneofWrappers() []interface{} {
 		(*Error_PodTerminated)(nil),
 		(*Error_JobRunPreemptedError)(nil),
 		(*Error_GangJobUnschedulable)(nil),
+		(*Error_JobRejected)(nil),
 	}
 }
 
@@ -3275,6 +3301,50 @@ func (m *GangJobUnschedulable) GetMessage() string {
 	return ""
 }
 
+type JobRejected struct {
+	Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
+}
+
+func (m *JobRejected) Reset()         { *m = JobRejected{} }
+func (m *JobRejected) String() string { return proto.CompactTextString(m) }
+func (*JobRejected) ProtoMessage()    {}
+func (*JobRejected) Descriptor() ([]byte, []int) {
+	return fileDescriptor_6aab92ca59e015f8, []int{39}
+}
+func (m *JobRejected) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *JobRejected) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_JobRejected.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *JobRejected) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_JobRejected.Merge(m, src)
+}
+func (m *JobRejected) XXX_Size() int {
+	return m.Size()
+}
+func (m *JobRejected) XXX_DiscardUnknown() {
+	xxx_messageInfo_JobRejected.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_JobRejected proto.InternalMessageInfo
+
+func (m *JobRejected) GetMessage() string {
+	if m != nil {
+		return m.Message
+	}
+	return ""
+}
+
 // Generated by the scheduler whenever it detects a SubmitJob message that includes a previously used deduplication id
 // (i.e., when it detects a duplicate job submission).
 type JobDuplicateDetected struct {
@@ -3286,7 +3356,7 @@ func (m *JobDuplicateDetected) Reset()         { *m = JobDuplicateDetected{} }
 func (m *JobDuplicateDetected) String() string { return proto.CompactTextString(m) }
 func (*JobDuplicateDetected) ProtoMessage()    {}
 func (*JobDuplicateDetected) Descriptor() ([]byte, []int) {
-	return fileDescriptor_6aab92ca59e015f8, []int{39}
+	return fileDescriptor_6aab92ca59e015f8, []int{40}
 }
 func (m *JobDuplicateDetected) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -3345,7 +3415,7 @@ func (m *JobRunPreempted) Reset()         { *m = JobRunPreempted{} }
 func (m *JobRunPreempted) String() string { return proto.CompactTextString(m) }
 func (*JobRunPreempted) ProtoMessage()    {}
 func (*JobRunPreempted) Descriptor() ([]byte, []int) {
-	return fileDescriptor_6aab92ca59e015f8, []int{40}
+	return fileDescriptor_6aab92ca59e015f8, []int{41}
 }
 func (m *JobRunPreempted) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -3414,7 +3484,7 @@ func (m *PartitionMarker) Reset()         { *m = PartitionMarker{} }
 func (m *PartitionMarker) String() string { return proto.CompactTextString(m) }
 func (*PartitionMarker) ProtoMessage()    {}
 func (*PartitionMarker) Descriptor() ([]byte, []int) {
-	return fileDescriptor_6aab92ca59e015f8, []int{41}
+	return fileDescriptor_6aab92ca59e015f8, []int{42}
 }
 func (m *PartitionMarker) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -3467,7 +3537,7 @@ func (m *JobRunPreemptionRequested) Reset()         { *m = JobRunPreemptionReque
 func (m *JobRunPreemptionRequested) String() string { return proto.CompactTextString(m) }
 func (*JobRunPreemptionRequested) ProtoMessage()    {}
 func (*JobRunPreemptionRequested) Descriptor() ([]byte, []int) {
-	return fileDescriptor_6aab92ca59e015f8, []int{42}
+	return fileDescriptor_6aab92ca59e015f8, []int{43}
 }
 func (m *JobRunPreemptionRequested) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -3519,7 +3589,7 @@ func (m *JobPreemptionRequested) Reset()         { *m = JobPreemptionRequested{}
 func (m *JobPreemptionRequested) String() string { return proto.CompactTextString(m) }
 func (*JobPreemptionRequested) ProtoMessage()    {}
 func (*JobPreemptionRequested) Descriptor() ([]byte, []int) {
-	return fileDescriptor_6aab92ca59e015f8, []int{43}
+	return fileDescriptor_6aab92ca59e015f8, []int{44}
 }
 func (m *JobPreemptionRequested) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -3555,6 +3625,51 @@ func (m *JobPreemptionRequested) GetJobId() *Uuid {
 	return nil
 }
 
+// Indicates that the scheduler is happy with the job
+type JobValidated struct {
+	JobId *Uuid `protobuf:"bytes,1,opt,name=job_id,json=jobId,proto3" json:"jobId,omitempty"`
+}
+
+func (m *JobValidated) Reset()         { *m = JobValidated{} }
+func (m *JobValidated) String() string { return proto.CompactTextString(m) }
+func (*JobValidated) ProtoMessage()    {}
+func (*JobValidated) Descriptor() ([]byte, []int) {
+	return fileDescriptor_6aab92ca59e015f8, []int{45}
+}
+func (m *JobValidated) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *JobValidated) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_JobValidated.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *JobValidated) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_JobValidated.Merge(m, src)
+}
+func (m *JobValidated) XXX_Size() int {
+	return m.Size()
+}
+func (m *JobValidated) XXX_DiscardUnknown() {
+	xxx_messageInfo_JobValidated.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_JobValidated proto.InternalMessageInfo
+
+func (m *JobValidated) GetJobId() *Uuid {
+	if m != nil {
+		return m.JobId
+	}
+	return nil
+}
+
 // Generated by the scheduler when a job is cancelled, all active job runs are also cancelled
 // One such message is generated per job run that was cancelled.
 type JobRunCancelled struct {
@@ -3566,7 +3681,7 @@ func (m *JobRunCancelled) Reset()         { *m = JobRunCancelled{} }
 func (m *JobRunCancelled) String() string { return proto.CompactTextString(m) }
 func (*JobRunCancelled) ProtoMessage()    {}
 func (*JobRunCancelled) Descriptor() ([]byte, []int) {
-	return fileDescriptor_6aab92ca59e015f8, []int{44}
+	return fileDescriptor_6aab92ca59e015f8, []int{46}
 }
 func (m *JobRunCancelled) XXX_Unmarshal(b []byte) error {
 	return m.Unmarshal(b)
@@ -3658,249 +3773,255 @@ func init() {
 	proto.RegisterType((*MaxRunsExceeded)(nil), "armadaevents.MaxRunsExceeded")
 	proto.RegisterType((*JobRunPreemptedError)(nil), "armadaevents.JobRunPreemptedError")
 	proto.RegisterType((*GangJobUnschedulable)(nil), "armadaevents.GangJobUnschedulable")
+	proto.RegisterType((*JobRejected)(nil), "armadaevents.JobRejected")
 	proto.RegisterType((*JobDuplicateDetected)(nil), "armadaevents.JobDuplicateDetected")
 	proto.RegisterType((*JobRunPreempted)(nil), "armadaevents.JobRunPreempted")
 	proto.RegisterType((*PartitionMarker)(nil), "armadaevents.PartitionMarker")
 	proto.RegisterType((*JobRunPreemptionRequested)(nil), "armadaevents.JobRunPreemptionRequested")
 	proto.RegisterType((*JobPreemptionRequested)(nil), "armadaevents.JobPreemptionRequested")
+	proto.RegisterType((*JobValidated)(nil), "armadaevents.JobValidated")
 	proto.RegisterType((*JobRunCancelled)(nil), "armadaevents.JobRunCancelled")
 }
 
 func init() { proto.RegisterFile("pkg/armadaevents/events.proto", fileDescriptor_6aab92ca59e015f8) }
 
 var fileDescriptor_6aab92ca59e015f8 = []byte{
-	// 3691 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x3b, 0x4b, 0x70, 0x1b, 0xc7,
-	0x95, 0x1a, 0x80, 0xc4, 0xe7, 0x81, 0x1f, 0xa8, 0xf9, 0x11, 0x44, 0x4b, 0x04, 0x35, 0xf2, 0xae,
-	0x65, 0x97, 0x0d, 0xda, 0xf2, 0xa7, 0xfc, 0xd9, 0xb2, 0x8b, 0x90, 0x68, 0x49, 0xb4, 0x28, 0xd1,
-	0xa0, 0xe8, 0xd5, 0xba, 0xbc, 0x05, 0x0f, 0x30, 0x4d, 0x70, 0x44, 0x60, 0x06, 0x9e, 0x0f, 0x45,
-	0x56, 0xf9, 0xb0, 0xde, 0xf2, 0x7a, 0x6f, 0x8e, 0x52, 0xf1, 0x21, 0x55, 0x39, 0x38, 0xd7, 0xb8,
-	0x2a, 0xe7, 0x9c, 0x73, 0x8a, 0x0f, 0xa9, 0x94, 0x73, 0x4b, 0xe5, 0x80, 0xa4, 0xec, 0xca, 0x05,
-	0x87, 0x9c, 0x93, 0x5c, 0x92, 0xea, 0xdf, 0x4c, 0xf7, 0xcc, 0x40, 0xa2, 0x7e, 0x91, 0x53, 0x3a,
-	0x91, 0xf3, 0xfe, 0xdd, 0xfd, 0xfa, 0xf5, 0x7b, 0xaf, 0x1b, 0x70, 0xb2, 0xbf, 0xdb, 0x59, 0x36,
-	0xdc, 0x9e, 0x61, 0x1a, 0x78, 0x0f, 0xdb, 0xbe, 0xb7, 0xcc, 0xfe, 0xd4, 0xfa, 0xae, 0xe3, 0x3b,
-	0x68, 0x42, 0x46, 0x2d, 0xe8, 0xbb, 0xaf, 0x7a, 0x35, 0xcb, 0x59, 0x36, 0xfa, 0xd6, 0x72, 0xdb,
-	0x71, 0xf1, 0xf2, 0xde, 0x0b, 0xcb, 0x1d, 0x6c, 0x63, 0xd7, 0xf0, 0xb1, 0xc9, 0x38, 0x16, 0xce,
-	0x48, 0x34, 0x36, 0xf6, 0x6f, 0x3a, 0xee, 0xae, 0x65, 0x77, 0xd2, 0x28, 0xab, 0x1d, 0xc7, 0xe9,
-	0x74, 0xf1, 0x32, 0xfd, 0x6a, 0x05, 0xdb, 0xcb, 0xbe, 0xd5, 0xc3, 0x9e, 0x6f, 0xf4, 0xfa, 0x9c,
-	0xe0, 0xa5, 0x48, 0x54, 0xcf, 0x68, 0xef, 0x58, 0x36, 0x76, 0x0f, 0x96, 0xa9, 0xbd, 0x7d, 0x6b,
-	0xd9, 0xc5, 0x9e, 0x13, 0xb8, 0x6d, 0x9c, 0x10, 0xfb, 0x5c, 0xc7, 0xf2, 0x77, 0x82, 0x56, 0xad,
-	0xed, 0xf4, 0x96, 0x3b, 0x4e, 0xc7, 0x89, 0xe4, 0x93, 0x2f, 0xfa, 0x41, 0xff, 0xe3, 0xe4, 0xaf,
-	0x5b, 0xb6, 0x8f, 0x5d, 0xdb, 0xe8, 0x2e, 0x7b, 0xed, 0x1d, 0x6c, 0x06, 0x5d, 0xec, 0x46, 0xff,
-	0x39, 0xad, 0x1b, 0xb8, 0xed, 0x7b, 0x09, 0x00, 0xe3, 0xd5, 0x3f, 0x99, 0x87, 0xc9, 0x55, 0x32,
-	0x35, 0x9b, 0xf8, 0xa3, 0x00, 0xdb, 0x6d, 0x8c, 0x9e, 0x86, 0xf1, 0x8f, 0x02, 0x1c, 0xe0, 0x8a,
-	0xb6, 0xa4, 0x9d, 0x29, 0xd6, 0x67, 0x86, 0x83, 0xea, 0x34, 0x05, 0x3c, 0xeb, 0xf4, 0x2c, 0x1f,
-	0xf7, 0xfa, 0xfe, 0x41, 0x83, 0x51, 0xa0, 0xd7, 0x61, 0xe2, 0x86, 0xd3, 0x6a, 0x7a, 0xd8, 0x6f,
-	0xda, 0x46, 0x0f, 0x57, 0x32, 0x94, 0xa3, 0x32, 0x1c, 0x54, 0x67, 0x6f, 0x38, 0xad, 0x4d, 0xec,
-	0x5f, 0x31, 0x7a, 0x32, 0x1b, 0x44, 0x50, 0xf4, 0x1c, 0xe4, 0x03, 0x0f, 0xbb, 0x4d, 0xcb, 0xac,
-	0x64, 0x29, 0xdb, 0xec, 0x70, 0x50, 0x2d, 0x13, 0xd0, 0x25, 0x53, 0x62, 0xc9, 0x31, 0x08, 0x7a,
-	0x16, 0x72, 0x1d, 0xd7, 0x09, 0xfa, 0x5e, 0x65, 0x6c, 0x29, 0x2b, 0xa8, 0x19, 0x44, 0xa6, 0x66,
-	0x10, 0x74, 0x15, 0x72, 0x6c, 0xbd, 0x2b, 0xe3, 0x4b, 0xd9, 0x33, 0xa5, 0xb3, 0xa7, 0x6a, 0xb2,
-	0x13, 0xd4, 0x94, 0x01, 0xb3, 0x2f, 0x26, 0x90, 0xe1, 0x65, 0x81, 0xdc, 0x6d, 0x7e, 0x3f, 0x03,
-	0xe3, 0x94, 0x0e, 0x5d, 0x85, 0x7c, 0xdb, 0xc5, 0x64, 0xb1, 0x2a, 0x68, 0x49, 0x3b, 0x53, 0x3a,
-	0xbb, 0x50, 0x63, 0x4e, 0x50, 0x13, 0x8b, 0x54, 0xbb, 0x26, 0x9c, 0xa0, 0x7e, 0x7c, 0x38, 0xa8,
-	0x1e, 0xe5, 0xe4, 0x91, 0xd4, 0x5b, 0x7f, 0xa8, 0x6a, 0x0d, 0x21, 0x05, 0x6d, 0x40, 0xd1, 0x0b,
-	0x5a, 0x3d, 0xcb, 0x5f, 0x73, 0x5a, 0x74, 0xce, 0x4b, 0x67, 0x8f, 0xa9, 0xe6, 0x6e, 0x0a, 0x74,
-	0xfd, 0xd8, 0x70, 0x50, 0x9d, 0x09, 0xa9, 0x23, 0x89, 0x17, 0x8f, 0x34, 0x22, 0x21, 0x68, 0x07,
-	0xa6, 0x5d, 0xdc, 0x77, 0x2d, 0xc7, 0xb5, 0x7c, 0xcb, 0xc3, 0x44, 0x6e, 0x86, 0xca, 0x3d, 0xa9,
-	0xca, 0x6d, 0xa8, 0x44, 0xf5, 0x93, 0xc3, 0x41, 0xf5, 0x78, 0x8c, 0x53, 0xd1, 0x11, 0x17, 0x8b,
-	0x7c, 0x40, 0x31, 0xd0, 0x26, 0xf6, 0xe9, 0x7a, 0x96, 0xce, 0x2e, 0xdd, 0x56, 0xd9, 0x26, 0xf6,
-	0xeb, 0x4b, 0xc3, 0x41, 0xf5, 0x44, 0x92, 0x5f, 0x51, 0x99, 0x22, 0x1f, 0x75, 0xa1, 0x2c, 0x43,
-	0x4d, 0x32, 0xc0, 0x31, 0xaa, 0x73, 0x71, 0xb4, 0x4e, 0x42, 0x55, 0x5f, 0x1c, 0x0e, 0xaa, 0x0b,
-	0x71, 0x5e, 0x45, 0x5f, 0x42, 0x32, 0x59, 0x9f, 0xb6, 0x61, 0xb7, 0x71, 0x97, 0xa8, 0x19, 0x4f,
-	0x5b, 0x9f, 0x73, 0x02, 0xcd, 0xd6, 0x27, 0xa4, 0x56, 0xd7, 0x27, 0x04, 0xa3, 0x0f, 0x60, 0x22,
-	0xfc, 0x20, 0xf3, 0x95, 0xe3, 0x7e, 0x94, 0x2e, 0x94, 0xcc, 0xd4, 0xc2, 0x70, 0x50, 0x9d, 0x97,
-	0x79, 0x14, 0xd1, 0x8a, 0xb4, 0x48, 0x7a, 0x97, 0xcd, 0x4c, 0x7e, 0xb4, 0x74, 0x46, 0x21, 0x4b,
-	0xef, 0x26, 0x67, 0x44, 0x91, 0x46, 0xa4, 0x93, 0x4d, 0x1c, 0xb4, 0xdb, 0x18, 0x9b, 0xd8, 0xac,
-	0x14, 0xd2, 0xa4, 0xaf, 0x49, 0x14, 0x4c, 0xba, 0xcc, 0xa3, 0x4a, 0x97, 0x31, 0x64, 0xae, 0x6f,
-	0x38, 0xad, 0x55, 0xd7, 0x75, 0x5c, 0xaf, 0x52, 0x4c, 0x9b, 0xeb, 0x35, 0x81, 0x66, 0x73, 0x1d,
-	0x52, 0xab, 0x73, 0x1d, 0x82, 0xb9, 0xbd, 0x8d, 0xc0, 0xbe, 0x8c, 0x0d, 0x0f, 0x9b, 0x15, 0x18,
-	0x61, 0x6f, 0x48, 0x11, 0xda, 0x1b, 0x42, 0x12, 0xf6, 0x86, 0x18, 0x64, 0xc2, 0x14, 0xfb, 0x5e,
-	0xf1, 0x3c, 0xab, 0x63, 0x63, 0xb3, 0x52, 0xa2, 0xf2, 0x4f, 0xa4, 0xc9, 0x17, 0x34, 0xf5, 0x13,
-	0xc3, 0x41, 0xb5, 0xa2, 0xf2, 0x29, 0x3a, 0x62, 0x32, 0xd1, 0x87, 0x30, 0xc9, 0x20, 0x8d, 0xc0,
-	0xb6, 0x2d, 0xbb, 0x53, 0x99, 0xa0, 0x4a, 0x9e, 0x48, 0x53, 0xc2, 0x49, 0xea, 0x4f, 0x0c, 0x07,
-	0xd5, 0x63, 0x0a, 0x97, 0xa2, 0x42, 0x15, 0x48, 0x22, 0x06, 0x03, 0x44, 0x0b, 0x3b, 0x99, 0x16,
-	0x31, 0xd6, 0x54, 0x22, 0x16, 0x31, 0x62, 0x9c, 0x6a, 0xc4, 0x88, 0x21, 0xa3, 0xf5, 0xe0, 0x8b,
-	0x3c, 0x35, 0x7a, 0x3d, 0xf8, 0x3a, 0x4b, 0xeb, 0x91, 0xb2, 0xd4, 0x8a, 0x34, 0xf4, 0x31, 0x90,
-	0x83, 0xe7, 0x7c, 0xd0, 0xef, 0x5a, 0x6d, 0xc3, 0xc7, 0xe7, 0xb1, 0x8f, 0xdb, 0x24, 0x52, 0x4f,
-	0x53, 0x2d, 0x7a, 0x42, 0x4b, 0x82, 0xb2, 0xae, 0x0f, 0x07, 0xd5, 0xc5, 0x34, 0x19, 0x8a, 0xd6,
-	0x54, 0x2d, 0xe8, 0x7f, 0x34, 0x98, 0xf3, 0x7c, 0xc3, 0x36, 0x8d, 0xae, 0x63, 0xe3, 0x4b, 0x76,
-	0xc7, 0xc5, 0x9e, 0x77, 0xc9, 0xde, 0x76, 0x2a, 0x65, 0xaa, 0xff, 0x74, 0x2c, 0xac, 0xa7, 0x91,
-	0xd6, 0x4f, 0x0f, 0x07, 0xd5, 0x6a, 0xaa, 0x14, 0xc5, 0x82, 0x74, 0x45, 0x68, 0x1f, 0x66, 0x44,
-	0x56, 0xb1, 0xe5, 0x5b, 0x5d, 0xcb, 0x33, 0x7c, 0xcb, 0xb1, 0x2b, 0x47, 0xa9, 0xfe, 0x53, 0xf1,
-	0xe8, 0x98, 0x20, 0xac, 0x9f, 0x1a, 0x0e, 0xaa, 0x27, 0x53, 0x24, 0x28, 0xba, 0xd3, 0x54, 0x44,
-	0x2e, 0xb4, 0xe1, 0x62, 0x42, 0x88, 0xcd, 0xca, 0xcc, 0x68, 0x17, 0x0a, 0x89, 0x64, 0x17, 0x0a,
-	0x81, 0x69, 0x2e, 0x14, 0x22, 0x89, 0xa6, 0xbe, 0xe1, 0xfa, 0x16, 0x51, 0xbb, 0x6e, 0xb8, 0xbb,
-	0xd8, 0xad, 0xcc, 0xa6, 0x69, 0xda, 0x50, 0x89, 0x98, 0xa6, 0x18, 0xa7, 0xaa, 0x29, 0x86, 0x44,
-	0xb7, 0x34, 0x50, 0x4d, 0xb3, 0x1c, 0xbb, 0x41, 0xd2, 0x06, 0x8f, 0x0c, 0x6f, 0x8e, 0x2a, 0x7d,
-	0xea, 0x36, 0xc3, 0x93, 0xc9, 0xeb, 0x4f, 0x0d, 0x07, 0xd5, 0xd3, 0x23, 0xa5, 0x29, 0x86, 0x8c,
-	0x56, 0x8a, 0xae, 0x43, 0x89, 0x20, 0x31, 0x4d, 0xc0, 0xcc, 0xca, 0x3c, 0xb5, 0xe1, 0x78, 0xd2,
-	0x06, 0x4e, 0x40, 0x33, 0x90, 0x39, 0x89, 0x43, 0xd1, 0x23, 0x8b, 0x42, 0x9f, 0x6a, 0x40, 0xb6,
-	0x59, 0xda, 0x48, 0x8f, 0x51, 0x2d, 0x4f, 0x26, 0xb4, 0xa4, 0x0d, 0xf3, 0xc9, 0xe1, 0xa0, 0xba,
-	0x94, 0x2e, 0x47, 0xd1, 0x3d, 0x42, 0x57, 0xe4, 0x47, 0xe1, 0x11, 0x55, 0xa9, 0x8c, 0xf6, 0xa3,
-	0x90, 0x48, 0xf6, 0xa3, 0x10, 0x98, 0xe6, 0x47, 0x11, 0x47, 0x1e, 0xc6, 0xa9, 0x2c, 0x7d, 0x98,
-	0x83, 0x99, 0x94, 0xcd, 0x80, 0xde, 0x84, 0x9c, 0x1b, 0xd8, 0x24, 0x43, 0x65, 0x69, 0x19, 0x52,
-	0x2d, 0xd8, 0x0a, 0x2c, 0x93, 0xa5, 0xc7, 0x6e, 0x60, 0x2b, 0x49, 0xeb, 0x38, 0x05, 0x10, 0x7e,
-	0x92, 0x1e, 0x5b, 0x26, 0x4f, 0xbf, 0x46, 0xf2, 0xdf, 0x70, 0x5a, 0x2a, 0x3f, 0x05, 0x20, 0x0c,
-	0x93, 0x62, 0xa7, 0x35, 0x2d, 0x12, 0x46, 0xb2, 0x69, 0xeb, 0xf0, 0x4e, 0xd0, 0xc2, 0xae, 0x8d,
-	0x7d, 0xec, 0x89, 0x31, 0xd0, 0x38, 0x42, 0xc3, 0xa6, 0x2b, 0x41, 0x24, 0xf9, 0x13, 0x32, 0x1c,
-	0x7d, 0xa1, 0x41, 0xa5, 0x67, 0xec, 0x37, 0x05, 0xd0, 0x6b, 0x6e, 0x3b, 0x6e, 0xb3, 0x8f, 0x5d,
-	0xcb, 0x31, 0x69, 0xb6, 0x5d, 0x3a, 0xfb, 0x1f, 0x77, 0x8c, 0x1c, 0xb5, 0x75, 0x63, 0x5f, 0x80,
-	0xbd, 0xb7, 0x1d, 0x77, 0x83, 0xb2, 0xaf, 0xda, 0xbe, 0x7b, 0x50, 0x3f, 0xf9, 0xf5, 0xa0, 0x7a,
-	0x84, 0xf8, 0x61, 0x2f, 0x8d, 0xa6, 0x91, 0x0e, 0x46, 0x3f, 0xd0, 0x60, 0xde, 0x77, 0x7c, 0xa3,
-	0xdb, 0x6c, 0x07, 0xbd, 0xa0, 0x6b, 0xf8, 0xd6, 0x1e, 0x6e, 0x06, 0x9e, 0xd1, 0xc1, 0x3c, 0xa9,
-	0x7f, 0xe3, 0xce, 0x46, 0x5d, 0x23, 0xfc, 0xe7, 0x42, 0xf6, 0x2d, 0xc2, 0xcd, 0x6c, 0x3a, 0xc1,
-	0x6d, 0x9a, 0xf5, 0x53, 0x48, 0x1a, 0xa9, 0xd0, 0x85, 0x9f, 0x6a, 0xb0, 0x30, 0x7a, 0x98, 0xe8,
-	0x34, 0x64, 0x77, 0xf1, 0x01, 0x2f, 0x9b, 0x8e, 0x0e, 0x07, 0xd5, 0xc9, 0x5d, 0x7c, 0x20, 0xcd,
-	0x3a, 0xc1, 0xa2, 0xff, 0x82, 0xf1, 0x3d, 0xa3, 0x1b, 0x60, 0xee, 0x12, 0xb5, 0x1a, 0x2b, 0x10,
-	0x6b, 0x72, 0x81, 0x58, 0xeb, 0xef, 0x76, 0x08, 0xa0, 0x26, 0x56, 0xa4, 0xf6, 0x6e, 0x60, 0xd8,
-	0xbe, 0xe5, 0x1f, 0x30, 0x77, 0xa1, 0x02, 0x64, 0x77, 0xa1, 0x80, 0xd7, 0x33, 0xaf, 0x6a, 0x0b,
-	0x5f, 0x6a, 0x70, 0x7c, 0xe4, 0xa0, 0xbf, 0x0f, 0x16, 0xea, 0x4d, 0x18, 0x23, 0x8e, 0x4f, 0x0a,
-	0xba, 0x1d, 0xab, 0xb3, 0xf3, 0xca, 0x4b, 0xd4, 0x9c, 0x1c, 0xab, 0xbf, 0x18, 0x44, 0xae, 0xbf,
-	0x18, 0x84, 0x14, 0xa5, 0x5d, 0xe7, 0xe6, 0x2b, 0x2f, 0x51, 0xa3, 0x72, 0x4c, 0x09, 0x05, 0xc8,
-	0x4a, 0x28, 0x40, 0xff, 0x7b, 0x0e, 0x8a, 0x61, 0xc5, 0x24, 0xed, 0x41, 0xed, 0x9e, 0xf6, 0xe0,
-	0x45, 0x28, 0x9b, 0xd8, 0xe4, 0x47, 0xbd, 0xe5, 0xd8, 0x62, 0x37, 0x17, 0x59, 0xc0, 0x51, 0x70,
-	0x0a, 0xff, 0x74, 0x0c, 0x85, 0xce, 0x42, 0x81, 0x57, 0x16, 0x07, 0x74, 0x23, 0x4f, 0xd6, 0xe7,
-	0x87, 0x83, 0x2a, 0x12, 0x30, 0x89, 0x35, 0xa4, 0x43, 0x0d, 0x00, 0x56, 0xae, 0xaf, 0x63, 0xdf,
-	0xe0, 0x35, 0x4e, 0x45, 0x1d, 0xc1, 0xd5, 0x10, 0xcf, 0x0a, 0xef, 0x88, 0x5e, 0x2e, 0xbc, 0x23,
-	0x28, 0xfa, 0x00, 0xa0, 0x67, 0x58, 0x36, 0xe3, 0xe3, 0x05, 0x8d, 0x3e, 0x2a, 0xa4, 0xac, 0x87,
-	0x94, 0x4c, 0x7a, 0xc4, 0x29, 0x4b, 0x8f, 0xa0, 0xa4, 0x3c, 0xe6, 0x0d, 0x86, 0x4a, 0x8e, 0xee,
-	0xd2, 0xc5, 0x51, 0xa2, 0xb9, 0xd8, 0x39, 0x52, 0x22, 0x73, 0x16, 0x49, 0xa6, 0x90, 0x42, 0xa6,
-	0xad, 0x6b, 0x6d, 0x63, 0xdf, 0xea, 0x61, 0x5a, 0xca, 0xf0, 0x69, 0x13, 0x30, 0x79, 0xda, 0x04,
-	0x0c, 0xbd, 0x0a, 0x60, 0xf8, 0xeb, 0x8e, 0xe7, 0x5f, 0xb5, 0xdb, 0x98, 0x96, 0x28, 0x05, 0x66,
-	0x7e, 0x04, 0x95, 0xcd, 0x8f, 0xa0, 0xe8, 0x0d, 0x28, 0xf5, 0xf9, 0xa1, 0xd4, 0xea, 0x62, 0x5a,
-	0x82, 0x14, 0xd8, 0x19, 0x2a, 0x81, 0x25, 0x5e, 0x99, 0x1a, 0x5d, 0x80, 0xe9, 0xb6, 0x63, 0xb7,
-	0x03, 0xd7, 0xc5, 0x76, 0xfb, 0x60, 0xd3, 0xd8, 0xc6, 0xb4, 0xdc, 0x28, 0x30, 0x57, 0x89, 0xa1,
-	0x64, 0x57, 0x89, 0xa1, 0xd0, 0xcb, 0x50, 0x0c, 0xdb, 0x35, 0xb4, 0xa2, 0x28, 0xf2, 0xca, 0x5f,
-	0x00, 0x25, 0xe6, 0x88, 0x92, 0x18, 0x6f, 0x79, 0x61, 0x5a, 0x4a, 0xab, 0x04, 0x6e, 0xbc, 0x04,
-	0x96, 0x8d, 0x97, 0xc0, 0xe8, 0x12, 0x1c, 0xa5, 0x89, 0x40, 0xd3, 0xf7, 0xbb, 0x4d, 0x0f, 0xb7,
-	0x1d, 0xdb, 0xf4, 0x68, 0x11, 0x90, 0x65, 0xe6, 0x53, 0xe4, 0x35, 0xbf, 0xbb, 0xc9, 0x50, 0xb2,
-	0xf9, 0x31, 0x94, 0xfe, 0x6b, 0x0d, 0x66, 0xd3, 0x5c, 0x28, 0xe6, 0xce, 0xda, 0x03, 0x71, 0xe7,
-	0xf7, 0xa0, 0xd0, 0x77, 0xcc, 0xa6, 0xd7, 0xc7, 0x6d, 0x1e, 0xb1, 0x62, 0xce, 0xbc, 0xe1, 0x98,
-	0x9b, 0x7d, 0xdc, 0xfe, 0x4f, 0xcb, 0xdf, 0x59, 0xd9, 0x73, 0x2c, 0xf3, 0xb2, 0xe5, 0x71, 0xaf,
-	0xeb, 0x33, 0x8c, 0x92, 0x25, 0xe4, 0x39, 0xb0, 0x5e, 0x80, 0x1c, 0xd3, 0xa2, 0xff, 0x26, 0x0b,
-	0xe5, 0xb8, 0xdb, 0xfe, 0x2b, 0x0d, 0x05, 0x5d, 0x87, 0xbc, 0xc5, 0x6a, 0x04, 0x9e, 0x41, 0xfc,
-	0x9b, 0x14, 0xd3, 0x6b, 0x51, 0x87, 0xb3, 0xb6, 0xf7, 0x42, 0x8d, 0x17, 0x13, 0x74, 0x0a, 0xa8,
-	0x64, 0xce, 0xa9, 0x4a, 0xe6, 0x40, 0xd4, 0x80, 0xbc, 0x87, 0xdd, 0x3d, 0xab, 0x8d, 0x79, 0x70,
-	0xaa, 0xca, 0x92, 0xdb, 0x8e, 0x8b, 0x89, 0xcc, 0x4d, 0x46, 0x12, 0xc9, 0xe4, 0x3c, 0xaa, 0x4c,
-	0x0e, 0x44, 0xef, 0x41, 0xb1, 0xed, 0xd8, 0xdb, 0x56, 0x67, 0xdd, 0xe8, 0xf3, 0xf0, 0x74, 0x32,
-	0x4d, 0xea, 0x39, 0x41, 0xc4, 0xbb, 0x2e, 0xe2, 0x33, 0xd6, 0x75, 0x09, 0xa9, 0xa2, 0x05, 0xfd,
-	0xf3, 0x18, 0x40, 0xb4, 0x38, 0xe8, 0x35, 0x28, 0xe1, 0x7d, 0xdc, 0x0e, 0x7c, 0xc7, 0x15, 0xe7,
-	0x04, 0x6f, 0x62, 0x0a, 0xb0, 0x12, 0xd8, 0x21, 0x82, 0x92, 0x8d, 0x6a, 0x1b, 0x3d, 0xec, 0xf5,
-	0x8d, 0xb6, 0xe8, 0x7e, 0x52, 0x63, 0x42, 0xa0, 0xbc, 0x51, 0x43, 0x20, 0xfa, 0x77, 0x18, 0xa3,
-	0xfd, 0x52, 0xd6, 0xf8, 0x44, 0xc3, 0x41, 0x75, 0xca, 0x56, 0x3b, 0xa5, 0x14, 0x8f, 0xde, 0x82,
-	0xc9, 0xdd, 0xd0, 0xf1, 0x88, 0x6d, 0x63, 0x94, 0x81, 0xa6, 0x76, 0x11, 0x42, 0xb1, 0x6e, 0x42,
-	0x86, 0xa3, 0x6d, 0x28, 0x19, 0xb6, 0xed, 0xf8, 0xf4, 0x0c, 0x12, 0xcd, 0xd0, 0xa7, 0x47, 0xb9,
-	0x69, 0x6d, 0x25, 0xa2, 0x65, 0x59, 0x12, 0x0d, 0x1e, 0x92, 0x04, 0x39, 0x78, 0x48, 0x60, 0xd4,
-	0x80, 0x5c, 0xd7, 0x68, 0xe1, 0xae, 0x08, 0xfa, 0x4f, 0x8e, 0x54, 0x71, 0x99, 0x92, 0x31, 0xe9,
-	0xf4, 0xc8, 0x67, 0x7c, 0xf2, 0x91, 0xcf, 0x20, 0x0b, 0xdb, 0x50, 0x8e, 0xdb, 0x73, 0xb8, 0x04,
-	0xe6, 0x69, 0x39, 0x81, 0x29, 0xde, 0x31, 0x65, 0x32, 0xa0, 0x24, 0x19, 0xf5, 0x30, 0x54, 0xe8,
-	0x3f, 0xd3, 0x60, 0x36, 0x6d, 0xef, 0xa2, 0x75, 0x69, 0xc7, 0x6b, 0xbc, 0xa9, 0x93, 0xe2, 0xea,
-	0x9c, 0x77, 0xc4, 0x56, 0x8f, 0x36, 0x7a, 0x1d, 0xa6, 0x6c, 0xc7, 0xc4, 0x4d, 0x83, 0x28, 0xe8,
-	0x5a, 0x9e, 0x5f, 0xc9, 0xd0, 0x66, 0x39, 0x6d, 0x06, 0x11, 0xcc, 0x8a, 0x40, 0x48, 0xdc, 0x93,
-	0x0a, 0x42, 0xff, 0x3f, 0x0d, 0xa6, 0x63, 0xbd, 0xda, 0xfb, 0x4e, 0xa2, 0xe4, 0xd4, 0x27, 0x73,
-	0xb8, 0xd4, 0x47, 0xff, 0x51, 0x06, 0x4a, 0x52, 0x21, 0x7b, 0xdf, 0x36, 0xdc, 0x80, 0x69, 0x7e,
-	0x52, 0x5a, 0x76, 0x87, 0x95, 0x53, 0x19, 0xde, 0x95, 0x49, 0x5c, 0x8d, 0xac, 0x39, 0xad, 0xcd,
-	0x90, 0x96, 0x56, 0x53, 0xb4, 0x65, 0xe7, 0x29, 0x30, 0x49, 0xc5, 0x94, 0x8a, 0x41, 0xd7, 0x61,
-	0x3e, 0xe8, 0x9b, 0x86, 0x8f, 0x9b, 0x1e, 0xbf, 0x64, 0x68, 0xda, 0x41, 0xaf, 0x85, 0x5d, 0xba,
-	0xe3, 0xc7, 0x59, 0x93, 0x89, 0x51, 0x88, 0x5b, 0x88, 0x2b, 0x14, 0x2f, 0xc9, 0x9c, 0x4d, 0xc3,
-	0xeb, 0x17, 0x01, 0x25, 0x1b, 0xe9, 0xca, 0xfc, 0x6a, 0x87, 0x9c, 0xdf, 0xcf, 0x34, 0x28, 0xc7,
-	0xfb, 0xe3, 0x8f, 0x64, 0xa1, 0x0f, 0xa0, 0x18, 0xf6, 0xba, 0xef, 0xdb, 0x80, 0x67, 0x21, 0xe7,
-	0x62, 0xc3, 0x73, 0x6c, 0xbe, 0x33, 0x69, 0x88, 0x61, 0x10, 0x39, 0xc4, 0x30, 0x88, 0x7e, 0x0d,
-	0x26, 0xd8, 0x0c, 0xbe, 0x6d, 0x75, 0x7d, 0xec, 0xa2, 0xf3, 0x90, 0xf3, 0x7c, 0xc3, 0xc7, 0x5e,
-	0x45, 0x5b, 0xca, 0x9e, 0x99, 0x3a, 0x3b, 0x9f, 0x6c, 0x6b, 0x13, 0x34, 0x93, 0xca, 0x28, 0x65,
-	0xa9, 0x0c, 0xa2, 0xff, 0xaf, 0x06, 0x13, 0x72, 0xf7, 0xfe, 0xc1, 0x88, 0xbd, 0xcb, 0xa1, 0x7d,
-	0x2c, 0x6c, 0xe8, 0x3e, 0x98, 0x95, 0xbd, 0x3b, 0xed, 0xbf, 0xd0, 0xd8, 0xcc, 0x86, 0x6d, 0xdf,
-	0xfb, 0x55, 0xdf, 0x89, 0x5a, 0x21, 0x64, 0x87, 0x79, 0x34, 0xb0, 0x1d, 0xb6, 0x15, 0x42, 0xc3,
-	0x9f, 0xc2, 0x2e, 0x87, 0x3f, 0x05, 0xa1, 0x7f, 0x31, 0x4e, 0x2d, 0x8f, 0x5a, 0xfc, 0x8f, 0xba,
-	0x09, 0x14, 0xcb, 0x4e, 0xb2, 0x77, 0x91, 0x9d, 0x3c, 0x07, 0x79, 0x7a, 0x1c, 0x84, 0x89, 0x03,
-	0x5d, 0x34, 0x02, 0x52, 0xaf, 0x58, 0x19, 0xe4, 0x36, 0x51, 0x6b, 0xfc, 0xfe, 0xa2, 0x16, 0x6a,
-	0xc2, 0xf1, 0x1d, 0xc3, 0x6b, 0x8a, 0x38, 0x6b, 0x36, 0x0d, 0xbf, 0x19, 0xc6, 0x89, 0x1c, 0x2d,
-	0x53, 0x68, 0xdb, 0x70, 0xc7, 0xf0, 0x36, 0x05, 0xcd, 0x8a, 0xbf, 0x91, 0x8c, 0x1a, 0xf3, 0xe9,
-	0x14, 0x68, 0x0b, 0xe6, 0xd2, 0x85, 0xe7, 0xa9, 0xe5, 0xb4, 0xab, 0xed, 0xdd, 0x56, 0xf2, 0x4c,
-	0x0a, 0x1a, 0x7d, 0xa2, 0x41, 0x85, 0x9c, 0xcf, 0x2e, 0xfe, 0x28, 0xb0, 0x5c, 0xdc, 0x23, 0x2b,
-	0xd6, 0x74, 0xf6, 0xb0, 0xdb, 0x35, 0x0e, 0xf8, 0xf5, 0xd4, 0xa9, 0xe4, 0xe9, 0xb1, 0xe1, 0x98,
-	0x0d, 0x89, 0x81, 0x0d, 0xad, 0xaf, 0x02, 0xaf, 0x32, 0x21, 0xf2, 0xd0, 0xd2, 0x29, 0xd6, 0xc6,
-	0x0a, 0x85, 0x72, 0x51, 0xff, 0xab, 0x06, 0x53, 0xea, 0x2d, 0xd2, 0x23, 0x77, 0xcc, 0xc4, 0x96,
-	0xcc, 0x3e, 0xa4, 0x2d, 0xf9, 0x17, 0x0d, 0x26, 0x95, 0xcb, 0xad, 0xc7, 0x67, 0xe8, 0x3f, 0xce,
-	0xc0, 0x7c, 0xba, 0x98, 0x87, 0x52, 0x80, 0x5e, 0x04, 0x92, 0x4a, 0x5e, 0x8a, 0x72, 0xa3, 0xb9,
-	0x44, 0xfd, 0x49, 0x87, 0x20, 0xf2, 0xd0, 0xc4, 0xad, 0x94, 0x60, 0x47, 0xd7, 0xa1, 0x64, 0x49,
-	0xf7, 0x5f, 0xd9, 0xb4, 0x6b, 0x0a, 0xf9, 0xd6, 0x8b, 0x75, 0x29, 0x46, 0xdc, 0x75, 0xc9, 0xa2,
-	0xea, 0x39, 0x18, 0x23, 0xc9, 0x9b, 0xbe, 0x07, 0x79, 0x6e, 0x0e, 0x7a, 0x11, 0x8a, 0x34, 0xce,
-	0xd1, 0x9a, 0x8a, 0x25, 0xee, 0x34, 0xed, 0x20, 0xc0, 0xd8, 0x0b, 0x94, 0x82, 0x80, 0xa1, 0x57,
-	0x00, 0xc8, 0xd6, 0xe6, 0x11, 0x2e, 0x43, 0xe3, 0x04, 0xad, 0xdd, 0xfa, 0x8e, 0x99, 0x08, 0x6b,
-	0xc5, 0x10, 0xa8, 0xff, 0x3c, 0x03, 0x25, 0xf9, 0xc6, 0xed, 0x9e, 0x94, 0x7f, 0x0c, 0xa2, 0xae,
-	0x6e, 0x1a, 0xa6, 0x49, 0xfe, 0x62, 0x71, 0xa4, 0x2d, 0x8f, 0x9c, 0x24, 0xf1, 0xff, 0x8a, 0xe0,
-	0x60, 0x55, 0x14, 0x7d, 0xd3, 0x60, 0xc5, 0x50, 0x92, 0xd6, 0x72, 0x1c, 0xb7, 0xb0, 0x0b, 0x73,
-	0xa9, 0xa2, 0xe4, 0xda, 0x67, 0xfc, 0x41, 0xd5, 0x3e, 0xbf, 0x1c, 0x87, 0xb9, 0xd4, 0x9b, 0xce,
-	0x47, 0xbe, 0x8b, 0xd5, 0x1d, 0x94, 0x7d, 0x20, 0x3b, 0xe8, 0x33, 0x2d, 0x6d, 0x65, 0xd9, 0x25,
-	0xca, 0x6b, 0x87, 0xb8, 0xfe, 0x7d, 0x50, 0x6b, 0xac, 0xba, 0xe5, 0xf8, 0x3d, 0xed, 0x89, 0xdc,
-	0x61, 0xf7, 0x04, 0x7a, 0x9e, 0x95, 0xb1, 0x54, 0x57, 0x9e, 0xea, 0x12, 0x11, 0x22, 0xa6, 0x2a,
-	0xcf, 0x41, 0xe8, 0x2d, 0x98, 0x14, 0x1c, 0xac, 0x79, 0x52, 0x88, 0x3a, 0x1b, 0x9c, 0x26, 0xde,
-	0x3f, 0x99, 0x90, 0xe1, 0xff, 0x5c, 0x1f, 0xfe, 0x9b, 0x06, 0xd3, 0xb1, 0xa7, 0x0f, 0x8f, 0xcf,
-	0x19, 0xf4, 0xb9, 0x06, 0xc5, 0xf0, 0xd5, 0xcd, 0x7d, 0x27, 0xf2, 0x2b, 0x90, 0xc3, 0xec, 0xe5,
-	0x07, 0x0b, 0x77, 0x33, 0xb1, 0x97, 0x79, 0x04, 0xc7, 0xdf, 0xe2, 0xc5, 0x1e, 0x7b, 0x34, 0x38,
-	0xa3, 0xfe, 0x5b, 0x4d, 0xa4, 0xe8, 0x91, 0x4d, 0x8f, 0x74, 0x29, 0xa2, 0x31, 0x65, 0xef, 0x75,
-	0x4c, 0xbf, 0x2a, 0xc2, 0x38, 0xa5, 0x23, 0x25, 0xb4, 0x8f, 0xdd, 0x9e, 0x65, 0x1b, 0x5d, 0x3a,
-	0x9c, 0x02, 0xdb, 0xb7, 0x02, 0x26, 0xef, 0x5b, 0x01, 0x43, 0x3b, 0x30, 0x1d, 0xb5, 0xfd, 0xa8,
-	0x98, 0xf4, 0x07, 0x7f, 0xef, 0xa8, 0x44, 0xac, 0xb1, 0x1f, 0xe3, 0x54, 0xef, 0xcc, 0x63, 0x48,
-	0x64, 0xc2, 0x54, 0xdb, 0xb1, 0x7d, 0xc3, 0xb2, 0xb1, 0xcb, 0x14, 0x65, 0xd3, 0x1e, 0x3c, 0x9d,
-	0x53, 0x68, 0x58, 0xf7, 0x44, 0xe5, 0x53, 0x1f, 0x3c, 0xa9, 0x38, 0xf4, 0x21, 0x4c, 0x8a, 0x32,
-	0x86, 0x29, 0x19, 0x4b, 0x7b, 0xf0, 0xb4, 0x2a, 0x93, 0x30, 0x97, 0x56, 0xb8, 0xd4, 0x07, 0x4f,
-	0x0a, 0x0a, 0x75, 0xa1, 0xdc, 0x77, 0xcc, 0x2d, 0x9b, 0x27, 0xef, 0x46, 0xab, 0x8b, 0x79, 0xaf,
-	0x79, 0x31, 0x91, 0xf2, 0x28, 0x54, 0x2c, 0x14, 0xc7, 0x79, 0xd5, 0x27, 0x84, 0x71, 0x2c, 0xfa,
-	0x00, 0x26, 0xba, 0xa4, 0x9a, 0x5c, 0xdd, 0xef, 0x5b, 0x2e, 0x36, 0xd3, 0x1f, 0xfc, 0x5d, 0x96,
-	0x28, 0x58, 0x20, 0x94, 0x79, 0xd4, 0x47, 0x4f, 0x32, 0x86, 0xac, 0x7e, 0xcf, 0xd8, 0x6f, 0x04,
-	0xb6, 0xb7, 0xba, 0xcf, 0x1f, 0x6f, 0xe5, 0xd3, 0x56, 0x7f, 0x5d, 0x25, 0x62, 0xab, 0x1f, 0xe3,
-	0x54, 0x57, 0x3f, 0x86, 0x44, 0x97, 0x69, 0x9c, 0x67, 0x4b, 0xc2, 0x1e, 0xfe, 0xcd, 0x27, 0x66,
-	0x8b, 0xad, 0x06, 0x6b, 0xfb, 0xf0, 0x2f, 0x45, 0x68, 0x28, 0x81, 0xaf, 0x01, 0x1d, 0x76, 0x03,
-	0xfb, 0x81, 0x6b, 0x63, 0x93, 0x17, 0x55, 0xc9, 0x35, 0x50, 0xa8, 0xc2, 0x35, 0x50, 0xa0, 0x89,
-	0x35, 0x50, 0xb0, 0xc4, 0xa7, 0xfa, 0x8e, 0x79, 0x8d, 0x6d, 0x19, 0x3f, 0x7c, 0x09, 0xf8, 0x44,
-	0x42, 0x55, 0x44, 0xc2, 0x7c, 0x4a, 0xe1, 0x52, 0x7d, 0x4a, 0x41, 0xf1, 0xc7, 0x67, 0xf2, 0x53,
-	0x25, 0x36, 0x53, 0xa5, 0x11, 0x8f, 0xcf, 0x12, 0x94, 0xe1, 0xe3, 0xb3, 0x04, 0x26, 0xf1, 0xf8,
-	0x2c, 0x41, 0x41, 0xb4, 0x77, 0x0c, 0xbb, 0xb3, 0xe6, 0xb4, 0x54, 0xaf, 0x9e, 0x48, 0xd3, 0x7e,
-	0x21, 0x85, 0x92, 0x69, 0x4f, 0x93, 0xa1, 0x6a, 0x4f, 0xa3, 0xa8, 0x17, 0x44, 0x7b, 0x48, 0xff,
-	0x52, 0x83, 0xe9, 0x58, 0x9c, 0x41, 0x6f, 0x42, 0xf8, 0xe2, 0xe4, 0xda, 0x41, 0x5f, 0xa4, 0xc9,
-	0xca, 0x0b, 0x15, 0x02, 0x4f, 0x7b, 0xa1, 0x42, 0xe0, 0xe8, 0x32, 0x40, 0x78, 0x26, 0xdd, 0x2e,
-	0x48, 0xd3, 0x1c, 0x2d, 0xa2, 0x94, 0x73, 0xb4, 0x08, 0xaa, 0xdf, 0x1a, 0x83, 0x82, 0x70, 0xd4,
-	0x87, 0x52, 0x46, 0x2d, 0x43, 0xbe, 0x87, 0x3d, 0xfa, 0x52, 0x25, 0x13, 0x65, 0x43, 0x1c, 0x24,
-	0x67, 0x43, 0x1c, 0xa4, 0x26, 0x6b, 0xd9, 0x7b, 0x4a, 0xd6, 0xc6, 0x0e, 0x9d, 0xac, 0x61, 0x7a,
-	0x4b, 0x2d, 0x85, 0x5b, 0x71, 0x2f, 0x74, 0xfb, 0x18, 0x2e, 0xee, 0xb0, 0x65, 0xc6, 0xd8, 0x1d,
-	0xb6, 0x8c, 0x42, 0xbb, 0x70, 0x54, 0xba, 0xbb, 0xe2, 0xbd, 0x43, 0x12, 0xf8, 0xa6, 0x46, 0x3f,
-	0x09, 0x68, 0x50, 0x2a, 0xb6, 0xbd, 0x77, 0x63, 0x50, 0x39, 0xdb, 0x8d, 0xe3, 0x88, 0x83, 0x99,
-	0xb8, 0x15, 0x74, 0xd6, 0xf9, 0xb4, 0xe7, 0x23, 0x07, 0x93, 0xe1, 0xb2, 0x83, 0xc9, 0x70, 0xfd,
-	0x4f, 0x19, 0x98, 0x52, 0xc7, 0xfb, 0x50, 0x1c, 0xe3, 0x45, 0x28, 0xe2, 0x7d, 0xcb, 0x6f, 0xb6,
-	0x1d, 0x13, 0xf3, 0x92, 0x93, 0xae, 0x33, 0x01, 0x9e, 0x73, 0x4c, 0x65, 0x9d, 0x05, 0x4c, 0xf6,
-	0xa6, 0xec, 0xa1, 0xbc, 0x29, 0x6a, 0xd5, 0x8e, 0xdd, 0xb9, 0x55, 0x9b, 0xbe, 0x4e, 0xc5, 0x87,
-	0xb3, 0x4e, 0xfa, 0x37, 0x19, 0x28, 0xc7, 0xa3, 0xf9, 0xf7, 0x63, 0x0b, 0xaa, 0xbb, 0x29, 0x7b,
-	0xe8, 0xdd, 0xf4, 0x16, 0x4c, 0x92, 0xdc, 0xd3, 0xf0, 0x7d, 0xfe, 0xe8, 0x75, 0x8c, 0xe6, 0x6c,
-	0x2c, 0xb6, 0x05, 0xf6, 0x8a, 0x80, 0x2b, 0xb1, 0x4d, 0x82, 0x27, 0x5c, 0x77, 0xfc, 0x2e, 0x5d,
-	0xf7, 0x93, 0x0c, 0x4c, 0x2a, 0xa7, 0xd6, 0xe3, 0x17, 0xd2, 0xf4, 0x69, 0x98, 0x54, 0x92, 0x41,
-	0xfd, 0x53, 0xe6, 0x67, 0x6a, 0x16, 0xf6, 0xf8, 0xcd, 0xcb, 0x14, 0x4c, 0xc8, 0x59, 0xa5, 0x5e,
-	0x87, 0xe9, 0x58, 0x12, 0x28, 0x0f, 0x40, 0x3b, 0xcc, 0x00, 0xf4, 0x79, 0x98, 0x4d, 0xcb, 0x5d,
-	0xf4, 0x0b, 0x30, 0x9b, 0x96, 0x55, 0xdc, 0xbd, 0x82, 0xaf, 0x34, 0xaa, 0x21, 0xf9, 0xbc, 0xfe,
-	0x22, 0x80, 0x8d, 0x6f, 0x36, 0xef, 0x58, 0x7e, 0xb2, 0xf9, 0xc4, 0x37, 0xd7, 0x62, 0xd5, 0x5a,
-	0x41, 0xc0, 0x88, 0x24, 0xa7, 0x6b, 0x36, 0xef, 0x58, 0xf4, 0x51, 0x49, 0x4e, 0xd7, 0x4c, 0x48,
-	0x12, 0x30, 0xfd, 0xff, 0xb3, 0xa2, 0x33, 0x10, 0xbd, 0x4f, 0x7f, 0x1f, 0xca, 0x7d, 0xf1, 0x71,
-	0x67, 0x6b, 0x69, 0x6d, 0x14, 0xd2, 0xc7, 0x35, 0x4d, 0xa9, 0x18, 0x55, 0x36, 0x2f, 0x7a, 0x33,
-	0x87, 0x94, 0xdd, 0x88, 0x55, 0xbf, 0x53, 0x2a, 0x06, 0xfd, 0x37, 0x1c, 0x15, 0xaf, 0xd9, 0xf6,
-	0xb0, 0x30, 0x3c, 0x3b, 0x52, 0x38, 0x7b, 0x4e, 0x1f, 0x32, 0xc4, 0x2d, 0x9f, 0x8e, 0xa1, 0x62,
-	0xe2, 0xb9, 0xed, 0x63, 0x87, 0x15, 0x1f, 0x37, 0x7e, 0x3a, 0x86, 0xd2, 0x3f, 0xd7, 0x60, 0x3a,
-	0xf6, 0xe2, 0x1f, 0x9d, 0x87, 0x02, 0xfd, 0x41, 0xe0, 0xed, 0x57, 0x80, 0x3a, 0x24, 0xa5, 0x53,
-	0x34, 0xe4, 0x39, 0x08, 0xbd, 0x0c, 0xc5, 0xf0, 0x87, 0x01, 0xfc, 0x56, 0x9b, 0x6d, 0x3e, 0x01,
-	0x54, 0x36, 0x9f, 0x00, 0xea, 0x3f, 0xd1, 0xe0, 0xf8, 0xc8, 0x5f, 0x03, 0x3c, 0xea, 0x9e, 0x85,
-	0x7e, 0x1d, 0xe6, 0xd3, 0x1f, 0xf0, 0xdf, 0x6f, 0x87, 0x47, 0xff, 0x61, 0xd8, 0x2c, 0x0b, 0x2f,
-	0xa0, 0x1f, 0xf5, 0x68, 0x9f, 0x79, 0x1e, 0x0a, 0xe2, 0x96, 0x1d, 0x01, 0xe4, 0xde, 0xdd, 0x5a,
-	0xdd, 0x5a, 0x3d, 0x5f, 0x3e, 0x82, 0x4a, 0x90, 0xdf, 0x58, 0xbd, 0x72, 0xfe, 0xd2, 0x95, 0x0b,
-	0x65, 0x8d, 0x7c, 0x34, 0xb6, 0xae, 0x5c, 0x21, 0x1f, 0x99, 0x67, 0x2e, 0xcb, 0x6f, 0xfe, 0x78,
-	0x96, 0x39, 0x01, 0x85, 0x95, 0x7e, 0x9f, 0x86, 0x3b, 0xc6, 0xbb, 0xba, 0x67, 0x91, 0xc8, 0x54,
-	0xd6, 0x50, 0x1e, 0xb2, 0x57, 0xaf, 0xae, 0x97, 0x33, 0x68, 0x16, 0xca, 0xe7, 0xb1, 0x61, 0x76,
-	0x2d, 0x1b, 0x8b, 0x18, 0x5b, 0xce, 0xd6, 0x6f, 0x7c, 0xfd, 0xed, 0xa2, 0xf6, 0xcd, 0xb7, 0x8b,
-	0xda, 0x1f, 0xbf, 0x5d, 0xd4, 0x6e, 0x7d, 0xb7, 0x78, 0xe4, 0x9b, 0xef, 0x16, 0x8f, 0xfc, 0xee,
-	0xbb, 0xc5, 0x23, 0xef, 0x3f, 0x2f, 0xfd, 0xd4, 0x97, 0x8d, 0xa9, 0xef, 0x3a, 0xe4, 0x78, 0xe1,
-	0x5f, 0xcb, 0xf1, 0x1f, 0x37, 0x7f, 0x95, 0x39, 0xb9, 0x42, 0x3f, 0x37, 0x18, 0x5d, 0xed, 0x92,
-	0x53, 0x63, 0x00, 0xfa, 0xfb, 0x54, 0xaf, 0x95, 0xa3, 0xbf, 0x43, 0x7d, 0xf1, 0x1f, 0x01, 0x00,
-	0x00, 0xff, 0xff, 0x21, 0x0e, 0xd1, 0x62, 0x17, 0x3d, 0x00, 0x00,
+	// 3756 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe4, 0x5b, 0x4b, 0x6c, 0x1b, 0xd7,
+	0xd5, 0xf6, 0x90, 0x12, 0x1f, 0x87, 0x7a, 0xd0, 0xd7, 0x92, 0x4c, 0x2b, 0xb6, 0x28, 0x8f, 0xf3,
+	0xff, 0x71, 0x82, 0x84, 0x4c, 0x9c, 0x07, 0xf2, 0xf8, 0x91, 0x40, 0xb4, 0x15, 0xdb, 0x8a, 0x65,
+	0x2b, 0x94, 0x95, 0xdf, 0x0d, 0x52, 0x30, 0x43, 0xce, 0x15, 0x35, 0xd2, 0x70, 0x86, 0x99, 0x87,
+	0x2c, 0x01, 0x59, 0x34, 0x45, 0x9a, 0xee, 0x52, 0x17, 0xcd, 0xa2, 0x40, 0x17, 0x69, 0x97, 0x0d,
+	0xd0, 0x75, 0xbb, 0xed, 0x2e, 0x8b, 0xa2, 0x48, 0x77, 0x5d, 0x31, 0x45, 0x82, 0x6e, 0xb8, 0xe8,
+	0xba, 0xed, 0xa6, 0xc5, 0x7d, 0xcc, 0xcc, 0xbd, 0x33, 0x43, 0x5b, 0xb6, 0xec, 0x3a, 0x85, 0x57,
+	0x12, 0xbf, 0xf3, 0xba, 0x73, 0xef, 0xb9, 0x67, 0xce, 0x39, 0xf7, 0x0e, 0x9c, 0xea, 0xef, 0x74,
+	0xeb, 0x9a, 0xd3, 0xd3, 0x74, 0x0d, 0xef, 0x62, 0xcb, 0x73, 0xeb, 0xec, 0x4f, 0xad, 0xef, 0xd8,
+	0x9e, 0x8d, 0x26, 0x44, 0xd2, 0xbc, 0xba, 0xf3, 0xb2, 0x5b, 0x33, 0xec, 0xba, 0xd6, 0x37, 0xea,
+	0x1d, 0xdb, 0xc1, 0xf5, 0xdd, 0xe7, 0xea, 0x5d, 0x6c, 0x61, 0x47, 0xf3, 0xb0, 0xce, 0x24, 0xe6,
+	0xcf, 0x0a, 0x3c, 0x16, 0xf6, 0x6e, 0xda, 0xce, 0x8e, 0x61, 0x75, 0xd3, 0x38, 0xab, 0x5d, 0xdb,
+	0xee, 0x9a, 0xb8, 0x4e, 0x7f, 0xb5, 0xfd, 0xcd, 0xba, 0x67, 0xf4, 0xb0, 0xeb, 0x69, 0xbd, 0x3e,
+	0x67, 0x78, 0x21, 0x52, 0xd5, 0xd3, 0x3a, 0x5b, 0x86, 0x85, 0x9d, 0xfd, 0x3a, 0x1d, 0x6f, 0xdf,
+	0xa8, 0x3b, 0xd8, 0xb5, 0x7d, 0xa7, 0x83, 0x13, 0x6a, 0x9f, 0xe9, 0x1a, 0xde, 0x96, 0xdf, 0xae,
+	0x75, 0xec, 0x5e, 0xbd, 0x6b, 0x77, 0xed, 0x48, 0x3f, 0xf9, 0x45, 0x7f, 0xd0, 0xff, 0x38, 0xfb,
+	0xab, 0x86, 0xe5, 0x61, 0xc7, 0xd2, 0xcc, 0xba, 0xdb, 0xd9, 0xc2, 0xba, 0x6f, 0x62, 0x27, 0xfa,
+	0xcf, 0x6e, 0x6f, 0xe3, 0x8e, 0xe7, 0x26, 0x00, 0x26, 0xab, 0x7e, 0x3d, 0x07, 0x93, 0xcb, 0x64,
+	0x6a, 0xd6, 0xf1, 0x07, 0x3e, 0xb6, 0x3a, 0x18, 0x3d, 0x09, 0xe3, 0x1f, 0xf8, 0xd8, 0xc7, 0x15,
+	0x65, 0x51, 0x39, 0x5b, 0x6c, 0x1c, 0x1b, 0x0e, 0xaa, 0xd3, 0x14, 0x78, 0xda, 0xee, 0x19, 0x1e,
+	0xee, 0xf5, 0xbd, 0xfd, 0x26, 0xe3, 0x40, 0xaf, 0xc2, 0xc4, 0xb6, 0xdd, 0x6e, 0xb9, 0xd8, 0x6b,
+	0x59, 0x5a, 0x0f, 0x57, 0x32, 0x54, 0xa2, 0x32, 0x1c, 0x54, 0x67, 0xb6, 0xed, 0xf6, 0x3a, 0xf6,
+	0xae, 0x6a, 0x3d, 0x51, 0x0c, 0x22, 0x14, 0x3d, 0x03, 0x79, 0xdf, 0xc5, 0x4e, 0xcb, 0xd0, 0x2b,
+	0x59, 0x2a, 0x36, 0x33, 0x1c, 0x54, 0xcb, 0x04, 0xba, 0xac, 0x0b, 0x22, 0x39, 0x86, 0xa0, 0xa7,
+	0x21, 0xd7, 0x75, 0x6c, 0xbf, 0xef, 0x56, 0xc6, 0x16, 0xb3, 0x01, 0x37, 0x43, 0x44, 0x6e, 0x86,
+	0xa0, 0x6b, 0x90, 0x63, 0xeb, 0x5d, 0x19, 0x5f, 0xcc, 0x9e, 0x2d, 0x9d, 0x3b, 0x5d, 0x13, 0x9d,
+	0xa0, 0x26, 0x3d, 0x30, 0xfb, 0xc5, 0x14, 0x32, 0xba, 0xa8, 0x90, 0xbb, 0xcd, 0xef, 0x66, 0x60,
+	0x9c, 0xf2, 0xa1, 0x6b, 0x90, 0xef, 0x38, 0x98, 0x2c, 0x56, 0x05, 0x2d, 0x2a, 0x67, 0x4b, 0xe7,
+	0xe6, 0x6b, 0xcc, 0x09, 0x6a, 0xc1, 0x22, 0xd5, 0xae, 0x07, 0x4e, 0xd0, 0x38, 0x31, 0x1c, 0x54,
+	0x8f, 0x72, 0xf6, 0x48, 0xeb, 0xad, 0xaf, 0xab, 0x4a, 0x33, 0xd0, 0x82, 0xd6, 0xa0, 0xe8, 0xfa,
+	0xed, 0x9e, 0xe1, 0xad, 0xd8, 0x6d, 0x3a, 0xe7, 0xa5, 0x73, 0xc7, 0xe5, 0xe1, 0xae, 0x07, 0xe4,
+	0xc6, 0xf1, 0xe1, 0xa0, 0x7a, 0x2c, 0xe4, 0x8e, 0x34, 0x5e, 0x3a, 0xd2, 0x8c, 0x94, 0xa0, 0x2d,
+	0x98, 0x76, 0x70, 0xdf, 0x31, 0x6c, 0xc7, 0xf0, 0x0c, 0x17, 0x13, 0xbd, 0x19, 0xaa, 0xf7, 0x94,
+	0xac, 0xb7, 0x29, 0x33, 0x35, 0x4e, 0x0d, 0x07, 0xd5, 0x13, 0x31, 0x49, 0xc9, 0x46, 0x5c, 0x2d,
+	0xf2, 0x00, 0xc5, 0xa0, 0x75, 0xec, 0xd1, 0xf5, 0x2c, 0x9d, 0x5b, 0xbc, 0xad, 0xb1, 0x75, 0xec,
+	0x35, 0x16, 0x87, 0x83, 0xea, 0xc9, 0xa4, 0xbc, 0x64, 0x32, 0x45, 0x3f, 0x32, 0xa1, 0x2c, 0xa2,
+	0x3a, 0x79, 0xc0, 0x31, 0x6a, 0x73, 0x61, 0xb4, 0x4d, 0xc2, 0xd5, 0x58, 0x18, 0x0e, 0xaa, 0xf3,
+	0x71, 0x59, 0xc9, 0x5e, 0x42, 0x33, 0x59, 0x9f, 0x8e, 0x66, 0x75, 0xb0, 0x49, 0xcc, 0x8c, 0xa7,
+	0xad, 0xcf, 0xf9, 0x80, 0xcc, 0xd6, 0x27, 0xe4, 0x96, 0xd7, 0x27, 0x84, 0xd1, 0x7b, 0x30, 0x11,
+	0xfe, 0x20, 0xf3, 0x95, 0xe3, 0x7e, 0x94, 0xae, 0x94, 0xcc, 0xd4, 0xfc, 0x70, 0x50, 0x9d, 0x13,
+	0x65, 0x24, 0xd5, 0x92, 0xb6, 0x48, 0xbb, 0xc9, 0x66, 0x26, 0x3f, 0x5a, 0x3b, 0xe3, 0x10, 0xb5,
+	0x9b, 0xc9, 0x19, 0x91, 0xb4, 0x11, 0xed, 0x64, 0x13, 0xfb, 0x9d, 0x0e, 0xc6, 0x3a, 0xd6, 0x2b,
+	0x85, 0x34, 0xed, 0x2b, 0x02, 0x07, 0xd3, 0x2e, 0xca, 0xc8, 0xda, 0x45, 0x0a, 0x99, 0xeb, 0x6d,
+	0xbb, 0xbd, 0xec, 0x38, 0xb6, 0xe3, 0x56, 0x8a, 0x69, 0x73, 0xbd, 0x12, 0x90, 0xd9, 0x5c, 0x87,
+	0xdc, 0xf2, 0x5c, 0x87, 0x30, 0x1f, 0x6f, 0xd3, 0xb7, 0xae, 0x60, 0xcd, 0xc5, 0x7a, 0x05, 0x46,
+	0x8c, 0x37, 0xe4, 0x08, 0xc7, 0x1b, 0x22, 0x89, 0xf1, 0x86, 0x14, 0xa4, 0xc3, 0x14, 0xfb, 0xbd,
+	0xe4, 0xba, 0x46, 0xd7, 0xc2, 0x7a, 0xa5, 0x44, 0xf5, 0x9f, 0x4c, 0xd3, 0x1f, 0xf0, 0x34, 0x4e,
+	0x0e, 0x07, 0xd5, 0x8a, 0x2c, 0x27, 0xd9, 0x88, 0xe9, 0x44, 0xef, 0xc3, 0x24, 0x43, 0x9a, 0xbe,
+	0x65, 0x19, 0x56, 0xb7, 0x32, 0x41, 0x8d, 0x3c, 0x96, 0x66, 0x84, 0xb3, 0x34, 0x1e, 0x1b, 0x0e,
+	0xaa, 0xc7, 0x25, 0x29, 0xc9, 0x84, 0xac, 0x90, 0x44, 0x0c, 0x06, 0x44, 0x0b, 0x3b, 0x99, 0x16,
+	0x31, 0x56, 0x64, 0x26, 0x16, 0x31, 0x62, 0x92, 0x72, 0xc4, 0x88, 0x11, 0xa3, 0xf5, 0xe0, 0x8b,
+	0x3c, 0x35, 0x7a, 0x3d, 0xf8, 0x3a, 0x0b, 0xeb, 0x91, 0xb2, 0xd4, 0x92, 0x36, 0xf4, 0x21, 0x90,
+	0x17, 0xcf, 0x05, 0xbf, 0x6f, 0x1a, 0x1d, 0xcd, 0xc3, 0x17, 0xb0, 0x87, 0x3b, 0x24, 0x52, 0x4f,
+	0x53, 0x2b, 0x6a, 0xc2, 0x4a, 0x82, 0xb3, 0xa1, 0x0e, 0x07, 0xd5, 0x85, 0x34, 0x1d, 0x92, 0xd5,
+	0x54, 0x2b, 0xe8, 0x07, 0x0a, 0xcc, 0xba, 0x9e, 0x66, 0xe9, 0x9a, 0x69, 0x5b, 0xf8, 0xb2, 0xd5,
+	0x75, 0xb0, 0xeb, 0x5e, 0xb6, 0x36, 0xed, 0x4a, 0x99, 0xda, 0x3f, 0x13, 0x0b, 0xeb, 0x69, 0xac,
+	0x8d, 0x33, 0xc3, 0x41, 0xb5, 0x9a, 0xaa, 0x45, 0x1a, 0x41, 0xba, 0x21, 0xb4, 0x07, 0xc7, 0x82,
+	0xac, 0x62, 0xc3, 0x33, 0x4c, 0xc3, 0xd5, 0x3c, 0xc3, 0xb6, 0x2a, 0x47, 0xa9, 0xfd, 0xd3, 0xf1,
+	0xe8, 0x98, 0x60, 0x6c, 0x9c, 0x1e, 0x0e, 0xaa, 0xa7, 0x52, 0x34, 0x48, 0xb6, 0xd3, 0x4c, 0x44,
+	0x2e, 0xb4, 0xe6, 0x60, 0xc2, 0x88, 0xf5, 0xca, 0xb1, 0xd1, 0x2e, 0x14, 0x32, 0x89, 0x2e, 0x14,
+	0x82, 0x69, 0x2e, 0x14, 0x12, 0x89, 0xa5, 0xbe, 0xe6, 0x78, 0x06, 0x31, 0xbb, 0xaa, 0x39, 0x3b,
+	0xd8, 0xa9, 0xcc, 0xa4, 0x59, 0x5a, 0x93, 0x99, 0x98, 0xa5, 0x98, 0xa4, 0x6c, 0x29, 0x46, 0x44,
+	0xb7, 0x14, 0x90, 0x87, 0x66, 0xd8, 0x56, 0x93, 0xa4, 0x0d, 0x2e, 0x79, 0xbc, 0x59, 0x6a, 0xf4,
+	0x89, 0xdb, 0x3c, 0x9e, 0xc8, 0xde, 0x78, 0x62, 0x38, 0xa8, 0x9e, 0x19, 0xa9, 0x4d, 0x1a, 0xc8,
+	0x68, 0xa3, 0xe8, 0x06, 0x94, 0x08, 0x11, 0xd3, 0x04, 0x4c, 0xaf, 0xcc, 0xd1, 0x31, 0x9c, 0x48,
+	0x8e, 0x81, 0x33, 0xd0, 0x0c, 0x64, 0x56, 0x90, 0x90, 0xec, 0x88, 0xaa, 0xd0, 0xc7, 0x0a, 0x90,
+	0x6d, 0x96, 0xf6, 0xa4, 0xc7, 0xa9, 0x95, 0xc7, 0x13, 0x56, 0xd2, 0x1e, 0xf3, 0xf1, 0xe1, 0xa0,
+	0xba, 0x98, 0xae, 0x47, 0xb2, 0x3d, 0xc2, 0x56, 0xe4, 0x47, 0xe1, 0x2b, 0xaa, 0x52, 0x19, 0xed,
+	0x47, 0x21, 0x93, 0xe8, 0x47, 0x21, 0x98, 0xe6, 0x47, 0x21, 0x91, 0x87, 0xa2, 0x77, 0x34, 0xd3,
+	0xd0, 0x69, 0x3a, 0x77, 0x62, 0x44, 0x28, 0x0a, 0x39, 0xc2, 0x50, 0x14, 0x22, 0x89, 0x50, 0x14,
+	0xf1, 0xe6, 0x61, 0x9c, 0xaa, 0x50, 0x87, 0x39, 0x38, 0x96, 0xb2, 0xd5, 0xd0, 0xeb, 0x90, 0x73,
+	0x7c, 0x8b, 0xe4, 0xbf, 0x2c, 0xe9, 0x43, 0xb2, 0xe1, 0x0d, 0xdf, 0xd0, 0x59, 0xf2, 0xed, 0xf8,
+	0x96, 0x94, 0x12, 0x8f, 0x53, 0x80, 0xc8, 0x93, 0xe4, 0xdb, 0xd0, 0x79, 0x72, 0x37, 0x52, 0x7e,
+	0xdb, 0x6e, 0xcb, 0xf2, 0x14, 0x40, 0x18, 0x26, 0x83, 0x7d, 0xdc, 0x32, 0x48, 0x90, 0xca, 0xa6,
+	0xad, 0xf2, 0x5b, 0x7e, 0x1b, 0x3b, 0x16, 0xf6, 0xb0, 0x1b, 0x3c, 0x03, 0x8d, 0x52, 0x74, 0x26,
+	0x1c, 0x01, 0x11, 0xf4, 0x4f, 0x88, 0x38, 0xfa, 0x4c, 0x81, 0x4a, 0x4f, 0xdb, 0x6b, 0x05, 0xa0,
+	0xdb, 0xda, 0xb4, 0x9d, 0x56, 0x1f, 0x3b, 0x86, 0xad, 0xd3, 0x5c, 0xbe, 0x74, 0xee, 0xff, 0xee,
+	0x18, 0x97, 0x6a, 0xab, 0xda, 0x5e, 0x00, 0xbb, 0x6f, 0xda, 0xce, 0x1a, 0x15, 0x5f, 0xb6, 0x3c,
+	0x67, 0xbf, 0x71, 0xea, 0xcb, 0x41, 0xf5, 0x08, 0xf1, 0xf2, 0x5e, 0x1a, 0x4f, 0x33, 0x1d, 0x46,
+	0x3f, 0x51, 0x60, 0xce, 0xb3, 0x3d, 0xcd, 0x6c, 0x75, 0xfc, 0x9e, 0x6f, 0x6a, 0x9e, 0xb1, 0x8b,
+	0x5b, 0xbe, 0xab, 0x75, 0x31, 0x2f, 0x19, 0x5e, 0xbb, 0xf3, 0xa0, 0xae, 0x13, 0xf9, 0xf3, 0xa1,
+	0xf8, 0x06, 0x91, 0x66, 0x63, 0x3a, 0xc9, 0xc7, 0x34, 0xe3, 0xa5, 0xb0, 0x34, 0x53, 0xd1, 0xf9,
+	0x5f, 0x2a, 0x30, 0x3f, 0xfa, 0x31, 0xd1, 0x19, 0xc8, 0xee, 0xe0, 0x7d, 0x5e, 0x94, 0x1d, 0x1d,
+	0x0e, 0xaa, 0x93, 0x3b, 0x78, 0x5f, 0x98, 0x75, 0x42, 0x45, 0xdf, 0x83, 0xf1, 0x5d, 0xcd, 0xf4,
+	0x31, 0x77, 0x89, 0x5a, 0x8d, 0x95, 0x9f, 0x35, 0xb1, 0xfc, 0xac, 0xf5, 0x77, 0xba, 0x04, 0xa8,
+	0x05, 0x2b, 0x52, 0x7b, 0xdb, 0xd7, 0x2c, 0xcf, 0xf0, 0xf6, 0x99, 0xbb, 0x50, 0x05, 0xa2, 0xbb,
+	0x50, 0xe0, 0xd5, 0xcc, 0xcb, 0xca, 0xfc, 0xe7, 0x0a, 0x9c, 0x18, 0xf9, 0xd0, 0xdf, 0x85, 0x11,
+	0xaa, 0x2d, 0x18, 0x23, 0x8e, 0x4f, 0xca, 0xc5, 0x2d, 0xa3, 0xbb, 0xf5, 0xd2, 0x0b, 0x74, 0x38,
+	0x39, 0x56, 0xdd, 0x31, 0x44, 0xac, 0xee, 0x18, 0x42, 0x4a, 0x5e, 0xd3, 0xbe, 0xf9, 0xd2, 0x0b,
+	0x74, 0x50, 0x39, 0x66, 0x84, 0x02, 0xa2, 0x11, 0x0a, 0xa8, 0xff, 0xca, 0x41, 0x31, 0xac, 0xc7,
+	0x84, 0x3d, 0xa8, 0xdc, 0xd3, 0x1e, 0xbc, 0x04, 0x65, 0x1d, 0xeb, 0x3c, 0x91, 0x30, 0x6c, 0x2b,
+	0xd8, 0xcd, 0x45, 0x16, 0xce, 0x24, 0x9a, 0x24, 0x3f, 0x1d, 0x23, 0xa1, 0x73, 0x50, 0xe0, 0x75,
+	0xcb, 0x3e, 0xdd, 0xc8, 0x93, 0x8d, 0xb9, 0xe1, 0xa0, 0x8a, 0x02, 0x4c, 0x10, 0x0d, 0xf9, 0x50,
+	0x13, 0x80, 0x35, 0x03, 0x56, 0xb1, 0xa7, 0xf1, 0x0a, 0xaa, 0x22, 0x3f, 0xc1, 0xb5, 0x90, 0xce,
+	0xca, 0xfa, 0x88, 0x5f, 0x2c, 0xeb, 0x23, 0x14, 0xbd, 0x07, 0xd0, 0xd3, 0x0c, 0x8b, 0xc9, 0xf1,
+	0x72, 0x49, 0x1d, 0x15, 0x52, 0x56, 0x43, 0x4e, 0xa6, 0x3d, 0x92, 0x14, 0xb5, 0x47, 0x28, 0x29,
+	0xbe, 0x79, 0xfb, 0xa2, 0x92, 0xa3, 0xbb, 0x74, 0x61, 0x94, 0x6a, 0xae, 0x76, 0x96, 0x14, 0xe0,
+	0x5c, 0x44, 0xd0, 0x19, 0x68, 0x21, 0xd3, 0x66, 0x1a, 0x9b, 0xd8, 0x33, 0x7a, 0x98, 0x16, 0x4a,
+	0x7c, 0xda, 0x02, 0x4c, 0x9c, 0xb6, 0x00, 0x43, 0x2f, 0x03, 0x68, 0xde, 0xaa, 0xed, 0x7a, 0xd7,
+	0xac, 0x0e, 0xa6, 0x05, 0x50, 0x81, 0x0d, 0x3f, 0x42, 0xc5, 0xe1, 0x47, 0x28, 0x7a, 0x0d, 0x4a,
+	0x7d, 0xfe, 0xca, 0x6b, 0x9b, 0x98, 0x16, 0x38, 0x05, 0xf6, 0x86, 0x16, 0x60, 0x41, 0x56, 0xe4,
+	0x46, 0x17, 0x61, 0xba, 0x63, 0x5b, 0x1d, 0xdf, 0x71, 0xb0, 0xd5, 0xd9, 0x5f, 0xd7, 0x36, 0x31,
+	0x2d, 0x66, 0x0a, 0xcc, 0x55, 0x62, 0x24, 0xd1, 0x55, 0x62, 0x24, 0xf4, 0x22, 0x14, 0xc3, 0x66,
+	0x10, 0xad, 0x57, 0x8a, 0xbc, 0xaf, 0x10, 0x80, 0x82, 0x70, 0xc4, 0x49, 0x06, 0x6f, 0xb8, 0x61,
+	0xd2, 0x4b, 0x6b, 0x10, 0x3e, 0x78, 0x01, 0x16, 0x07, 0x2f, 0xc0, 0xe8, 0x32, 0x1c, 0xa5, 0x69,
+	0x46, 0xcb, 0xf3, 0xcc, 0x96, 0x8b, 0x3b, 0xb6, 0xa5, 0xbb, 0xb4, 0xc4, 0xc8, 0xb2, 0xe1, 0x53,
+	0xe2, 0x75, 0xcf, 0x5c, 0x67, 0x24, 0x71, 0xf8, 0x31, 0x92, 0xfa, 0x07, 0x05, 0x66, 0xd2, 0x5c,
+	0x28, 0xe6, 0xce, 0xca, 0x7d, 0x71, 0xe7, 0x77, 0xa0, 0xd0, 0xb7, 0xf5, 0x96, 0xdb, 0xc7, 0x1d,
+	0x1e, 0xb1, 0x62, 0xce, 0xbc, 0x66, 0xeb, 0xeb, 0x7d, 0xdc, 0xf9, 0x7f, 0xc3, 0xdb, 0x5a, 0xda,
+	0xb5, 0x0d, 0xfd, 0x8a, 0xe1, 0x72, 0xaf, 0xeb, 0x33, 0x8a, 0x94, 0x22, 0xe4, 0x39, 0xd8, 0x28,
+	0x40, 0x8e, 0x59, 0x51, 0xff, 0x98, 0x85, 0x72, 0xdc, 0x6d, 0xff, 0x9b, 0x1e, 0x05, 0xdd, 0x80,
+	0xbc, 0xc1, 0x2a, 0x10, 0x9e, 0x41, 0xfc, 0x8f, 0x10, 0xd3, 0x6b, 0x51, 0xff, 0xb4, 0xb6, 0xfb,
+	0x5c, 0x8d, 0x97, 0x2a, 0x74, 0x0a, 0xa8, 0x66, 0x2e, 0x29, 0x6b, 0xe6, 0x20, 0x6a, 0x42, 0xde,
+	0xc5, 0xce, 0xae, 0xd1, 0xc1, 0x3c, 0x38, 0x55, 0x45, 0xcd, 0x1d, 0xdb, 0xc1, 0x44, 0xe7, 0x3a,
+	0x63, 0x89, 0x74, 0x72, 0x19, 0x59, 0x27, 0x07, 0xd1, 0x3b, 0x50, 0xec, 0xd8, 0xd6, 0xa6, 0xd1,
+	0x5d, 0xd5, 0xfa, 0x3c, 0x3c, 0x9d, 0x4a, 0xd3, 0x7a, 0x3e, 0x60, 0xe2, 0x3d, 0x9d, 0xe0, 0x67,
+	0xac, 0xa7, 0x13, 0x72, 0x45, 0x0b, 0xfa, 0xb7, 0x31, 0x80, 0x68, 0x71, 0xd0, 0x2b, 0x50, 0xc2,
+	0x7b, 0xb8, 0xe3, 0x7b, 0xb6, 0x13, 0xbc, 0x27, 0x78, 0x8b, 0x34, 0x80, 0xa5, 0xc0, 0x0e, 0x11,
+	0x4a, 0x36, 0xaa, 0xa5, 0xf5, 0xb0, 0xdb, 0xd7, 0x3a, 0x41, 0x6f, 0x95, 0x0e, 0x26, 0x04, 0xc5,
+	0x8d, 0x1a, 0x82, 0xe8, 0x7f, 0x61, 0x8c, 0x76, 0x63, 0x59, 0x5b, 0x15, 0x0d, 0x07, 0xd5, 0x29,
+	0x4b, 0xee, 0xc3, 0x52, 0x3a, 0x7a, 0x03, 0x26, 0x77, 0x42, 0xc7, 0x23, 0x63, 0x1b, 0xa3, 0x02,
+	0x34, 0xb5, 0x8b, 0x08, 0xd2, 0xe8, 0x26, 0x44, 0x1c, 0x6d, 0x42, 0x49, 0xb3, 0x2c, 0xdb, 0xa3,
+	0xef, 0xa0, 0xa0, 0xd5, 0xfa, 0xe4, 0x28, 0x37, 0xad, 0x2d, 0x45, 0xbc, 0x2c, 0x4b, 0xa2, 0xc1,
+	0x43, 0xd0, 0x20, 0x06, 0x0f, 0x01, 0x46, 0x4d, 0xc8, 0x99, 0x5a, 0x1b, 0x9b, 0x41, 0xd0, 0x7f,
+	0x7c, 0xa4, 0x89, 0x2b, 0x94, 0x8d, 0x69, 0xa7, 0xaf, 0x7c, 0x26, 0x27, 0xbe, 0xf2, 0x19, 0x32,
+	0xbf, 0x09, 0xe5, 0xf8, 0x78, 0x0e, 0x96, 0xc0, 0x3c, 0x29, 0x26, 0x30, 0xc5, 0x3b, 0xa6, 0x4c,
+	0x1a, 0x94, 0x84, 0x41, 0x3d, 0x08, 0x13, 0xea, 0xaf, 0x15, 0x98, 0x49, 0xdb, 0xbb, 0x68, 0x55,
+	0xd8, 0xf1, 0x0a, 0x6f, 0x19, 0xa5, 0xb8, 0x3a, 0x97, 0x1d, 0xb1, 0xd5, 0xa3, 0x8d, 0xde, 0x80,
+	0x29, 0xcb, 0xd6, 0x71, 0x4b, 0x23, 0x06, 0x4c, 0xc3, 0xf5, 0x2a, 0x19, 0xda, 0x8a, 0xa7, 0xad,
+	0x26, 0x42, 0x59, 0x0a, 0x08, 0x82, 0xf4, 0xa4, 0x44, 0x50, 0x7f, 0xa4, 0xc0, 0x74, 0xac, 0x13,
+	0x7c, 0xe8, 0x24, 0x4a, 0x4c, 0x7d, 0x32, 0x07, 0x4b, 0x7d, 0xd4, 0x9f, 0x65, 0xa0, 0x24, 0x94,
+	0xc9, 0x87, 0x1e, 0xc3, 0x36, 0x4c, 0xf3, 0x37, 0xa5, 0x61, 0x75, 0x59, 0x39, 0x95, 0xe1, 0x3d,
+	0x9f, 0xc4, 0xc1, 0xcb, 0x8a, 0xdd, 0x5e, 0x0f, 0x79, 0x69, 0x35, 0x45, 0x1b, 0x82, 0xae, 0x84,
+	0x09, 0x26, 0xa6, 0x64, 0x0a, 0xba, 0x01, 0x73, 0x7e, 0x9f, 0x14, 0x99, 0x2d, 0x97, 0x1f, 0x61,
+	0xb4, 0x2c, 0xbf, 0xd7, 0xc6, 0x0e, 0xdd, 0xf1, 0xe3, 0xac, 0x85, 0xc5, 0x38, 0x82, 0x33, 0x8e,
+	0xab, 0x94, 0x2e, 0xe8, 0x9c, 0x49, 0xa3, 0xab, 0x97, 0x00, 0x25, 0xdb, 0xf4, 0xd2, 0xfc, 0x2a,
+	0x07, 0x9c, 0xdf, 0x4f, 0x14, 0x28, 0xc7, 0xbb, 0xef, 0x0f, 0x65, 0xa1, 0xf7, 0xa1, 0x18, 0x76,
+	0xd2, 0x0f, 0x3d, 0x80, 0xa7, 0x21, 0xe7, 0x60, 0xcd, 0xb5, 0x2d, 0xbe, 0x33, 0x69, 0x88, 0x61,
+	0x88, 0x18, 0x62, 0x18, 0xa2, 0x5e, 0x87, 0x09, 0x36, 0x83, 0x6f, 0x1a, 0xa6, 0x87, 0x1d, 0x74,
+	0x01, 0x72, 0xae, 0xa7, 0x79, 0xd8, 0xad, 0x28, 0x8b, 0xd9, 0xb3, 0x53, 0xe7, 0xe6, 0x92, 0x4d,
+	0x73, 0x42, 0x66, 0x5a, 0x19, 0xa7, 0xa8, 0x95, 0x21, 0xea, 0x0f, 0x15, 0x98, 0x10, 0xcf, 0x06,
+	0xee, 0x8f, 0xda, 0xbb, 0x7c, 0xb4, 0x0f, 0x83, 0x31, 0x98, 0xf7, 0x67, 0x65, 0xef, 0xce, 0xfa,
+	0x6f, 0x15, 0x36, 0xb3, 0x61, 0x53, 0xf9, 0xb0, 0xe6, 0xbb, 0x51, 0x2b, 0x84, 0xec, 0x30, 0x97,
+	0x06, 0xb6, 0x83, 0xb6, 0x42, 0x68, 0xf8, 0x93, 0xc4, 0xc5, 0xf0, 0x27, 0x11, 0xd4, 0xcf, 0xc6,
+	0xe9, 0xc8, 0xa3, 0x03, 0x84, 0x87, 0xdd, 0x04, 0x8a, 0x65, 0x27, 0xd9, 0xbb, 0xc8, 0x4e, 0x9e,
+	0x81, 0x3c, 0x7d, 0x1d, 0x84, 0x89, 0x03, 0x5d, 0x34, 0x02, 0xc9, 0x07, 0xb8, 0x0c, 0xb9, 0x4d,
+	0xd4, 0x1a, 0x3f, 0x5c, 0xd4, 0x42, 0x2d, 0x38, 0xb1, 0xa5, 0xb9, 0xad, 0x20, 0xce, 0xea, 0x2d,
+	0xcd, 0x6b, 0x85, 0x71, 0x22, 0x47, 0xcb, 0x14, 0xda, 0x94, 0xdc, 0xd2, 0xdc, 0xf5, 0x80, 0x67,
+	0xc9, 0x5b, 0x4b, 0x46, 0x8d, 0xb9, 0x74, 0x0e, 0xb4, 0x01, 0xb3, 0xe9, 0xca, 0xf3, 0x74, 0xe4,
+	0xb4, 0x67, 0xee, 0xde, 0x56, 0xf3, 0xb1, 0x14, 0x32, 0xfa, 0x48, 0x81, 0x0a, 0x79, 0x3f, 0x3b,
+	0xf8, 0x03, 0xdf, 0x70, 0x70, 0x8f, 0xac, 0x58, 0xcb, 0xde, 0xc5, 0x8e, 0xa9, 0xed, 0xf3, 0xc3,
+	0xaf, 0xd3, 0xc9, 0xb7, 0xc7, 0x9a, 0xad, 0x37, 0x05, 0x01, 0xf6, 0x68, 0x7d, 0x19, 0xbc, 0xc6,
+	0x94, 0x88, 0x8f, 0x96, 0xce, 0xb1, 0x32, 0x56, 0x28, 0x94, 0x8b, 0xea, 0x3f, 0x14, 0x98, 0x92,
+	0xcf, 0xa8, 0x1e, 0xba, 0x63, 0x26, 0xb6, 0x64, 0xf6, 0x01, 0x6d, 0xc9, 0xbf, 0x2b, 0x30, 0x29,
+	0x1d, 0x9d, 0x3d, 0x3a, 0x8f, 0xfe, 0xf3, 0x0c, 0xcc, 0xa5, 0xab, 0x79, 0x20, 0x05, 0xe8, 0x25,
+	0x20, 0xa9, 0xe4, 0xe5, 0x28, 0x37, 0x9a, 0x4d, 0xd4, 0x9f, 0xf4, 0x11, 0x82, 0x3c, 0x34, 0x71,
+	0xe6, 0x15, 0x88, 0xa3, 0x1b, 0x50, 0x32, 0x84, 0xd3, 0xb5, 0x6c, 0xda, 0x21, 0x88, 0x78, 0xa6,
+	0xc6, 0xba, 0x14, 0x23, 0x4e, 0xd2, 0x44, 0x55, 0x8d, 0x1c, 0x8c, 0x91, 0xe4, 0x4d, 0xdd, 0x85,
+	0x3c, 0x1f, 0x0e, 0x7a, 0x1e, 0x8a, 0x34, 0xce, 0xd1, 0x9a, 0x8a, 0x25, 0xee, 0x34, 0xed, 0x20,
+	0x60, 0xec, 0x7e, 0x4b, 0x21, 0xc0, 0xd0, 0x4b, 0x00, 0x64, 0x6b, 0xf3, 0x08, 0x97, 0xa1, 0x71,
+	0x82, 0xd6, 0x6e, 0x7d, 0x5b, 0x4f, 0x84, 0xb5, 0x62, 0x08, 0xaa, 0xbf, 0xc9, 0x40, 0x49, 0x3c,
+	0xcf, 0xbb, 0x27, 0xe3, 0x1f, 0x42, 0x50, 0x57, 0xb7, 0x34, 0x5d, 0x27, 0x7f, 0x71, 0xf0, 0x4a,
+	0xab, 0x8f, 0x9c, 0xa4, 0xe0, 0xff, 0xa5, 0x40, 0x82, 0x55, 0x51, 0xf4, 0xc6, 0x84, 0x11, 0x23,
+	0x09, 0x56, 0xcb, 0x71, 0xda, 0xfc, 0x0e, 0xcc, 0xa6, 0xaa, 0x12, 0x6b, 0x9f, 0xf1, 0xfb, 0x55,
+	0xfb, 0xfc, 0x7e, 0x1c, 0x66, 0x53, 0xcf, 0x51, 0x1f, 0xfa, 0x2e, 0x96, 0x77, 0x50, 0xf6, 0xbe,
+	0xec, 0xa0, 0x4f, 0x94, 0xb4, 0x95, 0x65, 0x87, 0x28, 0xaf, 0x1c, 0xe0, 0x70, 0xf9, 0x7e, 0xad,
+	0xb1, 0xec, 0x96, 0xe3, 0xf7, 0xb4, 0x27, 0x72, 0x07, 0xdd, 0x13, 0xe8, 0x59, 0x56, 0xc6, 0x52,
+	0x5b, 0x79, 0x6a, 0x2b, 0x88, 0x10, 0x31, 0x53, 0x79, 0x0e, 0xa1, 0x37, 0x60, 0x32, 0x90, 0x60,
+	0xcd, 0x93, 0x42, 0xd4, 0xd9, 0xe0, 0x3c, 0xf1, 0xfe, 0xc9, 0x84, 0x88, 0xff, 0x67, 0x7d, 0xf8,
+	0x9f, 0x0a, 0x4c, 0xc7, 0x2e, 0x56, 0x3c, 0x3a, 0xef, 0xa0, 0x4f, 0x15, 0x28, 0x86, 0x77, 0x7a,
+	0x0e, 0x9d, 0xc8, 0x2f, 0x41, 0x0e, 0xb3, 0x7b, 0x25, 0x2c, 0xdc, 0x1d, 0x8b, 0xdd, 0xfb, 0x23,
+	0x34, 0x7e, 0xd3, 0x2f, 0x76, 0x95, 0xa4, 0xc9, 0x05, 0xd5, 0x3f, 0x29, 0x41, 0x8a, 0x1e, 0x8d,
+	0xe9, 0xa1, 0x2e, 0x45, 0xf4, 0x4c, 0xd9, 0x7b, 0x7d, 0xa6, 0x5f, 0x01, 0x8c, 0x53, 0x3e, 0x52,
+	0x42, 0x7b, 0xd8, 0xe9, 0x19, 0x96, 0x66, 0xd2, 0xc7, 0x29, 0xb0, 0x7d, 0x1b, 0x60, 0xe2, 0xbe,
+	0x0d, 0x30, 0xb4, 0x05, 0xd3, 0x51, 0xdb, 0x8f, 0xaa, 0x49, 0xbf, 0x4e, 0xf8, 0x96, 0xcc, 0xc4,
+	0x1a, 0xfb, 0x31, 0x49, 0xf9, 0x44, 0x3e, 0x46, 0x44, 0x3a, 0x4c, 0x75, 0x6c, 0xcb, 0xd3, 0x0c,
+	0x0b, 0x3b, 0xcc, 0x50, 0x36, 0xed, 0x3a, 0xd5, 0x79, 0x89, 0x87, 0x75, 0x4f, 0x64, 0x39, 0xf9,
+	0x3a, 0x95, 0x4c, 0x43, 0xef, 0xc3, 0x64, 0x50, 0xc6, 0x30, 0x23, 0x63, 0x69, 0xd7, 0xa9, 0x96,
+	0x45, 0x16, 0xe6, 0xd2, 0x92, 0x94, 0x7c, 0x9d, 0x4a, 0x22, 0x21, 0x13, 0xca, 0x7d, 0x5b, 0xdf,
+	0xb0, 0x78, 0xf2, 0xae, 0xb5, 0x4d, 0xcc, 0x7b, 0xcd, 0x0b, 0x89, 0x94, 0x47, 0xe2, 0x62, 0xa1,
+	0x38, 0x2e, 0x2b, 0x5f, 0x50, 0x8c, 0x53, 0xd1, 0x7b, 0x30, 0x61, 0x92, 0x6a, 0x72, 0x79, 0xaf,
+	0x6f, 0x38, 0x58, 0x4f, 0xbf, 0x4e, 0x78, 0x45, 0xe0, 0x60, 0x81, 0x50, 0x94, 0x91, 0xef, 0x31,
+	0x88, 0x14, 0xb2, 0xfa, 0x3d, 0x6d, 0xaf, 0xe9, 0x5b, 0xee, 0xf2, 0x1e, 0xbf, 0x1a, 0x96, 0x4f,
+	0x5b, 0xfd, 0x55, 0x99, 0x89, 0xad, 0x7e, 0x4c, 0x52, 0x5e, 0xfd, 0x18, 0x11, 0x5d, 0xa1, 0x71,
+	0x9e, 0x2d, 0x09, 0xbb, 0x56, 0x38, 0x97, 0x98, 0x2d, 0xb6, 0x1a, 0xac, 0xed, 0xc3, 0x7f, 0x49,
+	0x4a, 0x43, 0x0d, 0x7c, 0x0d, 0xe8, 0x63, 0x37, 0xb1, 0xe7, 0x3b, 0x16, 0xd6, 0x79, 0x51, 0x95,
+	0x5c, 0x03, 0x89, 0x2b, 0x5c, 0x03, 0x09, 0x4d, 0xac, 0x81, 0x44, 0x25, 0x3e, 0xd5, 0xb7, 0xf5,
+	0xeb, 0x6c, 0xcb, 0x78, 0xe1, 0x3d, 0xc3, 0xc7, 0x12, 0xa6, 0x22, 0x16, 0xe6, 0x53, 0x92, 0x94,
+	0xec, 0x53, 0x12, 0x89, 0x5f, 0x6d, 0x13, 0x2f, 0x42, 0xb1, 0x99, 0x2a, 0x8d, 0xb8, 0xda, 0x96,
+	0xe0, 0x0c, 0xaf, 0xb6, 0x25, 0x28, 0x89, 0xab, 0x6d, 0x09, 0x0e, 0x62, 0xbd, 0xab, 0x59, 0xdd,
+	0x15, 0xbb, 0x2d, 0x7b, 0xf5, 0x44, 0x9a, 0xf5, 0x8b, 0x29, 0x9c, 0xcc, 0x7a, 0x9a, 0x0e, 0xd9,
+	0x7a, 0x1a, 0x47, 0x78, 0xe9, 0x69, 0x9b, 0xdd, 0xe6, 0x9b, 0x1c, 0x79, 0xe9, 0x89, 0x31, 0x08,
+	0x97, 0x9e, 0xb6, 0x93, 0x77, 0xf7, 0x44, 0x55, 0x8d, 0x42, 0xd0, 0x78, 0x52, 0x3f, 0x57, 0x60,
+	0x3a, 0x16, 0xc1, 0xd0, 0xeb, 0x10, 0xde, 0x65, 0xb9, 0xbe, 0xdf, 0x0f, 0x12, 0x70, 0xe9, 0xee,
+	0x0b, 0xc1, 0xd3, 0xee, 0xbe, 0x10, 0x1c, 0x5d, 0x01, 0x08, 0xdf, 0x76, 0xb7, 0x0b, 0xff, 0x34,
+	0xfb, 0x8b, 0x38, 0xc5, 0xec, 0x2f, 0x42, 0xd5, 0x5b, 0x63, 0x50, 0x08, 0xb6, 0xc0, 0x03, 0x29,
+	0xd0, 0xea, 0x90, 0xef, 0x61, 0x97, 0xde, 0x81, 0xc9, 0x44, 0x79, 0x16, 0x87, 0xc4, 0x3c, 0x8b,
+	0x43, 0x72, 0x1a, 0x98, 0xbd, 0xa7, 0x34, 0x70, 0xec, 0xc0, 0x69, 0x20, 0xa6, 0xe7, 0xdf, 0x42,
+	0x20, 0x0f, 0x4e, 0x9c, 0x6e, 0xff, 0x76, 0x08, 0x4e, 0xc7, 0x45, 0xc1, 0xd8, 0xe9, 0xb8, 0x48,
+	0x42, 0x3b, 0x70, 0x54, 0x38, 0x15, 0xe3, 0x5d, 0x49, 0x12, 0x52, 0xa7, 0x46, 0x5f, 0x36, 0x68,
+	0x52, 0x2e, 0x16, 0x38, 0x76, 0x62, 0xa8, 0x98, 0x47, 0xc7, 0x69, 0xc4, 0xc1, 0x74, 0xdc, 0xf6,
+	0xbb, 0xab, 0x7c, 0xda, 0xf3, 0x91, 0x83, 0x89, 0xb8, 0xe8, 0x60, 0x22, 0xae, 0xfe, 0x35, 0x03,
+	0x53, 0xf2, 0xf3, 0x3e, 0x10, 0xc7, 0x78, 0x1e, 0x8a, 0x78, 0xcf, 0xf0, 0x5a, 0x1d, 0x5b, 0xc7,
+	0xbc, 0x98, 0xa5, 0xeb, 0x4c, 0xc0, 0xf3, 0xb6, 0x2e, 0xad, 0x73, 0x80, 0x89, 0xde, 0x94, 0x3d,
+	0x90, 0x37, 0x45, 0x4d, 0xe0, 0xb1, 0x3b, 0x37, 0x81, 0xd3, 0xd7, 0xa9, 0xf8, 0x60, 0xd6, 0x49,
+	0xfd, 0x2a, 0x03, 0xe5, 0xf8, 0x7b, 0xe2, 0xbb, 0xb1, 0x05, 0xe5, 0xdd, 0x94, 0x3d, 0xf0, 0x6e,
+	0x7a, 0x03, 0x26, 0x49, 0x56, 0xab, 0x79, 0x1e, 0xbf, 0xac, 0x3b, 0x46, 0xb3, 0x41, 0x16, 0xdb,
+	0x7c, 0x6b, 0x29, 0xc0, 0xa5, 0xd8, 0x26, 0xe0, 0x09, 0xd7, 0x1d, 0xbf, 0x4b, 0xd7, 0xfd, 0x28,
+	0x03, 0x93, 0xd2, 0xfb, 0xf0, 0xd1, 0x0b, 0x69, 0xea, 0x34, 0x4c, 0x4a, 0x69, 0xa6, 0xfa, 0x31,
+	0xf3, 0x33, 0xf9, 0xed, 0xf7, 0xe8, 0xcd, 0xcb, 0x14, 0x4c, 0x88, 0xf9, 0xaa, 0xda, 0x80, 0xe9,
+	0x58, 0x7a, 0x29, 0x3e, 0x80, 0x72, 0x90, 0x07, 0x50, 0xe7, 0x60, 0x26, 0x2d, 0x2b, 0x52, 0x2f,
+	0xc2, 0x4c, 0x5a, 0xbe, 0x72, 0xf7, 0x06, 0x5e, 0xe7, 0x27, 0xca, 0x2c, 0xb3, 0xb8, 0x7b, 0xf9,
+	0x2f, 0x14, 0x3a, 0xc2, 0xe4, 0x67, 0x05, 0x97, 0x00, 0x2c, 0x7c, 0xb3, 0x75, 0xc7, 0xc2, 0x98,
+	0xad, 0x07, 0xbe, 0xb9, 0x12, 0xab, 0x23, 0x0b, 0x01, 0x46, 0x34, 0xd9, 0xa6, 0xde, 0xba, 0x63,
+	0x39, 0x4a, 0x35, 0xd9, 0xa6, 0x9e, 0xd0, 0x14, 0x60, 0xea, 0x8f, 0xb3, 0x41, 0xcf, 0x22, 0xba,
+	0x97, 0xff, 0x2e, 0x94, 0xfb, 0xc1, 0x8f, 0x3b, 0x8f, 0x96, 0x56, 0x6d, 0x21, 0x7f, 0xdc, 0xd2,
+	0x94, 0x4c, 0x91, 0x75, 0xf3, 0x72, 0x3c, 0x73, 0x40, 0xdd, 0xcd, 0x58, 0x5d, 0x3e, 0x25, 0x53,
+	0xd0, 0xf7, 0xe1, 0x68, 0x70, 0xcf, 0x6e, 0x17, 0x07, 0x03, 0xcf, 0x8e, 0x54, 0xce, 0x3e, 0x23,
+	0x08, 0x05, 0xe2, 0x23, 0x9f, 0x8e, 0x91, 0x62, 0xea, 0xf9, 0xd8, 0xc7, 0x0e, 0xaa, 0x3e, 0x3e,
+	0xf8, 0xe9, 0x18, 0x49, 0xfd, 0x54, 0x81, 0xe9, 0xd8, 0x97, 0x0e, 0xe8, 0x02, 0x14, 0xe8, 0x87,
+	0x90, 0xb7, 0x5f, 0x01, 0xea, 0x90, 0x94, 0x4f, 0xb2, 0x90, 0xe7, 0x10, 0x7a, 0x11, 0x8a, 0xe1,
+	0x07, 0x11, 0xfc, 0xbc, 0x9d, 0x6d, 0xde, 0x00, 0x94, 0x36, 0x6f, 0x00, 0xaa, 0xbf, 0x50, 0xe0,
+	0xc4, 0xc8, 0xaf, 0x20, 0x1e, 0x76, 0x37, 0x45, 0xbd, 0x01, 0x73, 0xe9, 0x1f, 0x2e, 0x1c, 0xb6,
+	0xf7, 0xa4, 0x5e, 0xa5, 0x7d, 0xa3, 0xf0, 0x03, 0x80, 0x43, 0xeb, 0xfb, 0x69, 0xd8, 0x16, 0x8c,
+	0x3e, 0x59, 0x78, 0xc8, 0xb3, 0xf7, 0xd4, 0xb3, 0x50, 0x08, 0xee, 0x13, 0x20, 0x80, 0xdc, 0xdb,
+	0x1b, 0xcb, 0x1b, 0xcb, 0x17, 0xca, 0x47, 0x50, 0x09, 0xf2, 0x6b, 0xcb, 0x57, 0x2f, 0x5c, 0xbe,
+	0x7a, 0xb1, 0xac, 0x90, 0x1f, 0xcd, 0x8d, 0xab, 0x57, 0xc9, 0x8f, 0xcc, 0x53, 0x57, 0xc4, 0xdb,
+	0x8d, 0x3c, 0xeb, 0x9d, 0x80, 0xc2, 0x52, 0xbf, 0x4f, 0xc3, 0x2f, 0x93, 0x5d, 0xde, 0x35, 0x48,
+	0xa4, 0x2b, 0x2b, 0x28, 0x0f, 0xd9, 0x6b, 0xd7, 0x56, 0xcb, 0x19, 0x34, 0x03, 0xe5, 0x0b, 0x58,
+	0xd3, 0x4d, 0xc3, 0xc2, 0x41, 0xcc, 0x2f, 0x67, 0x1b, 0xdb, 0x5f, 0x7e, 0xb3, 0xa0, 0x7c, 0xf5,
+	0xcd, 0x82, 0xf2, 0x97, 0x6f, 0x16, 0x94, 0x5b, 0xdf, 0x2e, 0x1c, 0xf9, 0xea, 0xdb, 0x85, 0x23,
+	0x7f, 0xfe, 0x76, 0xe1, 0xc8, 0xbb, 0xcf, 0x0a, 0x9f, 0x4c, 0xb3, 0x67, 0xea, 0x3b, 0x36, 0x09,
+	0xc5, 0xfc, 0x57, 0x3d, 0xfe, 0x91, 0xf8, 0x17, 0x99, 0x53, 0x4b, 0xf4, 0xe7, 0x1a, 0xe3, 0xab,
+	0x5d, 0xb6, 0x6b, 0x0c, 0xa0, 0xdf, 0xf9, 0xba, 0xed, 0x1c, 0xfd, 0x9e, 0xf7, 0xf9, 0x7f, 0x07,
+	0x00, 0x00, 0xff, 0xff, 0xd9, 0x00, 0x7d, 0xf2, 0x5f, 0x3e, 0x00, 0x00,
 }
 
 func (m *EventSequence) Marshal() (dAtA []byte, err error) {
@@ -4513,6 +4634,29 @@ func (m *EventSequence_Event_JobRunCancelled) MarshalToSizedBuffer(dAtA []byte)
 	}
 	return len(dAtA) - i, nil
 }
+func (m *EventSequence_Event_JobValidated) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *EventSequence_Event_JobValidated) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	if m.JobValidated != nil {
+		{
+			size, err := m.JobValidated.MarshalToSizedBuffer(dAtA[:i])
+			if err != nil {
+				return 0, err
+			}
+			i -= size
+			i = encodeVarintEvents(dAtA, i, uint64(size))
+		}
+		i--
+		dAtA[i] = 0x1
+		i--
+		dAtA[i] = 0xca
+	}
+	return len(dAtA) - i, nil
+}
 func (m *ResourceUtilisation) Marshal() (dAtA []byte, err error) {
 	size := m.Size()
 	dAtA = make([]byte, size)
@@ -5346,20 +5490,20 @@ func (m *JobSetFilter) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	var l int
 	_ = l
 	if len(m.States) > 0 {
-		dAtA47 := make([]byte, len(m.States)*10)
-		var j46 int
+		dAtA48 := make([]byte, len(m.States)*10)
+		var j47 int
 		for _, num := range m.States {
 			for num >= 1<<7 {
-				dAtA47[j46] = uint8(uint64(num)&0x7f | 0x80)
+				dAtA48[j47] = uint8(uint64(num)&0x7f | 0x80)
 				num >>= 7
-				j46++
+				j47++
 			}
-			dAtA47[j46] = uint8(num)
-			j46++
+			dAtA48[j47] = uint8(num)
+			j47++
 		}
-		i -= j46
-		copy(dAtA[i:], dAtA47[:j46])
-		i = encodeVarintEvents(dAtA, i, uint64(j46))
+		i -= j47
+		copy(dAtA[i:], dAtA48[:j47])
+		i = encodeVarintEvents(dAtA, i, uint64(j47))
 		i--
 		dAtA[i] = 0xa
 	}
@@ -5394,20 +5538,20 @@ func (m *CancelJobSet) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 		dAtA[i] = 0x12
 	}
 	if len(m.States) > 0 {
-		dAtA49 := make([]byte, len(m.States)*10)
-		var j48 int
+		dAtA50 := make([]byte, len(m.States)*10)
+		var j49 int
 		for _, num := range m.States {
 			for num >= 1<<7 {
-				dAtA49[j48] = uint8(uint64(num)&0x7f | 0x80)
+				dAtA50[j49] = uint8(uint64(num)&0x7f | 0x80)
 				num >>= 7
-				j48++
+				j49++
 			}
-			dAtA49[j48] = uint8(num)
-			j48++
+			dAtA50[j49] = uint8(num)
+			j49++
 		}
-		i -= j48
-		copy(dAtA[i:], dAtA49[:j48])
-		i = encodeVarintEvents(dAtA, i, uint64(j48))
+		i -= j49
+		copy(dAtA[i:], dAtA50[:j49])
+		i = encodeVarintEvents(dAtA, i, uint64(j49))
 		i--
 		dAtA[i] = 0xa
 	}
@@ -6434,6 +6578,27 @@ func (m *Error_GangJobUnschedulable) MarshalToSizedBuffer(dAtA []byte) (int, err
 	}
 	return len(dAtA) - i, nil
 }
+func (m *Error_JobRejected) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *Error_JobRejected) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	if m.JobRejected != nil {
+		{
+			size, err := m.JobRejected.MarshalToSizedBuffer(dAtA[:i])
+			if err != nil {
+				return 0, err
+			}
+			i -= size
+			i = encodeVarintEvents(dAtA, i, uint64(size))
+		}
+		i--
+		dAtA[i] = 0x6a
+	}
+	return len(dAtA) - i, nil
+}
 func (m *KubernetesError) Marshal() (dAtA []byte, err error) {
 	size := m.Size()
 	dAtA = make([]byte, size)
@@ -6916,6 +7081,36 @@ func (m *GangJobUnschedulable) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 	return len(dAtA) - i, nil
 }
 
+func (m *JobRejected) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *JobRejected) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *JobRejected) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if len(m.Message) > 0 {
+		i -= len(m.Message)
+		copy(dAtA[i:], m.Message)
+		i = encodeVarintEvents(dAtA, i, uint64(len(m.Message)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
 func (m *JobDuplicateDetected) Marshal() (dAtA []byte, err error) {
 	size := m.Size()
 	dAtA = make([]byte, size)
@@ -7156,6 +7351,41 @@ func (m *JobPreemptionRequested) MarshalToSizedBuffer(dAtA []byte) (int, error)
 	return len(dAtA) - i, nil
 }
 
+func (m *JobValidated) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *JobValidated) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *JobValidated) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.JobId != nil {
+		{
+			size, err := m.JobId.MarshalToSizedBuffer(dAtA[:i])
+			if err != nil {
+				return 0, err
+			}
+			i -= size
+			i = encodeVarintEvents(dAtA, i, uint64(size))
+		}
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
 func (m *JobRunCancelled) Marshal() (dAtA []byte, err error) {
 	size := m.Size()
 	dAtA = make([]byte, size)
@@ -7539,6 +7769,18 @@ func (m *EventSequence_Event_JobRunCancelled) Size() (n int) {
 	}
 	return n
 }
+func (m *EventSequence_Event_JobValidated) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.JobValidated != nil {
+		l = m.JobValidated.Size()
+		n += 2 + l + sovEvents(uint64(l))
+	}
+	return n
+}
 func (m *ResourceUtilisation) Size() (n int) {
 	if m == nil {
 		return 0
@@ -8371,6 +8613,18 @@ func (m *Error_GangJobUnschedulable) Size() (n int) {
 	}
 	return n
 }
+func (m *Error_JobRejected) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.JobRejected != nil {
+		l = m.JobRejected.Size()
+		n += 1 + l + sovEvents(uint64(l))
+	}
+	return n
+}
 func (m *KubernetesError) Size() (n int) {
 	if m == nil {
 		return 0
@@ -8580,6 +8834,19 @@ func (m *GangJobUnschedulable) Size() (n int) {
 	return n
 }
 
+func (m *JobRejected) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.Message)
+	if l > 0 {
+		n += 1 + l + sovEvents(uint64(l))
+	}
+	return n
+}
+
 func (m *JobDuplicateDetected) Size() (n int) {
 	if m == nil {
 		return 0
@@ -8668,6 +8935,19 @@ func (m *JobPreemptionRequested) Size() (n int) {
 	return n
 }
 
+func (m *JobValidated) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	if m.JobId != nil {
+		l = m.JobId.Size()
+		n += 1 + l + sovEvents(uint64(l))
+	}
+	return n
+}
+
 func (m *JobRunCancelled) Size() (n int) {
 	if m == nil {
 		return 0
@@ -9773,6 +10053,41 @@ func (m *EventSequence_Event) Unmarshal(dAtA []byte) error {
 			}
 			m.Event = &EventSequence_Event_JobRunCancelled{v}
 			iNdEx = postIndex
+		case 25:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field JobValidated", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthEvents
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			v := &JobValidated{}
+			if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			m.Event = &EventSequence_Event_JobValidated{v}
+			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
 			skippy, err := skipEvents(dAtA[iNdEx:])
@@ -14912,6 +15227,41 @@ func (m *Error) Unmarshal(dAtA []byte) error {
 			}
 			m.Reason = &Error_GangJobUnschedulable{v}
 			iNdEx = postIndex
+		case 13:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field JobRejected", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthEvents
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			v := &JobRejected{}
+			if err := v.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			m.Reason = &Error_JobRejected{v}
+			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
 			skippy, err := skipEvents(dAtA[iNdEx:])
@@ -16334,6 +16684,88 @@ func (m *GangJobUnschedulable) Unmarshal(dAtA []byte) error {
 	}
 	return nil
 }
+func (m *JobRejected) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowEvents
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: JobRejected: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: JobRejected: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Message", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthEvents
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Message = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipEvents(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
 func (m *JobDuplicateDetected) Unmarshal(dAtA []byte) error {
 	l := len(dAtA)
 	iNdEx := 0
@@ -16963,6 +17395,92 @@ func (m *JobPreemptionRequested) Unmarshal(dAtA []byte) error {
 	}
 	return nil
 }
+func (m *JobValidated) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowEvents
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: JobValidated: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: JobValidated: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field JobId", wireType)
+			}
+			var msglen int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowEvents
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				msglen |= int(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if msglen < 0 {
+				return ErrInvalidLengthEvents
+			}
+			postIndex := iNdEx + msglen
+			if postIndex < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			if m.JobId == nil {
+				m.JobId = &Uuid{}
+			}
+			if err := m.JobId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+				return err
+			}
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipEvents(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthEvents
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
 func (m *JobRunCancelled) Unmarshal(dAtA []byte) error {
 	l := len(dAtA)
 	iNdEx := 0
diff --git a/pkg/armadaevents/events.proto b/pkg/armadaevents/events.proto
index fa9e1bc5..2a3d4250 100644
--- a/pkg/armadaevents/events.proto
+++ b/pkg/armadaevents/events.proto
@@ -92,6 +92,7 @@ message EventSequence {
             JobRequeued jobRequeued = 22;
             JobPreemptionRequested jobPreemptionRequested = 23;
             JobRunCancelled jobRunCancelled = 24;
+            JobValidated jobValidated = 25;
         }
     }
     // The system is namespaced by queue, and all events are associated with a job set.
@@ -427,6 +428,7 @@ message Error {
         PodTerminated podTerminated = 10;
         JobRunPreemptedError jobRunPreemptedError = 11;
         GangJobUnschedulable gangJobUnschedulable = 12;
+        JobRejected jobRejected = 13;
     }
 }
 
@@ -515,6 +517,10 @@ message GangJobUnschedulable{
     string message = 1;
 }
 
+message JobRejected {
+  string message = 1;
+}
+
 // Generated by the scheduler whenever it detects a SubmitJob message that includes a previously used deduplication id
 // (i.e., when it detects a duplicate job submission).
 message JobDuplicateDetected {
@@ -553,6 +559,11 @@ message JobPreemptionRequested {
     Uuid job_id = 1;
 }
 
+// Indicates that the scheduler is happy with the job
+message JobValidated {
+  Uuid job_id = 1;
+}
+
 // Generated by the scheduler when a job is cancelled, all active job runs are also cancelled
 // One such message is generated per job run that was cancelled.
 message JobRunCancelled {
diff --git a/pkg/armadaevents/jobid.go b/pkg/armadaevents/jobid.go
index 80eaf3dc..d3f1425d 100644
--- a/pkg/armadaevents/jobid.go
+++ b/pkg/armadaevents/jobid.go
@@ -42,6 +42,8 @@ func JobIdFromEvent(event *EventSequence_Event) (*Uuid, error) {
 		return e.JobRunCancelled.JobId, nil
 	case *EventSequence_Event_JobRequeued:
 		return e.JobRequeued.JobId, nil
+	case *EventSequence_Event_JobValidated:
+		return e.JobValidated.JobId, nil
 	default:
 		err := errors.WithStack(&armadaerrors.ErrInvalidArgument{
 			Name:    "event.Event",


Test patch:
diff --git a/internal/armada/submit/submit_test.go b/internal/armada/submit/submit_test.go
index f8da3a6c..1a0a5082 100644
--- a/internal/armada/submit/submit_test.go
+++ b/internal/armada/submit/submit_test.go
@@ -21,23 +21,21 @@ import (
 )
 
 type mockObjects struct {
-	publisher     *mocks.MockPublisher
-	queueRepo     *mocks.MockQueueRepository
-	jobRep        *mocks.MockJobRepository
-	deduplicator  *mocks.MockDeduplicator
-	submitChecker *mocks.MockSubmitScheduleChecker
-	authorizer    *mocks.MockActionAuthorizer
+	publisher    *mocks.MockPublisher
+	queueRepo    *mocks.MockQueueRepository
+	jobRep       *mocks.MockJobRepository
+	deduplicator *mocks.MockDeduplicator
+	authorizer   *mocks.MockActionAuthorizer
 }
 
 func createMocks(t *testing.T) *mockObjects {
 	ctrl := gomock.NewController(t)
 	return &mockObjects{
-		publisher:     mocks.NewMockPublisher(ctrl),
-		queueRepo:     mocks.NewMockQueueRepository(ctrl),
-		jobRep:        mocks.NewMockJobRepository(ctrl),
-		deduplicator:  mocks.NewMockDeduplicator(ctrl),
-		submitChecker: mocks.NewMockSubmitScheduleChecker(ctrl),
-		authorizer:    mocks.NewMockActionAuthorizer(ctrl),
+		publisher:    mocks.NewMockPublisher(ctrl),
+		queueRepo:    mocks.NewMockQueueRepository(ctrl),
+		jobRep:       mocks.NewMockJobRepository(ctrl),
+		deduplicator: mocks.NewMockDeduplicator(ctrl),
+		authorizer:   mocks.NewMockActionAuthorizer(ctrl),
 	}
 }
 
@@ -103,12 +101,6 @@ func TestSubmit_Success(t *testing.T) {
 				StoreOriginalJobIds(ctx, testfixtures.DefaultQueue.Name, gomock.Any()).
 				Times(1)
 
-			mockedObjects.submitChecker.
-				EXPECT().
-				CheckApiJobs(gomock.Any(), testfixtures.DefaultPriorityClass).
-				Return(true, "").
-				Times(1)
-
 			mockedObjects.jobRep.
 				EXPECT().
 				StorePulsarSchedulerJobDetails(ctx, gomock.Any()).
@@ -203,50 +195,6 @@ func TestSubmit_FailedValidation(t *testing.T) {
 	}
 }
 
-func TestSubmit_SubmitCheckFailed(t *testing.T) {
-	tests := map[string]struct {
-		req *api.JobSubmitRequest
-	}{
-		"Submit check fails": {
-			req: testfixtures.SubmitRequestWithNItems(1),
-		},
-	}
-	for name, tc := range tests {
-		t.Run(name, func(t *testing.T) {
-			ctx, cancel := armadacontext.WithTimeout(armadacontext.Background(), 5*time.Second)
-			server, mockedObjects := createTestServer(t)
-
-			mockedObjects.queueRepo.
-				EXPECT().GetQueue(ctx, tc.req.Queue).
-				Return(testfixtures.DefaultQueue, nil).
-				Times(1)
-
-			mockedObjects.authorizer.
-				EXPECT().
-				AuthorizeQueueAction(ctx, testfixtures.DefaultQueue, permissions.SubmitAnyJobs, queue.PermissionVerbSubmit).
-				Return(nil).
-				Times(1)
-
-			mockedObjects.deduplicator.
-				EXPECT().
-				GetOriginalJobIds(ctx, testfixtures.DefaultQueue.Name, tc.req.JobRequestItems).
-				Return(nil, nil).
-				Times(1)
-
-			mockedObjects.submitChecker.
-				EXPECT().
-				CheckApiJobs(gomock.Any(), testfixtures.DefaultPriorityClass).
-				Return(false, "").
-				Times(1)
-
-			resp, err := server.SubmitJobs(ctx, tc.req)
-			assert.Error(t, err)
-			assert.Nil(t, resp)
-			cancel()
-		})
-	}
-}
-
 func withNamespace(req *api.JobSubmitRequest, n string) *api.JobSubmitRequest {
 	for _, item := range req.JobRequestItems {
 		item.Namespace = n
@@ -315,7 +263,6 @@ func createTestServer(t *testing.T) (*Server, *mockObjects) {
 		m.jobRep,
 		testfixtures.DefaultSubmissionConfig(),
 		m.deduplicator,
-		m.submitChecker,
 		m.authorizer,
 		true)
 	server.clock = clock.NewFakeClock(testfixtures.DefaultTime)
diff --git a/internal/common/ingest/testfixtures/event.go b/internal/common/ingest/testfixtures/event.go
index bba02c70..59860e8f 100644
--- a/internal/common/ingest/testfixtures/event.go
+++ b/internal/common/ingest/testfixtures/event.go
@@ -314,6 +314,15 @@ var JobCancelled = &armadaevents.EventSequence_Event{
 	},
 }
 
+var JobValidated = &armadaevents.EventSequence_Event{
+	Created: &testfixtures.BaseTime,
+	Event: &armadaevents.EventSequence_Event_JobValidated{
+		JobValidated: &armadaevents.JobValidated{
+			JobId: JobIdProto,
+		},
+	},
+}
+
 var JobRequeued = &armadaevents.EventSequence_Event{
 	Created: &BaseTime,
 	Event: &armadaevents.EventSequence_Event_JobRequeued{
diff --git a/internal/scheduler/jobdb/job_test.go b/internal/scheduler/jobdb/job_test.go
index be8cf7ca..d0572cef 100644
--- a/internal/scheduler/jobdb/job_test.go
+++ b/internal/scheduler/jobdb/job_test.go
@@ -37,6 +37,7 @@ var baseJob = jobDb.NewJob(
 	false,
 	false,
 	3,
+	false,
 )
 
 var baseRun = &JobRun{
@@ -307,9 +308,9 @@ func TestJob_TestWithCreated(t *testing.T) {
 }
 
 func TestJob_DeepCopy(t *testing.T) {
-	original := jobDb.NewJob("test-job", "test-jobSet", "test-queue", 2, jobSchedulingInfo, true, 0, false, false, false, 3)
+	original := jobDb.NewJob("test-job", "test-jobSet", "test-queue", 2, jobSchedulingInfo, true, 0, false, false, false, 3, false)
 	original = original.WithUpdatedRun(baseJobRun.DeepCopy())
-	expected := jobDb.NewJob("test-job", "test-jobSet", "test-queue", 2, jobSchedulingInfo, true, 0, false, false, false, 3)
+	expected := jobDb.NewJob("test-job", "test-jobSet", "test-queue", 2, jobSchedulingInfo, true, 0, false, false, false, 3, false)
 	expected = expected.WithUpdatedRun(baseJobRun.DeepCopy())
 
 	result := original.DeepCopy()
@@ -361,7 +362,7 @@ func TestJobSchedulingInfoFieldsInitialised(t *testing.T) {
 	assert.Nil(t, infoWithNilFields.GetPodRequirements().NodeSelector)
 	assert.Nil(t, infoWithNilFields.GetPodRequirements().Annotations)
 
-	job := jobDb.NewJob("test-job", "test-jobSet", "test-queue", 2, infoWithNilFieldsCopy, true, 0, false, false, false, 3)
+	job := jobDb.NewJob("test-job", "test-jobSet", "test-queue", 2, infoWithNilFieldsCopy, true, 0, false, false, false, 3, false)
 	assert.NotNil(t, job.NodeSelector())
 	assert.NotNil(t, job.Annotations())
 
diff --git a/internal/scheduler/jobdb/jobdb_test.go b/internal/scheduler/jobdb/jobdb_test.go
index 6acedf17..13d8c9db 100644
--- a/internal/scheduler/jobdb/jobdb_test.go
+++ b/internal/scheduler/jobdb/jobdb_test.go
@@ -2,6 +2,7 @@ package jobdb
 
 import (
 	"math/rand"
+	"sort"
 	"testing"
 
 	"github.com/gogo/protobuf/proto"
@@ -71,6 +72,28 @@ func TestJobDb_TestGetById(t *testing.T) {
 	assert.Nil(t, txn.GetById(util.NewULID()))
 }
 
+func TestJobDb_TestGetUnvalidated(t *testing.T) {
+	jobDb := NewTestJobDb()
+	job1 := newJob().WithValidated(false)
+	job2 := newJob().WithValidated(true)
+	job3 := newJob().WithValidated(false)
+	txn := jobDb.WriteTxn()
+
+	err := txn.Upsert([]*Job{job1, job2, job3})
+	require.NoError(t, err)
+
+	expected := []*Job{job1, job3}
+
+	var actual []*Job
+	it := txn.UnvalidatedJobs()
+	for job, _ := it.Next(); job != nil; job, _ = it.Next() {
+		actual = append(actual, job)
+	}
+	sort.SliceStable(actual, func(i, j int) bool { return actual[i].id < actual[j].id })
+	sort.SliceStable(expected, func(i, j int) bool { return expected[i].id < expected[j].id })
+	assert.Equal(t, expected, actual)
+}
+
 func TestJobDb_TestGetByRunId(t *testing.T) {
 	jobDb := NewTestJobDb()
 	job1 := newJob().WithNewRun("executor", "nodeId", "nodeName", 5)
@@ -245,7 +268,7 @@ func TestJobDb_SchedulingKeyIsPopulated(t *testing.T) {
 		},
 	}
 	jobDb := NewTestJobDb()
-	job := jobDb.NewJob("jobId", "jobSet", "queue", 1, jobSchedulingInfo, false, 0, false, false, false, 2)
+	job := jobDb.NewJob("jobId", "jobSet", "queue", 1, jobSchedulingInfo, false, 0, false, false, false, 2, false)
 
 	actualSchedulingKey, ok := job.SchedulingKey()
 	require.True(t, ok)
diff --git a/internal/scheduler/scheduler_test.go b/internal/scheduler/scheduler_test.go
index 2d0106aa..ca2768c5 100644
--- a/internal/scheduler/scheduler_test.go
+++ b/internal/scheduler/scheduler_test.go
@@ -157,6 +157,7 @@ var queuedJob = testfixtures.JobDb.NewJob(
 	false,
 	false,
 	1,
+	true,
 )
 
 var queuedJobWithExpiredTtl = testfixtures.JobDb.NewJob(
@@ -171,6 +172,7 @@ var queuedJobWithExpiredTtl = testfixtures.JobDb.NewJob(
 	false,
 	false,
 	1,
+	true,
 )
 
 var leasedJob = testfixtures.JobDb.NewJob(
@@ -185,6 +187,7 @@ var leasedJob = testfixtures.JobDb.NewJob(
 	false,
 	false,
 	1,
+	true,
 ).WithNewRun("testExecutor", "test-node", "node", 5)
 
 var preemptibleLeasedJob = testfixtures.JobDb.NewJob(
@@ -199,6 +202,7 @@ var preemptibleLeasedJob = testfixtures.JobDb.NewJob(
 	false,
 	false,
 	1,
+	true,
 ).WithNewRun("testExecutor", "test-node", "node", 5)
 
 var cancelledJob = testfixtures.JobDb.NewJob(
@@ -213,6 +217,7 @@ var cancelledJob = testfixtures.JobDb.NewJob(
 	false,
 	true,
 	1,
+	true,
 ).WithNewRun("testExecutor", "test-node", "node", 5)
 
 var returnedOnceLeasedJob = testfixtures.JobDb.NewJob(
@@ -227,6 +232,7 @@ var returnedOnceLeasedJob = testfixtures.JobDb.NewJob(
 	false,
 	false,
 	1,
+	true,
 ).WithUpdatedRun(testfixtures.JobDb.CreateRun(
 	uuid.New(),
 	"01h3w2wtdchtc80hgyp782shrv",
@@ -288,6 +294,7 @@ var leasedFailFastJob = testfixtures.JobDb.NewJob(
 	false,
 	false,
 	1,
+	true,
 ).WithNewRun("testExecutor", "test-node", "node", 5)
 
 var (
@@ -308,6 +315,7 @@ var (
 		false,
 		false,
 		1,
+		true,
 	).WithUpdatedRun(testfixtures.JobDb.CreateRun(
 		uuid.New(),
 		requeuedJobId,
@@ -360,6 +368,7 @@ func TestScheduler_TestCycle(t *testing.T) {
 		expectedJobSucceeded             []string                          // ids of jobs we expect to have  produced succeeeded messages
 		expectedLeased                   []string                          // ids of jobs we expected to be leased in jobdb at the end of the cycle
 		expectedRequeued                 []string                          // ids of jobs we expected to be requeued in jobdb at the end of the cycle
+		expectedValidated                []string                          // ids of jobs we expected to have produced submit checked messages
 		expectedTerminal                 []string                          // ids of jobs we expected to be terminal in jobdb at the end of the cycle
 		expectedJobPriority              map[string]uint32                 // expected priority of jobs at the end of the cycle
 		expectedNodeAntiAffinities       []string                          // list of nodes there is expected to be anti affinities for on job scheduling info
@@ -372,6 +381,12 @@ func TestScheduler_TestCycle(t *testing.T) {
 			expectedLeased:        []string{queuedJob.Id()},
 			expectedQueuedVersion: 1,
 		},
+		"Submit check a job": {
+			initialJobs:           []*jobdb.Job{queuedJob.WithValidated(false)},
+			expectedQueued:        []string{queuedJob.Id()},
+			expectedValidated:     []string{queuedJob.Id()},
+			expectedQueuedVersion: 0,
+		},
 		"Lease a single job from an update": {
 			jobUpdates: []database.Job{
 				{
@@ -383,6 +398,7 @@ func TestScheduler_TestCycle(t *testing.T) {
 					SchedulingInfo:        schedulingInfoBytes,
 					SchedulingInfoVersion: int32(schedulingInfo.Version),
 					Serial:                1,
+					Validated:             true,
 				},
 			},
 			expectedJobRunLeased:  []string{queuedJob.Id()},
@@ -400,6 +416,7 @@ func TestScheduler_TestCycle(t *testing.T) {
 					SchedulingInfo:        schedulingInfoBytes,
 					SchedulingInfoVersion: int32(schedulingInfo.Version),
 					Serial:                1,
+					Validated:             true,
 				},
 				{
 					JobID:                 "01h434g4hxww2pknb2q1nfmfph",
@@ -410,6 +427,7 @@ func TestScheduler_TestCycle(t *testing.T) {
 					SchedulingInfo:        schedulingInfoBytes,
 					SchedulingInfoVersion: int32(schedulingInfo.Version),
 					Serial:                1,
+					Validated:             true,
 				},
 			},
 			expectedJobRunLeased:  []string{"01h3w2wtdchtc80hgyp782shrv", "01h434g4hxww2pknb2q1nfmfph"},
@@ -481,6 +499,7 @@ func TestScheduler_TestCycle(t *testing.T) {
 					SchedulingInfo:        schedulingInfoBytes,
 					SchedulingInfoVersion: int32(schedulingInfo.Version),
 					Serial:                1,
+					Validated:             true,
 				},
 			},
 			runUpdates: []database.Run{
@@ -541,7 +560,7 @@ func TestScheduler_TestCycle(t *testing.T) {
 		// When a lease is returned and the run was attempted, a node anti affinity is added
 		// If this node anti-affinity makes the job unschedulable, it should be failed.
 		"Lease returned and failed": {
-			initialJobs: []*jobdb.Job{leasedJob},
+			initialJobs: []*jobdb.Job{leasedJob.WithValidated(true)},
 			runUpdates: []database.Run{
 				{
 					RunID:        leasedJob.LatestRun().Id(),
@@ -666,6 +685,7 @@ func TestScheduler_TestCycle(t *testing.T) {
 					Serial:         1,
 					Submitted:      queuedJobWithExpiredTtl.Created(),
 					SchedulingInfo: schedulingInfoWithQueueTtlBytes,
+					Validated:      true,
 				},
 			},
 
@@ -788,6 +808,12 @@ func TestScheduler_TestCycle(t *testing.T) {
 			expectedTerminal:      []string{leasedJob.Id()},
 			expectedQueuedVersion: leasedJob.QueuedVersion(),
 		},
+		"Submit check failed": {
+			initialJobs:          []*jobdb.Job{queuedJob.WithValidated(false)},
+			submitCheckerFailure: true,
+			expectedJobErrors:    []string{queuedJob.Id()},
+			expectedTerminal:     []string{queuedJob.Id()},
+		},
 		"Job failed": {
 			initialJobs: []*jobdb.Job{leasedJob},
 			runUpdates: []database.Run{
@@ -928,6 +954,7 @@ func TestScheduler_TestCycle(t *testing.T) {
 				fmt.Sprintf("%T", &armadaevents.EventSequence_Event_JobSucceeded{}):     stringSet(tc.expectedJobSucceeded),
 				fmt.Sprintf("%T", &armadaevents.EventSequence_Event_JobRequeued{}):      stringSet(tc.expectedRequeued),
 				fmt.Sprintf("%T", &armadaevents.EventSequence_Event_CancelJob{}):        stringSet(tc.expectedJobRequestCancel),
+				fmt.Sprintf("%T", &armadaevents.EventSequence_Event_JobValidated{}):     stringSet(tc.expectedValidated),
 			}
 			err = subtractEventsFromOutstandingEventsByType(publisher.eventSequences, outstandingEventsByType)
 			require.NoError(t, err)
@@ -1078,7 +1105,7 @@ func TestRun(t *testing.T) {
 		wg.Add(1)
 		sched.onCycleCompleted = func() { wg.Done() }
 		jobId := util.NewULID()
-		jobRepo.updatedJobs = []database.Job{{JobID: jobId, Queue: "testQueue", Queued: true}}
+		jobRepo.updatedJobs = []database.Job{{JobID: jobId, Queue: "testQueue", Queued: true, Validated: true}}
 		schedulingAlgo.jobsToSchedule = []string{jobId}
 		testClock.Step(10 * time.Second)
 		wg.Wait()
@@ -1124,6 +1151,7 @@ func TestScheduler_TestSyncState(t *testing.T) {
 					Priority:       int64(queuedJob.Priority()),
 					SchedulingInfo: schedulingInfoBytes,
 					Serial:         1,
+					Validated:      true,
 				},
 			},
 			expectedUpdatedJobs: []*jobdb.Job{queuedJob},
@@ -1320,20 +1348,16 @@ type testSubmitChecker struct {
 	checkSuccess bool
 }
 
-func (t *testSubmitChecker) CheckApiJobs(_ *armadaevents.EventSequence, _ string) (bool, string) {
-	reason := ""
-	if !t.checkSuccess {
-		reason = "CheckApiJobs failed"
-	}
-	return t.checkSuccess, reason
-}
-
-func (t *testSubmitChecker) CheckJobDbJobs(_ []*jobdb.Job) (bool, string) {
-	reason := ""
-	if !t.checkSuccess {
-		reason = "CheckJobDbJobs failed"
+func (t *testSubmitChecker) Check(_ *armadacontext.Context, jobs []*jobdb.Job) (map[string]schedulingResult, error) {
+	result := make(map[string]schedulingResult)
+	for _, job := range jobs {
+		if t.checkSuccess {
+			result[job.Id()] = schedulingResult{isSchedulable: true}
+		} else {
+			result[job.Id()] = schedulingResult{isSchedulable: false, reason: "job not schedulable"}
+		}
 	}
-	return t.checkSuccess, reason
+	return result, nil
 }
 
 // Test implementations of the interfaces needed by the Scheduler
@@ -1556,6 +1580,7 @@ var (
 		QueuedVersion:         0,
 		SchedulingInfo:        schedulingInfoBytes,
 		SchedulingInfoVersion: int32(schedulingInfo.Version),
+		Validated:             true,
 		Serial:                0,
 	}
 	queuedJobWithTTLA = &database.Job{
@@ -1566,6 +1591,7 @@ var (
 		QueuedVersion:         0,
 		SchedulingInfo:        schedulingInfoWithQueueTtlBytes,
 		SchedulingInfoVersion: int32(schedulingInfoWithQueueTtl.Version),
+		Validated:             true,
 		Serial:                0,
 	}
 	queuedJobWithFailFastA = &database.Job{
@@ -1576,6 +1602,7 @@ var (
 		QueuedVersion:         0,
 		SchedulingInfo:        failFastSchedulingInfoBytes,
 		SchedulingInfoVersion: int32(failFastSchedulingInfo.Version),
+		Validated:             true,
 		Serial:                0,
 	}
 	queuedJobWithUpdatedPriorityA = &database.Job{
@@ -1586,6 +1613,7 @@ var (
 		QueuedVersion:         0,
 		SchedulingInfo:        schedulingInfoWithUpdatedPriorityBytes,
 		SchedulingInfoVersion: int32(schedulingInfoWithUpdatedPriority.Version),
+		Validated:             true,
 		Serial:                1,
 	}
 	runningJobA = &database.Job{
@@ -1595,6 +1623,7 @@ var (
 		QueuedVersion:         1,
 		SchedulingInfo:        schedulingInfoBytes,
 		SchedulingInfoVersion: int32(schedulingInfo.Version),
+		Validated:             true,
 		Serial:                0,
 	}
 	runningJobWithUpdatedPriorityA = &database.Job{
@@ -1604,6 +1633,7 @@ var (
 		QueuedVersion:         1,
 		SchedulingInfo:        schedulingInfoWithUpdatedPriorityBytes,
 		SchedulingInfoVersion: int32(schedulingInfoWithUpdatedPriority.Version),
+		Validated:             true,
 		Serial:                1,
 	}
 	failedJobA = &database.Job{
@@ -1614,6 +1644,7 @@ var (
 		QueuedVersion:         0,
 		SchedulingInfo:        schedulingInfoBytes,
 		SchedulingInfoVersion: int32(schedulingInfo.Version),
+		Validated:             true,
 		Serial:                0,
 	}
 	cancelledJobA = &database.Job{
@@ -1624,6 +1655,7 @@ var (
 		QueuedVersion:         0,
 		SchedulingInfo:        schedulingInfoBytes,
 		SchedulingInfoVersion: int32(schedulingInfo.Version),
+		Validated:             true,
 		Serial:                0,
 	}
 	cancelRequestedJobA = &database.Job{
@@ -1635,6 +1667,7 @@ var (
 		QueuedVersion:         0,
 		SchedulingInfo:        schedulingInfoBytes,
 		SchedulingInfoVersion: int32(schedulingInfo.Version),
+		Validated:             true,
 		Serial:                0,
 	}
 	cancelByJobSetRequestedJobA = &database.Job{
@@ -1646,6 +1679,7 @@ var (
 		QueuedVersion:           0,
 		SchedulingInfo:          schedulingInfoBytes,
 		SchedulingInfoVersion:   int32(schedulingInfo.Version),
+		Validated:               true,
 		Serial:                  0,
 	}
 	runningCancelRequestedJobA = &database.Job{
@@ -1657,6 +1691,7 @@ var (
 		QueuedVersion:         1,
 		SchedulingInfo:        schedulingInfoBytes,
 		SchedulingInfoVersion: int32(schedulingInfo.Version),
+		Validated:             true,
 		Serial:                0,
 	}
 	runningCancelByJobSetRequestedJobA = &database.Job{
@@ -1667,6 +1702,7 @@ var (
 		QueuedVersion:           1,
 		SchedulingInfo:          schedulingInfoBytes,
 		SchedulingInfoVersion:   int32(schedulingInfo.Version),
+		Validated:               true,
 		Serial:                  0,
 	}
 	newRunA = &database.Run{
@@ -1740,6 +1776,7 @@ func jobDbJobFromDbJob(job *database.Job) *jobdb.Job {
 		job.CancelByJobsetRequested,
 		job.Cancelled,
 		0,
+		job.Validated,
 	)
 }
 
@@ -1775,6 +1812,9 @@ func TestCycleConsistency(t *testing.T) {
 		expectedEventSequencesCycleOne   []*armadaevents.EventSequence
 		expectedEventSequencesCycleTwo   []*armadaevents.EventSequence
 		expectedEventSequencesCycleThree []*armadaevents.EventSequence
+
+		// If true then will jobs will fail submit check
+		failSubmitCheck bool
 	}{
 		"Load a queued job": {
 			firstSchedulerDbUpdate: schedulerDbUpdate{
@@ -2237,6 +2277,7 @@ func TestCycleConsistency(t *testing.T) {
 				}(),
 			},
 			expectedJobDbCycleThree: []*jobdb.Job{},
+			failSubmitCheck:         true,
 		},
 		"Schedule a new job that then fails to start with fail-fast set to true": {
 			firstSchedulerDbUpdate: schedulerDbUpdate{
@@ -2443,7 +2484,9 @@ func TestCycleConsistency(t *testing.T) {
 					},
 					leader.NewStandaloneLeaderController(),
 					newTestPublisher(),
-					&testSubmitChecker{},
+					&testSubmitChecker{
+						checkSuccess: !tc.failSubmitCheck,
+					},
 					1*time.Second,
 					5*time.Second,
 					0,
diff --git a/internal/scheduler/simulator/simulator_test.go b/internal/scheduler/simulator/simulator_test.go
index d2d0c2f3..7878fa8c 100644
--- a/internal/scheduler/simulator/simulator_test.go
+++ b/internal/scheduler/simulator/simulator_test.go
@@ -14,7 +14,7 @@ import (
 	"github.com/armadaproject/armada/internal/common/armadacontext"
 	armadaslices "github.com/armadaproject/armada/internal/common/slices"
 	"github.com/armadaproject/armada/internal/common/types"
-	schedulerobjects "github.com/armadaproject/armada/internal/scheduler/schedulerobjects"
+	"github.com/armadaproject/armada/internal/scheduler/schedulerobjects"
 	"github.com/armadaproject/armada/internal/scheduler/testfixtures"
 	"github.com/armadaproject/armada/pkg/armadaevents"
 )
diff --git a/internal/scheduler/submitcheck_test.go b/internal/scheduler/submitcheck_test.go
index 10413a1d..7e7d9a32 100644
--- a/internal/scheduler/submitcheck_test.go
+++ b/internal/scheduler/submitcheck_test.go
@@ -5,171 +5,107 @@ import (
 	"time"
 
 	"github.com/golang/mock/gomock"
-	"github.com/sirupsen/logrus"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
 	"k8s.io/apimachinery/pkg/util/clock"
 
 	"github.com/armadaproject/armada/internal/armada/configuration"
 	"github.com/armadaproject/armada/internal/common/armadacontext"
-	armadaslices "github.com/armadaproject/armada/internal/common/slices"
 	"github.com/armadaproject/armada/internal/scheduler/jobdb"
 	schedulermocks "github.com/armadaproject/armada/internal/scheduler/mocks"
 	"github.com/armadaproject/armada/internal/scheduler/schedulerobjects"
 	"github.com/armadaproject/armada/internal/scheduler/testfixtures"
-	"github.com/armadaproject/armada/pkg/armadaevents"
 )
 
 func TestSubmitChecker_CheckJobDbJobs(t *testing.T) {
 	defaultTimeout := 15 * time.Minute
 	baseTime := time.Now().UTC()
-	expiredTime := baseTime.Add(-defaultTimeout).Add(-1 * time.Second)
+	// expiredTime := baseTime.Add(-defaultTimeout).Add(-1 * time.Second)
+	smallJob1 := testfixtures.Test1Cpu4GiJob("queue", testfixtures.PriorityClass1)
+	smallJob2 := testfixtures.Test1Cpu4GiJob("queue", testfixtures.PriorityClass1)
+	largeJob1 := testfixtures.Test32Cpu256GiJob("queue", testfixtures.PriorityClass1)
+
+	// This Gang job will fit
+	smallGangJob := testfixtures.
+		WithGangAnnotationsJobs(testfixtures.N1Cpu4GiJobs("queue", testfixtures.PriorityClass1, 2))
+
+	// This gang job doesn't fit  as we only have room for three of these joobs
+	largeGangJob := testfixtures.
+		WithGangAnnotationsJobs(testfixtures.N1Cpu4GiJobs("queue", testfixtures.PriorityClass1, 4))
 
 	tests := map[string]struct {
 		executorTimout time.Duration
 		config         configuration.SchedulingConfig
 		executors      []*schedulerobjects.Executor
-		job            *jobdb.Job
-		expectPass     bool
+		jobs           []*jobdb.Job
+		expectedResult map[string]bool
 	}{
-		"one job schedules": {
+		"One job schedulable": {
 			executorTimout: defaultTimeout,
 			config:         testfixtures.TestSchedulingConfig(),
 			executors:      []*schedulerobjects.Executor{testfixtures.TestExecutor(baseTime)},
-			job:            testfixtures.Test1Cpu4GiJob("queue", testfixtures.PriorityClass1),
-			expectPass:     true,
+			jobs:           []*jobdb.Job{smallJob1},
+			expectedResult: map[string]bool{smallJob1.Id(): true},
 		},
-		"no jobs schedule due to resources": {
+		"Two jobs schedules": {
 			executorTimout: defaultTimeout,
 			config:         testfixtures.TestSchedulingConfig(),
 			executors:      []*schedulerobjects.Executor{testfixtures.TestExecutor(baseTime)},
-			job:            testfixtures.Test32Cpu256GiJob("queue", testfixtures.PriorityClass1),
-			expectPass:     false,
+			jobs:           []*jobdb.Job{smallJob1, smallJob2},
+			expectedResult: map[string]bool{smallJob1.Id(): true, smallJob2.Id(): true},
 		},
-		"no jobs schedule due to selector": {
+		"One job schedulable, one not due to resources": {
 			executorTimout: defaultTimeout,
 			config:         testfixtures.TestSchedulingConfig(),
 			executors:      []*schedulerobjects.Executor{testfixtures.TestExecutor(baseTime)},
-			job:            testfixtures.WithNodeSelectorJob(map[string]string{"foo": "bar"}, testfixtures.Test1Cpu4GiJob("queue", testfixtures.PriorityClass1)),
-			expectPass:     false,
+			jobs:           []*jobdb.Job{smallJob1, largeJob1},
+			expectedResult: map[string]bool{smallJob1.Id(): true, largeJob1.Id(): false},
 		},
-		"no jobs schedule due to executor timeout": {
+		"No jobs schedulable due to resources": {
 			executorTimout: defaultTimeout,
 			config:         testfixtures.TestSchedulingConfig(),
-			executors:      []*schedulerobjects.Executor{testfixtures.TestExecutor(expiredTime)},
-			job:            testfixtures.Test1Cpu4GiJob("queue", testfixtures.PriorityClass1),
-			expectPass:     false,
-		},
-		"multiple executors, 1 expired": {
-			executorTimout: defaultTimeout,
-			config:         testfixtures.TestSchedulingConfig(),
-			executors:      []*schedulerobjects.Executor{testfixtures.TestExecutor(expiredTime), testfixtures.TestExecutor(baseTime)},
-			job:            testfixtures.Test1Cpu4GiJob("queue", testfixtures.PriorityClass1),
-			expectPass:     true,
-		},
-	}
-	for name, tc := range tests {
-		t.Run(name, func(t *testing.T) {
-			ctx, cancel := armadacontext.WithTimeout(armadacontext.Background(), 5*time.Second)
-			defer cancel()
-
-			ctrl := gomock.NewController(t)
-			mockExecutorRepo := schedulermocks.NewMockExecutorRepository(ctrl)
-			mockExecutorRepo.EXPECT().GetExecutors(ctx).Return(tc.executors, nil).AnyTimes()
-			fakeClock := clock.NewFakeClock(baseTime)
-			submitCheck := NewSubmitChecker(tc.executorTimout, tc.config, mockExecutorRepo, testfixtures.TestResourceListFactory)
-			submitCheck.clock = fakeClock
-			submitCheck.updateExecutors(ctx)
-			isSchedulable, reason := submitCheck.CheckJobDbJobs([]*jobdb.Job{tc.job})
-			assert.Equal(t, tc.expectPass, isSchedulable)
-			if !tc.expectPass {
-				assert.NotEqual(t, "", reason)
-			}
-			logrus.Info(reason)
-		})
-	}
-}
-
-func TestSubmitChecker_TestCheckApiJobs(t *testing.T) {
-	defaultTimeout := 15 * time.Minute
-	testfixtures.BaseTime = time.Now().UTC()
-	expiredTime := testfixtures.BaseTime.Add(-defaultTimeout).Add(-1 * time.Second)
-
-	tests := map[string]struct {
-		executorTimout time.Duration
-		config         configuration.SchedulingConfig
-		executors      []*schedulerobjects.Executor
-		jobs           []*armadaevents.SubmitJob
-		expectPass     bool
-	}{
-		"one job schedules": {
-			executorTimout: defaultTimeout,
-			config:         testfixtures.TestSchedulingConfig(),
-			executors:      []*schedulerobjects.Executor{testfixtures.TestExecutor(testfixtures.BaseTime)},
-			jobs:           []*armadaevents.SubmitJob{testfixtures.Test1CoreSubmitMsg()},
-			expectPass:     true,
-		},
-		"multiple jobs schedule": {
-			executorTimout: defaultTimeout,
-			config:         testfixtures.TestSchedulingConfig(),
-			executors:      []*schedulerobjects.Executor{testfixtures.TestExecutor(testfixtures.BaseTime)},
-			jobs:           []*armadaevents.SubmitJob{testfixtures.Test1CoreSubmitMsg(), testfixtures.Test1CoreSubmitMsg()},
-			expectPass:     true,
-		},
-		"first job schedules, second doesn't": {
-			executorTimout: defaultTimeout,
-			config:         testfixtures.TestSchedulingConfig(),
-			executors:      []*schedulerobjects.Executor{testfixtures.TestExecutor(testfixtures.BaseTime)},
-			jobs:           []*armadaevents.SubmitJob{testfixtures.Test1CoreSubmitMsg(), testfixtures.Test100CoreSubmitMsg()},
-			expectPass:     false,
-		},
-		"no jobs schedule due to resources": {
-			executorTimout: defaultTimeout,
-			config:         testfixtures.TestSchedulingConfig(),
-			executors:      []*schedulerobjects.Executor{testfixtures.TestExecutor(testfixtures.BaseTime)},
-			jobs:           []*armadaevents.SubmitJob{testfixtures.Test100CoreSubmitMsg()},
-			expectPass:     false,
-		},
-		"no jobs schedule due to selector": {
-			executorTimout: defaultTimeout,
-			config:         testfixtures.TestSchedulingConfig(),
-			executors:      []*schedulerobjects.Executor{testfixtures.TestExecutor(testfixtures.BaseTime)},
-			jobs:           []*armadaevents.SubmitJob{testfixtures.Test1CoreSubmitMsgWithNodeSelector(map[string]string{"foo": "bar"})},
-			expectPass:     false,
-		},
-		"no jobs schedule due to executor timeout": {
-			executorTimout: defaultTimeout,
-			config:         testfixtures.TestSchedulingConfig(),
-			executors:      []*schedulerobjects.Executor{testfixtures.TestExecutor(expiredTime)},
-			jobs:           []*armadaevents.SubmitJob{testfixtures.Test1CoreSubmitMsg()},
-			expectPass:     false,
+			executors:      []*schedulerobjects.Executor{testfixtures.TestExecutor(baseTime)},
+			jobs:           []*jobdb.Job{largeJob1},
+			expectedResult: map[string]bool{largeJob1.Id(): false},
 		},
-		"multiple executors, 1 expired": {
+		"No jobs schedulable due to selector": {
 			executorTimout: defaultTimeout,
 			config:         testfixtures.TestSchedulingConfig(),
-			executors:      []*schedulerobjects.Executor{testfixtures.TestExecutor(expiredTime), testfixtures.TestExecutor(testfixtures.BaseTime)},
-			jobs:           []*armadaevents.SubmitJob{testfixtures.Test1CoreSubmitMsg()},
-			expectPass:     true,
+			executors:      []*schedulerobjects.Executor{testfixtures.TestExecutor(baseTime)},
+			jobs:           []*jobdb.Job{testfixtures.WithNodeSelectorJob(map[string]string{"foo": "bar"}, smallJob1)},
+			expectedResult: map[string]bool{smallJob1.Id(): false},
 		},
-		"gang job all jobs fit": {
+		"Gang Schedules": {
 			executorTimout: defaultTimeout,
 			config:         testfixtures.TestSchedulingConfig(),
-			executors:      []*schedulerobjects.Executor{testfixtures.TestExecutor(testfixtures.BaseTime)},
-			jobs:           testfixtures.TestNSubmitMsgGang(5),
-			expectPass:     true,
+			executors:      []*schedulerobjects.Executor{testfixtures.TestExecutor(baseTime)},
+			jobs:           smallGangJob,
+			expectedResult: map[string]bool{smallGangJob[0].Id(): true, smallGangJob[1].Id(): true},
 		},
-		"gang job all jobs don't fit": {
+		"Individual jobs fit but gang doesn't": {
 			executorTimout: defaultTimeout,
 			config:         testfixtures.TestSchedulingConfig(),
-			executors:      []*schedulerobjects.Executor{testfixtures.TestExecutor(testfixtures.BaseTime)},
-			jobs:           testfixtures.TestNSubmitMsgGang(100),
-			expectPass:     false,
+			executors:      []*schedulerobjects.Executor{testfixtures.TestExecutor(baseTime)},
+			jobs:           largeGangJob,
+			expectedResult: map[string]bool{
+				largeGangJob[0].Id(): false,
+				largeGangJob[1].Id(): false,
+				largeGangJob[2].Id(): false,
+				largeGangJob[3].Id(): false,
+			},
 		},
-		"Less than min cardinality gang jobs in a batch skips submit check": {
+		"One job fits, one gang doesn't, out of order": {
 			executorTimout: defaultTimeout,
 			config:         testfixtures.TestSchedulingConfig(),
-			executors:      []*schedulerobjects.Executor{testfixtures.TestExecutor(testfixtures.BaseTime)},
-			jobs:           testfixtures.TestNSubmitMsgGangLessThanMinCardinality(5),
-			expectPass:     true,
+			executors:      []*schedulerobjects.Executor{testfixtures.TestExecutor(baseTime)},
+			jobs:           []*jobdb.Job{largeGangJob[0], smallJob1, largeGangJob[1], largeGangJob[2], largeGangJob[3]},
+			expectedResult: map[string]bool{
+				largeGangJob[0].Id(): false,
+				largeGangJob[1].Id(): false,
+				largeGangJob[2].Id(): false,
+				largeGangJob[3].Id(): false,
+				smallJob1.Id():       true,
+			},
 		},
 	}
 	for name, tc := range tests {
@@ -180,22 +116,18 @@ func TestSubmitChecker_TestCheckApiJobs(t *testing.T) {
 			ctrl := gomock.NewController(t)
 			mockExecutorRepo := schedulermocks.NewMockExecutorRepository(ctrl)
 			mockExecutorRepo.EXPECT().GetExecutors(ctx).Return(tc.executors, nil).AnyTimes()
-			fakeClock := clock.NewFakeClock(testfixtures.BaseTime)
-			submitCheck := NewSubmitChecker(tc.executorTimout, tc.config, mockExecutorRepo, testfixtures.TestResourceListFactory)
+			fakeClock := clock.NewFakeClock(baseTime)
+			submitCheck := NewSubmitChecker(tc.config, mockExecutorRepo, testfixtures.TestResourceListFactory)
 			submitCheck.clock = fakeClock
 			submitCheck.updateExecutors(ctx)
-			events := armadaslices.Map(tc.jobs, func(s *armadaevents.SubmitJob) *armadaevents.EventSequence_Event {
-				return &armadaevents.EventSequence_Event{
-					Event: &armadaevents.EventSequence_Event_SubmitJob{SubmitJob: s},
-				}
-			})
-			es := &armadaevents.EventSequence{Events: events}
-			result, msg := submitCheck.CheckApiJobs(es, testfixtures.TestDefaultPriorityClass)
-			assert.Equal(t, tc.expectPass, result)
-			if !tc.expectPass {
-				assert.NotEqual(t, "", msg)
+			results, err := submitCheck.Check(ctx, tc.jobs)
+			require.NoError(t, err)
+			require.Equal(t, len(tc.expectedResult), len(results))
+			for id, expectedSchedulable := range tc.expectedResult {
+				actualResult, ok := results[id]
+				require.True(t, ok)
+				assert.Equal(t, expectedSchedulable, actualResult.isSchedulable)
 			}
-			logrus.Info(msg)
 		})
 	}
 }
diff --git a/internal/scheduler/testfixtures/testfixtures.go b/internal/scheduler/testfixtures/testfixtures.go
index b9e5e0ba..010a3428 100644
--- a/internal/scheduler/testfixtures/testfixtures.go
+++ b/internal/scheduler/testfixtures/testfixtures.go
@@ -349,6 +349,7 @@ func WithNodeSelectorJobs(selector map[string]string, jobs []*jobdb.Job) []*jobd
 }
 
 func WithNodeSelectorJob(selector map[string]string, job *jobdb.Job) *jobdb.Job {
+	job = job.DeepCopy()
 	for _, req := range job.JobSchedulingInfo().GetObjectRequirements() {
 		req.GetPodRequirements().NodeSelector = maps.Clone(selector)
 	}
@@ -464,6 +465,7 @@ func TestJob(queue string, jobId ulid.ULID, priorityClassName string, req *sched
 		false,
 		false,
 		created,
+		false,
 	)
 }
 
@@ -818,6 +820,7 @@ func TestQueuedJobDbJob() *jobdb.Job {
 		false,
 		false,
 		BaseTime.UnixNano(),
+		false,
 	)
 }
 
diff --git a/internal/scheduleringester/instructions_test.go b/internal/scheduleringester/instructions_test.go
index 5e5cd945..8bc783ff 100644
--- a/internal/scheduleringester/instructions_test.go
+++ b/internal/scheduleringester/instructions_test.go
@@ -187,6 +187,12 @@ func TestConvertSequence(t *testing.T) {
 				}},
 			},
 		},
+		"SubmitChecked": {
+			events: []*armadaevents.EventSequence_Event{f.JobValidated},
+			expected: []DbOperation{
+				MarkJobsValidated{f.JobIdString: true},
+			},
+		},
 		"PositionMarker": {
 			events: []*armadaevents.EventSequence_Event{f.PartitionMarker},
 			expected: []DbOperation{
diff --git a/internal/scheduleringester/schedulerdb_test.go b/internal/scheduleringester/schedulerdb_test.go
index c3c50af5..4bd28b4c 100644
--- a/internal/scheduleringester/schedulerdb_test.go
+++ b/internal/scheduleringester/schedulerdb_test.go
@@ -44,6 +44,16 @@ func TestWriteOps(t *testing.T) {
 				jobIds[3]: &schedulerdb.Job{JobID: jobIds[3], JobSet: "set2"},
 			},
 		}},
+		"Submit Check": {Ops: []DbOperation{
+			InsertJobs{
+				jobIds[0]: &schedulerdb.Job{JobID: jobIds[0], JobSet: "set1"},
+				jobIds[1]: &schedulerdb.Job{JobID: jobIds[1], JobSet: "set2"},
+			},
+			MarkJobsValidated{
+				jobIds[0]: true,
+				jobIds[1]: true,
+			},
+		}},
 		"InsertRuns": {Ops: []DbOperation{
 			InsertJobs{
 				jobIds[0]: &schedulerdb.Job{JobID: jobIds[0], Queue: testQueueName, JobSet: "set1"},
@@ -751,6 +761,21 @@ func assertOpSuccess(t *testing.T, schedulerDb *SchedulerDb, serials map[string]
 			assert.Equal(t, expectedMarker.PartitionID, actualMarker.PartitionID)
 			assert.Equal(t, expectedMarker.Created, actualMarker.Created)
 		}
+	case MarkJobsValidated:
+		jobs, err := selectNewJobs(ctx, serials["jobs"])
+		if err != nil {
+			return errors.WithStack(err)
+		}
+		numChanged := 0
+		jobIds := make([]string, 0)
+		for _, job := range jobs {
+			if _, ok := expected[job.JobID]; ok {
+				assert.True(t, job.Validated)
+				numChanged++
+				jobIds = append(jobIds, job.JobID)
+			}
+		}
+		assert.Equal(t, len(expected), numChanged)
 	default:
 		return errors.Errorf("received unexpected op %+v", op)
 	}