Remove pre-1.0 production governance surfaces

This commit is contained in:
npc0-hue
2026-08-21 00:06:00 +08:00
parent a7e2e4c6c0
commit da6c8d607e
30 changed files with 89 additions and 429 deletions
+2 -2
View File
@@ -114,9 +114,9 @@ func (svc *CoreService) InvokeAIForSession(sessionID string, request domain.AIIn
if request.ServerInstanceID == "" {
return domain.AIInvocationResponse{}, validationError("serverInstanceId is required for AI config recommendations")
}
svc.productionMu.Lock()
svc.pluginOperationsMu.Lock()
preview, persistErr := svc.persistAIConfigDiff(user.ID, provider, request, result)
svc.productionMu.Unlock()
svc.pluginOperationsMu.Unlock()
if persistErr != nil {
return domain.AIInvocationResponse{}, persistErr
}
+1 -1
View File
@@ -182,7 +182,7 @@ func TestCoreServiceDoesNotPrebindLegacyRunBeforeDistributionBuild(t *testing.T)
func TestCoreServiceDistributionBuildIgnoresStaleRunEndpoint(t *testing.T) {
svc, session, instance := newDistributionTestFixture(t)
svc.now = func() time.Time { return fixedTime.Add(capacityHeartbeatStaleAfter + time.Second) }
svc.now = func() time.Time { return fixedTime.Add(runHeartbeatStaleAfter + time.Second) }
distribution, err := svc.GenerateRunDistributionForSession(session, domain.RunDistributionGenerateRequest{
ServerInstanceID: instance.ID,
+1 -1
View File
@@ -284,7 +284,7 @@ func (svc *CoreService) CompleteRunJob(result domain.RunJobResult) (domain.RunJo
if err := svc.projectClientManagerLifecycleResult(job, stamp); err != nil {
return domain.RunJobResultResult{}, err
}
if err := svc.projectProductionOpsJobResult(job, stamp); err != nil {
if err := svc.projectPluginOperationsJobResult(job, stamp); err != nil {
return domain.RunJobResultResult{}, err
}
return domain.RunJobResultResult{Accepted: true, Job: assignmentFromJob(job, result.LeaseToken), ServerTime: stamp}, nil
@@ -70,8 +70,8 @@ func (svc *CoreService) RunPluginLifecycleForSession(sessionID string, request d
return svc.pluginLifecycleDenied(user.ID, instance, plugin, request, "plugin is not compatible with the assigned endpoint platform")
}
svc.productionMu.Lock()
defer svc.productionMu.Unlock()
svc.pluginOperationsMu.Lock()
defer svc.pluginOperationsMu.Unlock()
installationID := pluginLifecycleInstallationID(request.PluginID, request.ServerInstanceID)
installation, getErr := svc.store.PluginLifecycles().Get(installationID)
if errors.Is(getErr, repo.ErrNotFound) {
@@ -156,8 +156,8 @@ func (svc *CoreService) ApproveAIConfigDiffForSession(sessionID string, request
if err != nil {
return domain.AIConfigDiffApprovalResult{}, err
}
svc.productionMu.Lock()
defer svc.productionMu.Unlock()
svc.pluginOperationsMu.Lock()
defer svc.pluginOperationsMu.Unlock()
preview, err := svc.store.AIConfigDiffs().Get(request.DiffID)
if err != nil {
return domain.AIConfigDiffApprovalResult{}, err
@@ -206,12 +206,12 @@ func (svc *CoreService) ApproveAIConfigDiffForSession(sessionID string, request
return domain.CopyAIConfigDiffApprovalResult(domain.AIConfigDiffApprovalResult{Preview: preview, Dispatch: dispatch}), nil
}
func (svc *CoreService) projectProductionOpsJobResult(job domain.Job, stamp time.Time) error {
func (svc *CoreService) projectPluginOperationsJobResult(job domain.Job, stamp time.Time) error {
if !strings.HasPrefix(job.ID, "job-plugin-lifecycle-") || job.ExecutionInput.LifecycleOperation == "" || job.ExecutionInput.PluginID == "" {
return nil
}
svc.productionMu.Lock()
defer svc.productionMu.Unlock()
svc.pluginOperationsMu.Lock()
defer svc.pluginOperationsMu.Unlock()
installation, err := svc.store.PluginLifecycles().Get(pluginLifecycleInstallationID(job.ExecutionInput.PluginID, job.ServerInstanceID))
if err != nil {
return err
@@ -288,8 +288,8 @@ func (svc *CoreService) getServerConfigForUser(userID, serverInstanceID string)
}
func (svc *CoreService) pluginLifecycleDenied(actorID string, instance domain.ServerInstance, plugin domain.GamePlugin, request domain.PluginLifecycleRequest, reason string) (domain.PluginLifecycleResult, error) {
svc.productionMu.Lock()
defer svc.productionMu.Unlock()
svc.pluginOperationsMu.Lock()
defer svc.pluginOperationsMu.Unlock()
stamp := svc.now()
installation := domain.PluginLifecycleInstallation{ID: pluginLifecycleInstallationID(plugin.ID, instance.ID), PluginID: plugin.ID, ServerInstanceID: instance.ID, TargetVersion: request.TargetVersion, DesiredState: domain.PluginLifecycleStatePending, CurrentState: domain.PluginLifecycleStateFailed, LastOperation: request.Operation, Compatibility: "incompatible", DependencyState: domain.DependencyStateUnknown, FailureReason: safeBridgeReason(reason), IdempotencyKey: request.IdempotencyKey, CreatedAt: stamp, UpdatedAt: stamp}
if existing, err := svc.store.PluginLifecycles().Get(installation.ID); err == nil {
@@ -308,13 +308,15 @@ func (svc *CoreService) pluginLifecycleDeniedLocked(actorID string, installation
if err := validator.ValidatePluginLifecycleInstallation(installation); err != nil {
return domain.PluginLifecycleResult{}, err
}
if _, err := svc.store.PluginLifecycles().Get(installation.ID); errors.Is(err, repo.ErrNotFound) {
err = svc.store.PluginLifecycles().Create(installation)
} else if err == nil {
err = svc.store.PluginLifecycles().Update(installation)
_, getErr := svc.store.PluginLifecycles().Get(installation.ID)
switch {
case errors.Is(getErr, repo.ErrNotFound):
getErr = svc.store.PluginLifecycles().Create(installation)
case getErr == nil:
getErr = svc.store.PluginLifecycles().Update(installation)
}
if err != nil {
return domain.PluginLifecycleResult{}, err
if getErr != nil {
return domain.PluginLifecycleResult{}, getErr
}
return domain.CopyPluginLifecycleResult(domain.PluginLifecycleResult{Installation: installation, Status: "denied"}), nil
}
@@ -10,7 +10,7 @@ import (
)
func TestPluginLifecycleDispatchIsIdempotentAndRejectsInputDrift(t *testing.T) {
svc, session, instance := newProductionOpsFixture(t)
svc, session, instance := newPluginOperationsFixture(t)
request := domain.PluginLifecycleRequest{PluginID: instance.PluginID, ServerInstanceID: instance.ID, Operation: domain.PluginLifecycleOperationInstall, TargetVersion: "1.0.0", IdempotencyKey: "plugin-install-v1"}
first, err := svc.RunPluginLifecycleForSession(session, request)
if err != nil {
@@ -35,7 +35,7 @@ func TestPluginLifecycleDispatchIsIdempotentAndRejectsInputDrift(t *testing.T) {
}
func TestPluginLifecycleBridgeDispatchesBoundedJob(t *testing.T) {
svc, session, instance := newProductionOpsFixture(t)
svc, session, instance := newPluginOperationsFixture(t)
plugin, err := svc.store.GamePlugins().Get(instance.PluginID)
if err != nil {
t.Fatalf("get plugin: %v", err)
@@ -73,7 +73,7 @@ func TestPluginLifecycleBridgeDispatchesBoundedJob(t *testing.T) {
}
func TestAIConfigRecommendationRequiresApprovalAndRejectsStaleRevision(t *testing.T) {
svc, session, instance := newProductionOpsFixture(t)
svc, session, instance := newPluginOperationsFixture(t)
provider, err := svc.CreateAIProvider(domain.AIProvider{ID: "ai-local", Name: "Local AI", Kind: domain.AIProviderKindOllama, BaseURL: "http://127.0.0.1:11434/v1", Models: []string{"test-model"}, DefaultModel: "test-model", RelayMode: domain.AIRelayModeLocal, TimeoutMS: 1000, Status: domain.AIProviderStatusActive, RedactionPolicy: "strict"})
if err != nil {
t.Fatalf("create provider: %v", err)
@@ -122,7 +122,7 @@ func TestAIConfigRecommendationRequiresApprovalAndRejectsStaleRevision(t *testin
}
}
func newProductionOpsFixture(t *testing.T) (*CoreService, string, domain.ServerInstance) {
func newPluginOperationsFixture(t *testing.T) (*CoreService, string, domain.ServerInstance) {
t.Helper()
svc := newTestCoreService()
plugin, endpoint := createPluginAndRunEndpoint(t, svc)
+13 -3
View File
@@ -23,7 +23,10 @@ var (
ErrForbidden = errors.New("forbidden")
)
const ServerDeletionForceConfirmation = "FORCE DELETE"
const (
ServerDeletionForceConfirmation = "FORCE DELETE"
runHeartbeatStaleAfter = 2 * time.Minute
)
type ForbiddenError struct {
Reason string
@@ -234,7 +237,7 @@ type CoreService struct {
artifactTransfers map[string]domain.ArtifactTransferSession
artifactPayloads map[string][]byte
artifactTransferSeq uint64
productionMu sync.Mutex
pluginOperationsMu sync.Mutex
sourceRCONCommands *sourceRCONCommandBroker
aiProviderClient AIProviderClient
secretEnvelope SecretEnvelope
@@ -2654,7 +2657,14 @@ func (svc *CoreService) runEndpointHeartbeatCurrent(endpoint domain.RunEndpoint)
if endpoint.LastHeartbeatAt.IsZero() {
return false
}
return !svc.now().After(endpoint.LastHeartbeatAt.Add(capacityHeartbeatStaleAfter))
return !svc.now().After(endpoint.LastHeartbeatAt.Add(runHeartbeatStaleAfter))
}
func maxInt(a, b int) int {
if a > b {
return a
}
return b
}
func validateJobServerTarget(job domain.Job, instance domain.ServerInstance, plugin domain.GamePlugin) error {