This commit is contained in:
2026-05-14 23:30:34 +03:00
parent 26cb65e936
commit 04c46042d9
239 changed files with 34102 additions and 438 deletions
+2644
View File
File diff suppressed because it is too large Load Diff
@@ -2,6 +2,7 @@ package main
import (
"context"
"encoding/json"
"flag"
"fmt"
"log"
@@ -58,6 +59,14 @@ func main() {
if err := runUpdateLoop(ctx, os.Args[2:]); err != nil {
log.Fatalf("update-loop failed: %v", err)
}
case "monitor-loop":
if err := runMonitorLoop(ctx, os.Args[2:]); err != nil {
log.Fatalf("monitor-loop failed: %v", err)
}
case "monitor-once":
if err := runMonitorOnce(ctx, os.Args[2:]); err != nil {
log.Fatalf("monitor-once failed: %v", err)
}
case "install-updater":
if err := runInstallUpdater(ctx, os.Args[2:]); err != nil {
log.Fatalf("install-updater failed: %v", err)
@@ -288,6 +297,9 @@ func runInstall(ctx context.Context, args []string) error {
return err
}
fmt.Print(result.Unit)
if result.MonitorUnit != "" {
fmt.Print(result.MonitorUnit)
}
}
return nil
}
@@ -304,7 +316,7 @@ func runInstall(ctx context.Context, args []string) error {
if err != nil {
return err
}
fmt.Printf("updater_service=%s unit=%s binary=%s started=%t\n", serviceResult.UnitName, serviceResult.UnitPath, serviceResult.BinaryPath, serviceResult.Started)
fmt.Printf("updater_service=%s unit=%s binary=%s started=%t monitor_service=%s\n", serviceResult.UnitName, serviceResult.UnitPath, serviceResult.BinaryPath, serviceResult.Started, serviceResult.MonitorUnitName)
}
fmt.Println("next: approve the join request in the platform admin panel, then the node-agent will finish bootstrap and start heartbeats")
return nil
@@ -429,6 +441,75 @@ func runUpdateLoop(ctx context.Context, args []string) error {
return (hostagent.DockerManager{}).RunUpdateLoop(ctx, cfg)
}
func runMonitorLoop(ctx context.Context, args []string) error {
cfg, err := parseMonitor(args)
if err != nil {
return err
}
return hostagent.RunMonitorLoop(ctx, cfg)
}
func runMonitorOnce(ctx context.Context, args []string) error {
cfg, err := parseMonitor(args)
if err != nil {
return err
}
cfg.MaxRuns = 1
result := hostagent.RunMonitorOnce(ctx, cfg)
if err := json.NewEncoder(os.Stdout).Encode(result); err != nil {
return err
}
return nil
}
func parseMonitor(args []string) (hostagent.MonitorConfig, error) {
fs := flag.NewFlagSet("monitor-loop", flag.ContinueOnError)
cfg := hostagent.MonitorConfig{}
var intervalSeconds int
var initialDelaySeconds int
var maxRuns int
var restartCooldownSeconds int
var staleRestartingSeconds int
var tmpMinAgeMinutes int
watchContainers := repeatedFlag{}
fs.StringVar(&cfg.BackendURL, "backend-url", getenv("RAP_BACKEND_URL", ""), "Control Plane API base URL used for monitor status reports.")
fs.StringVar(&cfg.ClusterID, "cluster-id", getenv("RAP_CLUSTER_ID", ""), "Cluster ID.")
fs.StringVar(&cfg.NodeID, "node-id", getenv("RAP_NODE_ID", ""), "Already enrolled node ID.")
fs.StringVar(&cfg.StateDir, "state-dir", getenv("RAP_NODE_STATE_DIR", hostagent.DefaultStateDir), "Host path containing node-agent identity.json.")
fs.StringVar(&cfg.Product, "product", getenv("RAP_MONITOR_PRODUCT", hostagent.DefaultMonitorProduct), "Status product name.")
fs.StringVar(&cfg.CurrentVersion, "current-version", getenv("RAP_HOST_AGENT_VERSION", agent.Version), "Current rap-host-agent version.")
fs.StringVar(&cfg.DockerBinary, "docker-binary", getenv("RAP_DOCKER_BINARY", "docker"), "Docker CLI binary.")
fs.StringVar(&cfg.DiskPath, "disk-path", getenv("RAP_MONITOR_DISK_PATH", "/"), "Filesystem path used for disk usage checks.")
fs.StringVar(&cfg.TmpDir, "tmp-dir", getenv("RAP_MONITOR_TMP_DIR", "/tmp"), "Temporary directory cleaned under pressure.")
fs.StringVar(&cfg.StatusFile, "status-file", getenv("RAP_MONITOR_STATUS_FILE", ""), "Optional JSON status file written after every run.")
fs.IntVar(&intervalSeconds, "interval-seconds", getenvInt("RAP_MONITOR_INTERVAL_SECONDS", hostagent.DefaultMonitorIntervalSeconds), "Seconds between monitor checks.")
fs.IntVar(&initialDelaySeconds, "initial-delay-seconds", getenvInt("RAP_MONITOR_INITIAL_DELAY_SECONDS", 0), "Seconds to wait before first monitor check.")
fs.IntVar(&maxRuns, "max-runs", getenvInt("RAP_MONITOR_MAX_RUNS", 0), "Maximum monitor iterations. Use 0 to run until stopped.")
fs.IntVar(&cfg.DiskWarnPercent, "disk-warn-percent", getenvInt("RAP_MONITOR_DISK_WARN_PERCENT", hostagent.DefaultMonitorDiskWarnPercent), "Disk used percent that reports warning.")
fs.IntVar(&cfg.DiskCleanupPercent, "disk-cleanup-percent", getenvInt("RAP_MONITOR_DISK_CLEANUP_PERCENT", hostagent.DefaultMonitorDiskCleanupPercent), "Disk used percent that triggers cleanup.")
fs.IntVar(&cfg.DiskCriticalPercent, "disk-critical-percent", getenvInt("RAP_MONITOR_DISK_CRITICAL_PERCENT", hostagent.DefaultMonitorDiskCriticalPercent), "Disk used percent that reports failure after cleanup.")
fs.IntVar(&restartCooldownSeconds, "restart-cooldown-seconds", getenvInt("RAP_MONITOR_RESTART_COOLDOWN_SECONDS", hostagent.DefaultMonitorRestartCooldownSec), "Minimum seconds between repeated restarts of the same target.")
fs.IntVar(&staleRestartingSeconds, "stale-restarting-seconds", getenvInt("RAP_MONITOR_STALE_RESTARTING_SECONDS", hostagent.DefaultMonitorStaleRestartingSec), "Seconds after which docker restarting state is considered stuck.")
fs.IntVar(&tmpMinAgeMinutes, "tmp-min-age-minutes", getenvInt("RAP_MONITOR_TMP_MIN_AGE_MINUTES", hostagent.DefaultMonitorTmpMinAgeMinutes), "Minimum age for /tmp rap-* and go-build* cleanup.")
fs.BoolVar(&cfg.RestartContainers, "restart-containers", getenvBool("RAP_MONITOR_RESTART_CONTAINERS", true), "Start/restart watched containers when they are stopped, unhealthy, or stuck restarting.")
fs.BoolVar(&cfg.CleanupDocker, "cleanup-docker", getenvBool("RAP_MONITOR_CLEANUP_DOCKER", true), "Run safe docker prune cleanup when disk is above cleanup threshold.")
fs.Var(&watchContainers, "watch-container", "Docker container to watch and heal; may be repeated.")
if err := fs.Parse(args); err != nil {
return hostagent.MonitorConfig{}, err
}
cfg.WatchContainers = watchContainers
cfg.Interval = time.Duration(intervalSeconds) * time.Second
cfg.InitialDelay = time.Duration(initialDelaySeconds) * time.Second
cfg.MaxRuns = maxRuns
cfg.RestartCooldown = time.Duration(restartCooldownSeconds) * time.Second
cfg.StaleRestartingAfter = time.Duration(staleRestartingSeconds) * time.Second
cfg.TmpMinAge = time.Duration(tmpMinAgeMinutes) * time.Minute
cfg.Logf = func(format string, args ...any) {
fmt.Printf(format+"\n", args...)
}
return cfg, nil
}
func firstNonEmptyLocal(values ...string) string {
for _, value := range values {
if strings.TrimSpace(value) != "" {
@@ -444,6 +525,8 @@ func runInstallUpdater(ctx context.Context, args []string) error {
service := hostagent.UpdateServiceConfig{}
var dryRun bool
var selfUpdater bool
var monitorEnabled bool
monitorContainers := repeatedFlag{}
fs.StringVar(&runtimeCfg.BackendURL, "backend-url", getenv("RAP_BACKEND_URL", ""), "Control Plane API base URL.")
fs.StringVar(&runtimeCfg.ClusterID, "cluster-id", getenv("RAP_CLUSTER_ID", ""), "Cluster ID.")
fs.StringVar(&runtimeCfg.ContainerName, "container-name", getenv("RAP_NODE_AGENT_CONTAINER", hostagent.DefaultContainerName), "Docker container name to update.")
@@ -456,6 +539,14 @@ func runInstallUpdater(ctx context.Context, args []string) error {
fs.IntVar(&service.HealthTimeoutSec, "health-timeout-seconds", getenvInt("RAP_UPDATE_HEALTH_TIMEOUT_SECONDS", 30), "Updated container running-state timeout in seconds.")
fs.StringVar(&service.BinaryInstallPath, "binary-path", getenv("RAP_HOST_AGENT_BINARY_PATH", hostagent.DefaultHostAgentInstallPath), "Persistent host path for rap-host-agent binary used by the service.")
fs.BoolVar(&selfUpdater, "self-updater-enabled", getenvBool("RAP_HOST_AGENT_SELF_UPDATE_ENABLED", true), "Install and start one global host-agent binary self-updater service.")
fs.BoolVar(&monitorEnabled, "monitor-enabled", getenvBool("RAP_HOST_AGENT_MONITOR_ENABLED", true), "Install and start the local host monitor service.")
fs.IntVar(&service.MonitorIntervalSec, "monitor-interval-seconds", getenvInt("RAP_MONITOR_INTERVAL_SECONDS", hostagent.DefaultMonitorIntervalSeconds), "Seconds between monitor checks.")
fs.StringVar(&service.MonitorStatusFile, "monitor-status-file", getenv("RAP_MONITOR_STATUS_FILE", ""), "Optional JSON status file written by the monitor.")
fs.IntVar(&service.MonitorDiskWarn, "monitor-disk-warn-percent", getenvInt("RAP_MONITOR_DISK_WARN_PERCENT", hostagent.DefaultMonitorDiskWarnPercent), "Disk used percent that reports warning.")
fs.IntVar(&service.MonitorDiskCleanup, "monitor-disk-cleanup-percent", getenvInt("RAP_MONITOR_DISK_CLEANUP_PERCENT", hostagent.DefaultMonitorDiskCleanupPercent), "Disk used percent that triggers cleanup.")
fs.IntVar(&service.MonitorDiskCritical, "monitor-disk-critical-percent", getenvInt("RAP_MONITOR_DISK_CRITICAL_PERCENT", hostagent.DefaultMonitorDiskCriticalPercent), "Disk used percent that reports failure after cleanup.")
fs.BoolVar(&service.MonitorCleanupDocker, "monitor-cleanup-docker", getenvBool("RAP_MONITOR_CLEANUP_DOCKER", true), "Run safe docker prune cleanup when disk is above cleanup threshold.")
fs.Var(&monitorContainers, "monitor-container", "Extra Docker container watched by monitor; may be repeated.")
fs.BoolVar(&dryRun, "dry-run", false, "Print the systemd unit without installing it.")
if err := fs.Parse(args); err != nil {
return err
@@ -465,6 +556,8 @@ func runInstallUpdater(ctx context.Context, args []string) error {
service.DryRun = dryRun
service.InstallSelfUpdater = selfUpdater
service.SelfUpdateVersion = agent.Version
service.InstallMonitor = monitorEnabled
service.MonitorContainers = monitorContainers
result, err := (hostagent.DockerManager{}).InstallUpdateService(ctx, service)
if err != nil {
return err
@@ -474,9 +567,12 @@ func runInstallUpdater(ctx context.Context, args []string) error {
if result.SelfUnit != "" {
fmt.Print(result.SelfUnit)
}
if result.MonitorUnit != "" {
fmt.Print(result.MonitorUnit)
}
return nil
}
fmt.Printf("updater_service=%s unit=%s binary=%s started=%t self_updater=%s\n", result.UnitName, result.UnitPath, result.BinaryPath, result.Started, result.SelfUnitName)
fmt.Printf("updater_service=%s unit=%s binary=%s started=%t self_updater=%s monitor_service=%s\n", result.UnitName, result.UnitPath, result.BinaryPath, result.Started, result.SelfUnitName, result.MonitorUnitName)
return nil
}
@@ -572,6 +668,7 @@ func parseInstall(args []string) (installCommandConfig, error) {
var installToken string
var autoUpdateEnabled bool
autoUpdate := hostagent.UpdateServiceConfig{}
monitorContainers := repeatedFlag{}
fs.StringVar(&cfg.BackendURL, "backend-url", getenv("RAP_BACKEND_URL", ""), "Control Plane API base URL.")
fs.StringVar(&cfg.ClusterID, "cluster-id", getenv("RAP_CLUSTER_ID", ""), "Cluster ID.")
fs.StringVar(&cfg.JoinToken, "join-token", getenv("RAP_JOIN_TOKEN", ""), "One-time join token for first enrollment.")
@@ -591,6 +688,7 @@ func parseInstall(args []string) (installCommandConfig, error) {
fs.BoolVar(&dryRun, "dry-run", false, "Print the docker command with secrets redacted.")
fs.BoolVar(&autoUpdateEnabled, "auto-update-enabled", getenvBool("RAP_AUTO_UPDATE_ENABLED", true), "Install and start the local update-loop service.")
fs.BoolVar(&autoUpdate.InstallSelfUpdater, "host-agent-self-update-enabled", getenvBool("RAP_HOST_AGENT_SELF_UPDATE_ENABLED", true), "Install and start one global host-agent binary self-updater service.")
fs.BoolVar(&autoUpdate.InstallMonitor, "host-agent-monitor-enabled", getenvBool("RAP_HOST_AGENT_MONITOR_ENABLED", true), "Install and start the local host monitor service.")
fs.StringVar(&autoUpdate.CurrentVersion, "auto-update-current-version", getenv("RAP_NODE_AGENT_VERSION", agent.Version), "Initial node-agent version used by update-loop before the first successful update.")
fs.StringVar(&autoUpdate.SelfUpdateVersion, "host-agent-current-version", getenv("RAP_HOST_AGENT_VERSION", agent.Version), "Initial host-agent binary version used by the self-updater.")
fs.StringVar(&autoUpdate.Channel, "auto-update-channel", getenv("RAP_UPDATE_CHANNEL", ""), "Optional update channel override for update-loop.")
@@ -599,6 +697,12 @@ func parseInstall(args []string) (installCommandConfig, error) {
fs.Float64Var(&autoUpdate.Jitter, "auto-update-jitter", getenvFloat("RAP_UPDATE_JITTER", 0.15), "Update-loop interval jitter, 0..1.")
fs.IntVar(&autoUpdate.HealthTimeoutSec, "auto-update-health-timeout-seconds", getenvInt("RAP_UPDATE_HEALTH_TIMEOUT_SECONDS", 30), "Updated container running-state timeout in seconds.")
fs.StringVar(&autoUpdate.BinaryInstallPath, "auto-update-binary-path", getenv("RAP_HOST_AGENT_BINARY_PATH", hostagent.DefaultHostAgentInstallPath), "Persistent host path for rap-host-agent binary used by the service.")
fs.IntVar(&autoUpdate.MonitorIntervalSec, "monitor-interval-seconds", getenvInt("RAP_MONITOR_INTERVAL_SECONDS", hostagent.DefaultMonitorIntervalSeconds), "Seconds between monitor checks.")
fs.StringVar(&autoUpdate.MonitorStatusFile, "monitor-status-file", getenv("RAP_MONITOR_STATUS_FILE", ""), "Optional JSON status file written by the monitor.")
fs.IntVar(&autoUpdate.MonitorDiskWarn, "monitor-disk-warn-percent", getenvInt("RAP_MONITOR_DISK_WARN_PERCENT", hostagent.DefaultMonitorDiskWarnPercent), "Disk used percent that reports warning.")
fs.IntVar(&autoUpdate.MonitorDiskCleanup, "monitor-disk-cleanup-percent", getenvInt("RAP_MONITOR_DISK_CLEANUP_PERCENT", hostagent.DefaultMonitorDiskCleanupPercent), "Disk used percent that triggers cleanup.")
fs.IntVar(&autoUpdate.MonitorDiskCritical, "monitor-disk-critical-percent", getenvInt("RAP_MONITOR_DISK_CRITICAL_PERCENT", hostagent.DefaultMonitorDiskCriticalPercent), "Disk used percent that reports failure after cleanup.")
fs.BoolVar(&autoUpdate.MonitorCleanupDocker, "monitor-cleanup-docker", getenvBool("RAP_MONITOR_CLEANUP_DOCKER", true), "Run safe docker prune cleanup when disk is above cleanup threshold.")
fs.BoolVar(&cfg.WorkloadSupervisionEnabled, "workload-supervision-enabled", getenvBool("RAP_WORKLOAD_SUPERVISION_ENABLED", false), "Enable node-agent workload status reporting.")
fs.BoolVar(&cfg.MeshSyntheticRuntimeEnabled, "mesh-synthetic-runtime-enabled", getenvBool("RAP_MESH_SYNTHETIC_RUNTIME_ENABLED", false), "Enable synthetic mesh runtime.")
fs.BoolVar(&cfg.MeshProductionForwardingEnabled, "mesh-production-forwarding-enabled", getenvBool("RAP_MESH_PRODUCTION_FORWARDING_ENABLED", false), "Enable production forwarding gate; runtime still fail-closed if unavailable.")
@@ -622,12 +726,14 @@ func parseInstall(args []string) (installCommandConfig, error) {
fs.Var(&extraEnv, "env", "Extra KEY=VALUE env passed to node-agent container; may be repeated.")
fs.Var(&extraRunArg, "docker-run-arg", "Extra raw docker run argument; may be repeated.")
fs.Var(&imageArtifactURL, "image-artifact-url", "Docker image tar artifact URL to docker load before running; may be repeated.")
fs.Var(&monitorContainers, "monitor-container", "Extra Docker container watched by monitor; may be repeated.")
if err := fs.Parse(args); err != nil {
return installCommandConfig{}, err
}
cfg.ExtraEnv = extraEnv
cfg.AdditionalDockerRunArgs = extraRunArg
cfg.ImageArtifactURLs = append(cfg.ImageArtifactURLs, imageArtifactURL...)
autoUpdate.MonitorContainers = monitorContainers
if strings.TrimSpace(profileURL) != "" || strings.TrimSpace(installToken) != "" {
profile, err := hostagent.FetchDockerInstallProfile(context.Background(), hostagent.ProfileRequest{
URL: profileURL,
@@ -738,6 +844,8 @@ func usage() {
rap-host-agent install-updater -backend-url URL -cluster-id ID -state-dir DIR -container-name NAME
rap-host-agent update-host-agent -backend-url URL -cluster-id ID -state-dir DIR
rap-host-agent update-host-agent-loop -backend-url URL -cluster-id ID -state-dir DIR
rap-host-agent monitor-loop -backend-url URL -cluster-id ID -state-dir DIR --watch-container NAME
rap-host-agent monitor-once -backend-url URL -cluster-id ID -state-dir DIR --watch-container NAME
rap-host-agent update -backend-url URL -cluster-id ID -node-id ID [-container-name NAME]
rap-host-agent update-loop -backend-url URL -cluster-id ID -node-id ID [-container-name NAME]
rap-host-agent status [-container-name NAME]`)
@@ -222,6 +222,11 @@ type NodeVPNAssignmentLeaseRenewRequest struct {
TTLSeconds int `json:"ttl_seconds"`
}
type NodeVPNAssignmentLeaseAcquireRequest struct {
TTLSeconds int `json:"ttl_seconds"`
Metadata map[string]any `json:"metadata,omitempty"`
}
type MeshLinkObservationRequest struct {
SourceNodeID string `json:"source_node_id"`
TargetNodeID string `json:"target_node_id"`
@@ -658,6 +663,17 @@ func (c *Client) ReportNodeVPNAssignmentStatus(ctx context.Context, clusterID, n
return c.postJSON(ctx, path, request, nil)
}
func (c *Client) AcquireNodeVPNAssignmentLease(ctx context.Context, clusterID, nodeID, vpnConnectionID string, request NodeVPNAssignmentLeaseAcquireRequest) (*NodeVPNAssignmentLease, error) {
var response struct {
Lease NodeVPNAssignmentLease `json:"lease"`
}
path := fmt.Sprintf("/clusters/%s/nodes/%s/vpn/assignments/%s/lease/acquire", clusterID, nodeID, vpnConnectionID)
if err := c.postJSON(ctx, path, request, &response); err != nil {
return nil, err
}
return &response.Lease, nil
}
func (c *Client) RenewNodeVPNAssignmentLease(ctx context.Context, clusterID, nodeID, vpnConnectionID, leaseID string, request NodeVPNAssignmentLeaseRenewRequest) error {
path := fmt.Sprintf("/clusters/%s/nodes/%s/vpn/assignments/%s/lease/%s/renew", clusterID, nodeID, vpnConnectionID, leaseID)
return c.postJSON(ctx, path, request, nil)
@@ -40,6 +40,10 @@ type Config struct {
MeshSyntheticConfigPath string
MeshPeerEndpointsJSON string
MeshSyntheticRoutesJSON string
RemoteWorkspaceRealAdapterEnabled bool
RemoteWorkspaceRealAdapterCommand string
RemoteWorkspaceRealAdapterArgsJSON string
RemoteWorkspaceRealAdapterWorkDir string
}
func Load(args []string, env map[string]string) (Config, error) {
@@ -73,6 +77,10 @@ func Load(args []string, env map[string]string) (Config, error) {
fs.StringVar(&cfg.MeshSyntheticConfigPath, "mesh-synthetic-config", getEnv(env, "RAP_MESH_SYNTHETIC_CONFIG", ""), "Path to scoped synthetic mesh config snapshot. Preferred over debug JSON env.")
fs.StringVar(&cfg.MeshPeerEndpointsJSON, "mesh-peer-endpoints-json", getEnv(env, "RAP_MESH_PEER_ENDPOINTS_JSON", ""), "JSON object mapping peer node_id to synthetic mesh endpoint URL.")
fs.StringVar(&cfg.MeshSyntheticRoutesJSON, "mesh-synthetic-routes-json", getEnv(env, "RAP_MESH_SYNTHETIC_ROUTES_JSON", ""), "JSON array of synthetic mesh routes for test-only runtime.")
fs.BoolVar(&cfg.RemoteWorkspaceRealAdapterEnabled, "remote-workspace-real-adapter-enabled", getEnvBool(env, "RAP_REMOTE_WORKSPACE_REAL_ADAPTER_ENABLED", false), "Request future real remote workspace adapter supervision. Disabled until the real runtime stage is implemented.")
fs.StringVar(&cfg.RemoteWorkspaceRealAdapterCommand, "remote-workspace-real-adapter-command", getEnv(env, "RAP_REMOTE_WORKSPACE_REAL_ADAPTER_COMMAND", ""), "Future real remote workspace adapter command path. Redacted from status payloads.")
fs.StringVar(&cfg.RemoteWorkspaceRealAdapterArgsJSON, "remote-workspace-real-adapter-args-json", getEnv(env, "RAP_REMOTE_WORKSPACE_REAL_ADAPTER_ARGS_JSON", ""), "Future real remote workspace adapter args JSON. Redacted from status payloads.")
fs.StringVar(&cfg.RemoteWorkspaceRealAdapterWorkDir, "remote-workspace-real-adapter-workdir", getEnv(env, "RAP_REMOTE_WORKSPACE_REAL_ADAPTER_WORKDIR", ""), "Future real remote workspace adapter working directory. Redacted from status payloads.")
heartbeatSeconds := getEnvInt(env, "RAP_HEARTBEAT_INTERVAL_SECONDS", 15)
fs.DurationVar(&cfg.HeartbeatInterval, "heartbeat-interval", time.Duration(heartbeatSeconds)*time.Second, "Heartbeat interval.")
enrollmentPollIntervalSeconds := getEnvInt(env, "RAP_ENROLLMENT_POLL_INTERVAL_SECONDS", 5)
@@ -100,6 +108,9 @@ func Load(args []string, env map[string]string) (Config, error) {
cfg.MeshSyntheticConfigPath = strings.TrimSpace(cfg.MeshSyntheticConfigPath)
cfg.MeshPeerEndpointsJSON = strings.TrimSpace(cfg.MeshPeerEndpointsJSON)
cfg.MeshSyntheticRoutesJSON = strings.TrimSpace(cfg.MeshSyntheticRoutesJSON)
cfg.RemoteWorkspaceRealAdapterCommand = strings.TrimSpace(cfg.RemoteWorkspaceRealAdapterCommand)
cfg.RemoteWorkspaceRealAdapterArgsJSON = strings.TrimSpace(cfg.RemoteWorkspaceRealAdapterArgsJSON)
cfg.RemoteWorkspaceRealAdapterWorkDir = strings.TrimSpace(cfg.RemoteWorkspaceRealAdapterWorkDir)
if cfg.BackendURL == "" {
return Config{}, errors.New("backend URL is required")
}
@@ -34,6 +34,10 @@ func TestLoadConfigFromEnvAndArgs(t *testing.T) {
"RAP_MESH_SYNTHETIC_CONFIG": "/tmp/rap-node/mesh-synthetic.json",
"RAP_MESH_PEER_ENDPOINTS_JSON": `{"node-b":"http://127.0.0.1:19002"}`,
"RAP_MESH_SYNTHETIC_ROUTES_JSON": `[{"route_id":"route-1"}]`,
"RAP_REMOTE_WORKSPACE_REAL_ADAPTER_ENABLED": "true",
"RAP_REMOTE_WORKSPACE_REAL_ADAPTER_COMMAND": " /opt/rap/bin/rdp-worker ",
"RAP_REMOTE_WORKSPACE_REAL_ADAPTER_ARGS_JSON": ` ["--future-probe"] `,
"RAP_REMOTE_WORKSPACE_REAL_ADAPTER_WORKDIR": " /var/lib/rap-node-agent/rdp-worker ",
})
if err != nil {
t.Fatalf("load config: %v", err)
@@ -85,6 +89,12 @@ func TestLoadConfigFromEnvAndArgs(t *testing.T) {
if cfg.MeshPeerEndpointsJSON == "" || cfg.MeshSyntheticRoutesJSON == "" {
t.Fatalf("mesh live synthetic config was not loaded: %+v", cfg)
}
if !cfg.RemoteWorkspaceRealAdapterEnabled ||
cfg.RemoteWorkspaceRealAdapterCommand != "/opt/rap/bin/rdp-worker" ||
cfg.RemoteWorkspaceRealAdapterArgsJSON != `["--future-probe"]` ||
cfg.RemoteWorkspaceRealAdapterWorkDir != "/var/lib/rap-node-agent/rdp-worker" {
t.Fatalf("unexpected remote workspace real adapter config: %+v", cfg)
}
}
func TestLoadConfigDefaultsEnrollmentPollingToNoTimeout(t *testing.T) {
@@ -98,6 +108,12 @@ func TestLoadConfigDefaultsEnrollmentPollingToNoTimeout(t *testing.T) {
if cfg.EnrollmentPollTimeout != 0 {
t.Fatalf("EnrollmentPollTimeout = %s, want no timeout", cfg.EnrollmentPollTimeout)
}
if cfg.RemoteWorkspaceRealAdapterEnabled ||
cfg.RemoteWorkspaceRealAdapterCommand != "" ||
cfg.RemoteWorkspaceRealAdapterArgsJSON != "" ||
cfg.RemoteWorkspaceRealAdapterWorkDir != "" {
t.Fatalf("real adapter config should default disabled and empty: %+v", cfg)
}
}
func TestLoadConfigRejectsNegativeProductionObservationSinkCapacity(t *testing.T) {
@@ -0,0 +1,27 @@
//go:build !windows
package hostagent
import "syscall"
func diskUsage(path string) (DiskUsage, error) {
var stat syscall.Statfs_t
if err := syscall.Statfs(path, &stat); err != nil {
return DiskUsage{}, err
}
total := stat.Blocks * uint64(stat.Bsize)
free := stat.Bavail * uint64(stat.Bsize)
used := total - free
percent := 0
if total > 0 {
percent = int((used*100 + total - 1) / total)
}
return DiskUsage{
Path: path,
TotalBytes: total,
FreeBytes: free,
UsedBytes: used,
UsedPercent: percent,
AvailablePercent: 100 - percent,
}, nil
}
@@ -0,0 +1,9 @@
//go:build windows
package hostagent
import "fmt"
func diskUsage(path string) (DiskUsage, error) {
return DiskUsage{Path: path}, fmt.Errorf("disk usage monitor is not implemented on windows")
}
@@ -0,0 +1,494 @@
package hostagent
import (
"context"
"encoding/json"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"time"
"github.com/example/remote-access-platform/agents/rap-node-agent/internal/state"
)
const (
DefaultMonitorProduct = "rap-host-agent"
DefaultMonitorPhase = "host_monitor"
DefaultMonitorIntervalSeconds = 60
DefaultMonitorDiskWarnPercent = 80
DefaultMonitorDiskCleanupPercent = 85
DefaultMonitorDiskCriticalPercent = 95
DefaultMonitorRestartCooldownSec = 300
DefaultMonitorTmpMinAgeMinutes = 240
DefaultMonitorStaleRestartingSec = 180
DefaultMonitorDockerBinary = "docker"
DefaultMonitorDiskPath = "/"
DefaultMonitorTmpDir = "/tmp"
DefaultMonitorStatusSchemaVersion = "rap.host_monitor_status.v1"
DefaultMonitorRemediationSucceeded = "remediated"
)
type MonitorConfig struct {
BackendURL string
ClusterID string
NodeID string
StateDir string
Product string
CurrentVersion string
Interval time.Duration
InitialDelay time.Duration
MaxRuns int
DockerBinary string
WatchContainers []string
RestartContainers bool
RestartCooldown time.Duration
StaleRestartingAfter time.Duration
DiskPath string
TmpDir string
DiskWarnPercent int
DiskCleanupPercent int
DiskCriticalPercent int
TmpMinAge time.Duration
CleanupDocker bool
StatusFile string
Runner CommandRunner
Logf func(format string, args ...any)
restartHistory map[string]time.Time
}
type DiskUsage struct {
Path string `json:"path"`
TotalBytes uint64 `json:"total_bytes"`
FreeBytes uint64 `json:"free_bytes"`
UsedBytes uint64 `json:"used_bytes"`
UsedPercent int `json:"used_percent"`
AvailablePercent int `json:"available_percent"`
}
type MonitorContainerStatus struct {
Name string `json:"name"`
Status string `json:"status,omitempty"`
Running bool `json:"running"`
Restarting bool `json:"restarting"`
ExitCode int `json:"exit_code,omitempty"`
Health string `json:"health,omitempty"`
RestartCount int `json:"restart_count,omitempty"`
StartedAt string `json:"started_at,omitempty"`
FinishedAt string `json:"finished_at,omitempty"`
LastAction string `json:"last_action,omitempty"`
LastActionOK bool `json:"last_action_ok,omitempty"`
LastActionError string `json:"last_action_error,omitempty"`
}
type MonitorAction struct {
Kind string `json:"kind"`
Target string `json:"target,omitempty"`
Reason string `json:"reason,omitempty"`
Success bool `json:"success"`
Error string `json:"error,omitempty"`
}
type MonitorResult struct {
SchemaVersion string `json:"schema_version"`
Status string `json:"status"`
ObservedAt time.Time `json:"observed_at"`
Disk *DiskUsage `json:"disk,omitempty"`
Containers []MonitorContainerStatus `json:"containers,omitempty"`
Actions []MonitorAction `json:"actions,omitempty"`
Errors []string `json:"errors,omitempty"`
}
type monitorDockerInspect struct {
Name string `json:"Name"`
RestartCount int `json:"RestartCount"`
State struct {
Status string `json:"Status"`
Running bool `json:"Running"`
Restarting bool `json:"Restarting"`
ExitCode int `json:"ExitCode"`
Error string `json:"Error"`
StartedAt string `json:"StartedAt"`
FinishedAt string `json:"FinishedAt"`
Health *struct {
Status string `json:"Status"`
} `json:"Health"`
} `json:"State"`
}
func RunMonitorLoop(ctx context.Context, cfg MonitorConfig) error {
cfg = normalizeMonitorConfig(cfg)
if cfg.InitialDelay > 0 {
if err := sleepContext(ctx, cfg.InitialDelay); err != nil {
return err
}
}
runs := 0
restartHistory := map[string]time.Time{}
for {
cfg.restartHistory = restartHistory
result := RunMonitorOnce(ctx, cfg)
logMonitorResult(cfg, result)
if err := writeMonitorStatusFile(cfg.StatusFile, result); err != nil && cfg.Logf != nil {
cfg.Logf("monitor status-file failed: %v", err)
}
if err := reportMonitorStatus(ctx, cfg, result); err != nil && cfg.Logf != nil {
cfg.Logf("monitor report failed: %v", err)
}
runs++
if cfg.MaxRuns > 0 && runs >= cfg.MaxRuns {
return nil
}
if err := sleepContext(ctx, cfg.Interval); err != nil {
return err
}
}
}
func RunMonitorOnce(ctx context.Context, cfg MonitorConfig) MonitorResult {
cfg = normalizeMonitorConfig(cfg)
result := MonitorResult{
SchemaVersion: DefaultMonitorStatusSchemaVersion,
Status: "ok",
ObservedAt: time.Now().UTC(),
}
if usage, err := diskUsage(cfg.DiskPath); err != nil {
result.Errors = append(result.Errors, fmt.Sprintf("disk usage %s: %v", cfg.DiskPath, err))
} else {
result.Disk = &usage
if usage.UsedPercent >= cfg.DiskWarnPercent {
result.Status = "warning"
}
if usage.UsedPercent >= cfg.DiskCleanupPercent {
runCleanup(ctx, cfg, &result, fmt.Sprintf("disk_used_%d_percent", usage.UsedPercent))
if refreshed, err := diskUsage(cfg.DiskPath); err == nil {
result.Disk = &refreshed
}
}
if result.Disk != nil && result.Disk.UsedPercent >= cfg.DiskCriticalPercent {
result.Status = "failed"
result.Errors = append(result.Errors, fmt.Sprintf("disk %s critical: %d%% used", cfg.DiskPath, result.Disk.UsedPercent))
}
}
for _, name := range uniqueTrimmed(cfg.WatchContainers) {
status := inspectMonitorContainer(ctx, cfg, name)
if cfg.RestartContainers {
remediateMonitorContainer(ctx, cfg, &status, &result)
}
if !status.Running || status.Health == "unhealthy" || status.Restarting || status.LastActionError != "" {
if result.Status == "ok" {
result.Status = "warning"
}
}
result.Containers = append(result.Containers, status)
}
for _, action := range result.Actions {
if !action.Success {
result.Status = "failed"
if action.Error != "" {
result.Errors = append(result.Errors, action.Error)
}
}
}
return result
}
func normalizeMonitorConfig(cfg MonitorConfig) MonitorConfig {
cfg.BackendURL = strings.TrimRight(strings.TrimSpace(cfg.BackendURL), "/")
cfg.ClusterID = strings.TrimSpace(cfg.ClusterID)
cfg.NodeID = strings.TrimSpace(cfg.NodeID)
cfg.StateDir = strings.TrimSpace(cfg.StateDir)
cfg.Product = firstNonEmpty(cfg.Product, DefaultMonitorProduct)
if cfg.Interval <= 0 {
cfg.Interval = time.Duration(DefaultMonitorIntervalSeconds) * time.Second
}
if cfg.DockerBinary == "" {
cfg.DockerBinary = DefaultMonitorDockerBinary
}
if cfg.DiskPath == "" {
cfg.DiskPath = DefaultMonitorDiskPath
}
if cfg.TmpDir == "" {
cfg.TmpDir = DefaultMonitorTmpDir
}
if cfg.DiskWarnPercent == 0 {
cfg.DiskWarnPercent = DefaultMonitorDiskWarnPercent
}
if cfg.DiskCleanupPercent == 0 {
cfg.DiskCleanupPercent = DefaultMonitorDiskCleanupPercent
}
if cfg.DiskCriticalPercent == 0 {
cfg.DiskCriticalPercent = DefaultMonitorDiskCriticalPercent
}
if cfg.RestartCooldown == 0 {
cfg.RestartCooldown = time.Duration(DefaultMonitorRestartCooldownSec) * time.Second
}
if cfg.StaleRestartingAfter == 0 {
cfg.StaleRestartingAfter = time.Duration(DefaultMonitorStaleRestartingSec) * time.Second
}
if cfg.TmpMinAge == 0 {
cfg.TmpMinAge = time.Duration(DefaultMonitorTmpMinAgeMinutes) * time.Minute
}
if cfg.Runner == nil {
cfg.Runner = ExecRunner{}
}
return cfg
}
func inspectMonitorContainer(ctx context.Context, cfg MonitorConfig, name string) MonitorContainerStatus {
out := MonitorContainerStatus{Name: name}
raw, err := cfg.Runner.Run(ctx, cfg.DockerBinary, "inspect", name)
if err != nil {
out.LastActionError = strings.TrimSpace(err.Error())
return out
}
var inspected []monitorDockerInspect
if err := json.Unmarshal([]byte(raw), &inspected); err != nil {
out.LastActionError = fmt.Sprintf("parse docker inspect: %v", err)
return out
}
if len(inspected) == 0 {
out.LastActionError = "docker inspect returned no containers"
return out
}
item := inspected[0]
out.Name = strings.TrimPrefix(firstNonEmpty(item.Name, name), "/")
out.Status = item.State.Status
out.Running = item.State.Running
out.Restarting = item.State.Restarting
out.ExitCode = item.State.ExitCode
out.RestartCount = item.RestartCount
out.StartedAt = item.State.StartedAt
out.FinishedAt = item.State.FinishedAt
if item.State.Health != nil {
out.Health = strings.TrimSpace(item.State.Health.Status)
}
if item.State.Error != "" {
out.LastActionError = item.State.Error
}
return out
}
func remediateMonitorContainer(ctx context.Context, cfg MonitorConfig, status *MonitorContainerStatus, result *MonitorResult) {
if status.Name == "" {
return
}
action := ""
reason := ""
switch {
case status.LastActionError != "" && status.Status == "":
action = "start"
reason = "inspect_failed_or_missing"
case status.Health == "unhealthy":
action = "restart"
reason = "health_unhealthy"
case status.Restarting && restartingIsStale(status.StartedAt, status.FinishedAt, cfg.StaleRestartingAfter):
action = "restart"
reason = "restarting_stale"
case !status.Running && status.Status != "":
action = "start"
reason = "not_running"
default:
return
}
if cfg.restartHistory != nil {
if last, ok := cfg.restartHistory[status.Name]; ok && time.Since(last) < cfg.RestartCooldown {
result.Actions = append(result.Actions, MonitorAction{
Kind: "docker_" + action + "_skipped",
Target: status.Name,
Reason: "restart_cooldown",
Success: true,
})
return
}
}
args := []string{action, status.Name}
_, err := cfg.Runner.Run(ctx, cfg.DockerBinary, args...)
monitorAction := MonitorAction{Kind: "docker_" + action, Target: status.Name, Reason: reason, Success: err == nil}
status.LastAction = action
status.LastActionOK = err == nil
if err != nil {
monitorAction.Error = strings.TrimSpace(err.Error())
status.LastActionError = monitorAction.Error
} else {
if cfg.restartHistory != nil {
cfg.restartHistory[status.Name] = time.Now()
}
status.LastActionError = ""
status.Running = true
status.Restarting = false
status.Status = DefaultMonitorRemediationSucceeded
}
result.Actions = append(result.Actions, monitorAction)
}
func restartingIsStale(startedAt, finishedAt string, threshold time.Duration) bool {
for _, value := range []string{finishedAt, startedAt} {
parsed, err := time.Parse(time.RFC3339Nano, strings.TrimSpace(value))
if err == nil && !parsed.IsZero() {
return time.Since(parsed) >= threshold
}
}
return true
}
func runCleanup(ctx context.Context, cfg MonitorConfig, result *MonitorResult, reason string) {
if cfg.CleanupDocker {
for _, args := range [][]string{
{"builder", "prune", "-af"},
{"image", "prune", "-f"},
{"container", "prune", "-f"},
} {
_, err := cfg.Runner.Run(ctx, cfg.DockerBinary, args...)
action := MonitorAction{Kind: "docker_" + strings.Join(args[:len(args)-1], "_"), Reason: reason, Success: err == nil}
if err != nil {
action.Error = strings.TrimSpace(err.Error())
}
result.Actions = append(result.Actions, action)
}
}
removed, err := cleanupTmpBuildDirs(cfg.TmpDir, cfg.TmpMinAge)
action := MonitorAction{Kind: "tmp_cleanup", Target: cfg.TmpDir, Reason: reason, Success: err == nil}
if err != nil {
action.Error = err.Error()
} else {
action.Target = fmt.Sprintf("%s removed=%d", cfg.TmpDir, removed)
}
result.Actions = append(result.Actions, action)
}
func cleanupTmpBuildDirs(tmpDir string, minAge time.Duration) (int, error) {
tmpDir = filepath.Clean(strings.TrimSpace(tmpDir))
if tmpDir == "" || tmpDir == "." || tmpDir == string(filepath.Separator) {
return 0, fmt.Errorf("unsafe tmp dir: %q", tmpDir)
}
entries, err := os.ReadDir(tmpDir)
if err != nil {
return 0, err
}
now := time.Now()
removed := 0
for _, entry := range entries {
name := entry.Name()
if !strings.HasPrefix(name, "rap-") && !strings.HasPrefix(name, "go-build") {
continue
}
info, err := entry.Info()
if err != nil || now.Sub(info.ModTime()) < minAge {
continue
}
if err := os.RemoveAll(filepath.Join(tmpDir, name)); err != nil {
return removed, err
}
removed++
}
return removed, nil
}
func reportMonitorStatus(ctx context.Context, cfg MonitorConfig, result MonitorResult) error {
cfg = normalizeMonitorConfig(cfg)
nodeID, clusterID, err := resolveMonitorIdentity(cfg)
if err != nil {
if errors.Is(err, ErrNodeIdentityNotReady) {
return nil
}
return err
}
if cfg.BackendURL == "" || clusterID == "" || nodeID == "" {
return nil
}
payload := map[string]any{
"schema_version": result.SchemaVersion,
"monitor_status": result.Status,
"disk": result.Disk,
"containers": result.Containers,
"actions": result.Actions,
"errors": result.Errors,
}
errText := ""
if len(result.Errors) > 0 {
errText = strings.Join(result.Errors, "; ")
}
req := NodeUpdateStatusRequest{
Product: cfg.Product,
CurrentVersion: cfg.CurrentVersion,
Phase: DefaultMonitorPhase,
Status: result.Status,
Payload: payload,
ObservedAt: result.ObservedAt,
}
if errText != "" {
req.ErrorMessage = &errText
}
return ReportNodeUpdateStatus(ctx, cfg.BackendURL, clusterID, nodeID, req)
}
func resolveMonitorIdentity(cfg MonitorConfig) (string, string, error) {
nodeID := strings.TrimSpace(cfg.NodeID)
clusterID := strings.TrimSpace(cfg.ClusterID)
if nodeID != "" {
return nodeID, clusterID, nil
}
if strings.TrimSpace(cfg.StateDir) == "" {
return "", clusterID, ErrNodeIdentityNotReady
}
identity, err := state.Load(filepath.Join(cfg.StateDir, state.FileName))
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return "", clusterID, ErrNodeIdentityNotReady
}
return "", clusterID, err
}
nodeID = strings.TrimSpace(identity.NodeID)
if nodeID == "" {
return "", clusterID, ErrNodeIdentityNotReady
}
if clusterID == "" {
clusterID = strings.TrimSpace(identity.ClusterID)
}
return nodeID, clusterID, nil
}
func writeMonitorStatusFile(path string, result MonitorResult) error {
path = strings.TrimSpace(path)
if path == "" {
return nil
}
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
return err
}
payload, err := json.MarshalIndent(result, "", " ")
if err != nil {
return err
}
tmp := path + ".tmp"
if err := os.WriteFile(tmp, payload, 0o644); err != nil {
return err
}
return os.Rename(tmp, path)
}
func logMonitorResult(cfg MonitorConfig, result MonitorResult) {
if cfg.Logf == nil {
return
}
cfg.Logf("monitor status=%s containers=%d actions=%d errors=%d", result.Status, len(result.Containers), len(result.Actions), len(result.Errors))
}
func uniqueTrimmed(values []string) []string {
seen := map[string]struct{}{}
out := make([]string, 0, len(values))
for _, value := range values {
value = strings.TrimSpace(value)
if value == "" {
continue
}
if _, ok := seen[value]; ok {
continue
}
seen[value] = struct{}{}
out = append(out, value)
}
return out
}
@@ -0,0 +1,87 @@
package hostagent
import (
"context"
"fmt"
"strings"
"testing"
"time"
)
type monitorRunner struct {
inspect map[string]string
calls []string
}
func (r *monitorRunner) Run(_ context.Context, name string, args ...string) (string, error) {
call := strings.TrimSpace(name + " " + strings.Join(args, " "))
r.calls = append(r.calls, call)
if len(args) >= 2 && args[0] == "inspect" {
out, ok := r.inspect[args[1]]
if !ok {
return "", fmt.Errorf("not found")
}
return out, nil
}
return "", nil
}
func TestRunMonitorOnceStartsExitedContainer(t *testing.T) {
runner := &monitorRunner{inspect: map[string]string{
"rap-node-agent": `[{"Name":"/rap-node-agent","State":{"Status":"exited","Running":false,"ExitCode":137,"StartedAt":"2026-05-13T00:00:00Z","FinishedAt":"2026-05-13T00:01:00Z"}}]`,
}}
result := RunMonitorOnce(context.Background(), MonitorConfig{
WatchContainers: []string{"rap-node-agent"},
RestartContainers: true,
Runner: runner,
DiskPath: t.TempDir(),
DiskCleanupPercent: 101,
DiskWarnPercent: 101,
DiskCriticalPercent: 101,
})
if len(result.Actions) != 1 || result.Actions[0].Kind != "docker_start" || !result.Actions[0].Success {
t.Fatalf("unexpected actions: %+v", result.Actions)
}
if !containsCall(runner.calls, "docker start rap-node-agent") {
t.Fatalf("start call missing: %+v", runner.calls)
}
}
func TestRunMonitorOnceRestartsUnhealthyContainer(t *testing.T) {
runner := &monitorRunner{inspect: map[string]string{
"rap-backend": `[{"Name":"/rap-backend","State":{"Status":"running","Running":true,"StartedAt":"2026-05-13T00:00:00Z","Health":{"Status":"unhealthy"}}}]`,
}}
result := RunMonitorOnce(context.Background(), MonitorConfig{
WatchContainers: []string{"rap-backend"},
RestartContainers: true,
Runner: runner,
DiskPath: t.TempDir(),
DiskCleanupPercent: 101,
DiskWarnPercent: 101,
DiskCriticalPercent: 101,
})
if len(result.Actions) != 1 || result.Actions[0].Kind != "docker_restart" || !result.Actions[0].Success {
t.Fatalf("unexpected actions: %+v", result.Actions)
}
if !containsCall(runner.calls, "docker restart rap-backend") {
t.Fatalf("restart call missing: %+v", runner.calls)
}
}
func TestRestartingIsStale(t *testing.T) {
if !restartingIsStale(time.Now().Add(-10*time.Minute).UTC().Format(time.RFC3339Nano), "", time.Minute) {
t.Fatalf("old restarting container should be stale")
}
if restartingIsStale(time.Now().UTC().Format(time.RFC3339Nano), "", time.Hour) {
t.Fatalf("fresh restarting container should not be stale")
}
}
func containsCall(calls []string, want string) bool {
for _, call := range calls {
if call == want {
return true
}
}
return false
}
@@ -31,6 +31,14 @@ type UpdateServiceConfig struct {
DryRun bool
InstallSelfUpdater bool
SelfUpdateVersion string
InstallMonitor bool
MonitorIntervalSec int
MonitorContainers []string
MonitorStatusFile string
MonitorDiskWarn int
MonitorDiskCleanup int
MonitorDiskCritical int
MonitorCleanupDocker bool
}
type UpdateServiceResult struct {
@@ -43,6 +51,9 @@ type UpdateServiceResult struct {
SelfUnitName string
SelfUnitPath string
SelfUnit string
MonitorUnitName string
MonitorUnitPath string
MonitorUnit string
}
func (m DockerManager) InstallUpdateService(ctx context.Context, cfg UpdateServiceConfig) (UpdateServiceResult, error) {
@@ -59,6 +70,9 @@ func (m DockerManager) InstallUpdateService(ctx context.Context, cfg UpdateServi
if cfg.HealthTimeoutSec == 0 {
cfg.HealthTimeoutSec = 30
}
if cfg.MonitorIntervalSec == 0 {
cfg.MonitorIntervalSec = DefaultMonitorIntervalSeconds
}
cfg.BinaryInstallPath = firstNonEmpty(cfg.BinaryInstallPath, DefaultHostAgentInstallPath)
cfg.UnitDir = firstNonEmpty(cfg.UnitDir, DefaultSystemdUnitDir)
unitName := "rap-host-agent-updater-" + safeUnitSlug(cfg.RuntimeConfig.ContainerName) + ".service"
@@ -82,6 +96,15 @@ func (m DockerManager) InstallUpdateService(ctx context.Context, cfg UpdateServi
result.SelfUnitName = selfUnitName
result.SelfUnitPath = selfUnitPath
}
if cfg.InstallMonitor {
monitorUnit, monitorUnitName, monitorUnitPath, err := buildHostAgentMonitorUnit(cfg)
if err != nil {
return result, err
}
result.MonitorUnit = monitorUnit
result.MonitorUnitName = monitorUnitName
result.MonitorUnitPath = monitorUnitPath
}
return result, nil
}
if runtime.GOOS != "linux" && cfg.UnitDir == DefaultSystemdUnitDir {
@@ -108,6 +131,18 @@ func (m DockerManager) InstallUpdateService(ctx context.Context, cfg UpdateServi
result.SelfUnitName = selfUnitName
result.SelfUnitPath = selfUnitPath
}
if cfg.InstallMonitor {
monitorUnit, monitorUnitName, monitorUnitPath, err := buildHostAgentMonitorUnit(cfg)
if err != nil {
return result, err
}
if err := os.WriteFile(monitorUnitPath, []byte(monitorUnit), 0o644); err != nil {
return result, err
}
result.MonitorUnit = monitorUnit
result.MonitorUnitName = monitorUnitName
result.MonitorUnitPath = monitorUnitPath
}
result.Installed = true
if cfg.ManageSystemd {
runner := m.Runner
@@ -125,6 +160,11 @@ func (m DockerManager) InstallUpdateService(ctx context.Context, cfg UpdateServi
return result, err
}
}
if cfg.InstallMonitor && result.MonitorUnitName != "" {
if _, err := runner.Run(ctx, "systemctl", "enable", "--now", result.MonitorUnitName); err != nil {
return result, err
}
}
result.Started = true
}
return result, nil
@@ -223,6 +263,64 @@ WantedBy=multi-user.target
`, systemdJoin(args)), unitName, unitPath, nil
}
func buildHostAgentMonitorUnit(cfg UpdateServiceConfig) (string, string, string, error) {
runtimeCfg := cfg.RuntimeConfig.Normalize()
if runtimeCfg.BackendURL == "" || runtimeCfg.ClusterID == "" || runtimeCfg.StateDir == "" {
return "", "", "", fmt.Errorf("backend-url, cluster-id, and state-dir are required for host monitor")
}
containers := uniqueTrimmed(append([]string{runtimeCfg.ContainerName}, cfg.MonitorContainers...))
if len(containers) == 0 {
return "", "", "", fmt.Errorf("at least one monitor container is required")
}
unitName := "rap-host-agent-monitor-" + safeUnitSlug(runtimeCfg.ContainerName) + ".service"
unitPath := filepath.Join(firstNonEmpty(cfg.UnitDir, DefaultSystemdUnitDir), unitName)
args := []string{
cfg.BinaryInstallPath,
"monitor-loop",
"--backend-url", runtimeCfg.BackendURL,
"--cluster-id", runtimeCfg.ClusterID,
"--state-dir", runtimeCfg.StateDir,
"--current-version", firstNonEmpty(cfg.SelfUpdateVersion, cfg.CurrentVersion),
"--interval-seconds", fmt.Sprintf("%d", firstNonZero(cfg.MonitorIntervalSec, DefaultMonitorIntervalSeconds)),
"--disk-warn-percent", fmt.Sprintf("%d", firstNonZero(cfg.MonitorDiskWarn, DefaultMonitorDiskWarnPercent)),
"--disk-cleanup-percent", fmt.Sprintf("%d", firstNonZero(cfg.MonitorDiskCleanup, DefaultMonitorDiskCleanupPercent)),
"--disk-critical-percent", fmt.Sprintf("%d", firstNonZero(cfg.MonitorDiskCritical, DefaultMonitorDiskCriticalPercent)),
}
if cfg.MonitorCleanupDocker {
args = append(args, "--cleanup-docker")
}
if strings.TrimSpace(cfg.MonitorStatusFile) != "" {
args = append(args, "--status-file", strings.TrimSpace(cfg.MonitorStatusFile))
}
for _, container := range containers {
args = append(args, "--watch-container", container)
}
return fmt.Sprintf(`[Unit]
Description=RAP host-agent monitor for %s
After=network-online.target docker.service
Wants=network-online.target
Requires=docker.service
[Service]
Type=simple
ExecStart=%s
Restart=always
RestartSec=30
[Install]
WantedBy=multi-user.target
`, runtimeCfg.ContainerName, systemdJoin(args)), unitName, unitPath, nil
}
func firstNonZero(values ...int) int {
for _, value := range values {
if value != 0 {
return value
}
}
return 0
}
func installHostAgentBinary(sourcePath, targetPath string) error {
sourcePath = strings.TrimSpace(sourcePath)
targetPath = strings.TrimSpace(targetPath)
@@ -33,6 +33,9 @@ func TestInstallUpdateServiceWritesSystemdUnit(t *testing.T) {
ManageSystemd: false,
InstallSelfUpdater: true,
SelfUpdateVersion: "0.1.0-host",
InstallMonitor: true,
MonitorContainers: []string{"rap-test-backend"},
MonitorCleanupDocker: true,
})
if err != nil {
t.Fatalf("install update service: %v", err)
@@ -73,6 +76,25 @@ func TestInstallUpdateServiceWritesSystemdUnit(t *testing.T) {
if text := string(selfUnit); !strings.Contains(text, "update-host-agent-loop") || !strings.Contains(text, "--current-version 0.1.0-host") {
t.Fatalf("unexpected self unit:\n%s", text)
}
if result.MonitorUnitName == "" || result.MonitorUnitPath == "" {
t.Fatalf("monitor result = %+v", result)
}
monitorUnit, err := os.ReadFile(result.MonitorUnitPath)
if err != nil {
t.Fatalf("read monitor unit: %v", err)
}
monitorText := string(monitorUnit)
for _, want := range []string{
"monitor-loop",
"--watch-container rap-node-agent-node-a",
"--watch-container rap-test-backend",
"--cleanup-docker",
"Restart=always",
} {
if !strings.Contains(monitorText, want) {
t.Fatalf("monitor unit missing %q:\n%s", want, monitorText)
}
}
}
func TestWindowsHostAgentUpdateScriptTargetsWindowsService(t *testing.T) {
@@ -313,6 +313,9 @@ func (m DockerManager) ApplyUpdate(ctx context.Context, req UpdateRequest) (Upda
cfg.ClusterID = firstNonEmpty(cfg.ClusterID, req.ClusterID)
cfg.ContainerName = req.ContainerName
cfg.Image = artifactImage(*plan.Artifact, cfg.Image)
if artifactDockerVPNGatewayEnabled(*plan.Artifact) {
cfg.DockerVPNGatewayEnabled = true
}
cfg.ImageArtifactURLs = artifactURLsForBackend(*plan.Artifact, req.BackendURL)
cfg.ImageArtifactSHA256 = plan.Artifact.SHA256
cfg.ImageArtifactSizeBytes = plan.Artifact.SizeBytes
@@ -681,6 +684,20 @@ func artifactImage(artifact ReleaseArtifact, fallback string) string {
return firstNonEmpty(fallback, DefaultImage)
}
func artifactDockerVPNGatewayEnabled(artifact ReleaseArtifact) bool {
if len(artifact.Metadata) == 0 {
return false
}
var metadata struct {
DockerVPNGatewayEnabled bool `json:"docker_vpn_gateway_enabled"`
VPNGatewayEnabled bool `json:"vpn_gateway_enabled"`
}
if err := json.Unmarshal(artifact.Metadata, &metadata); err != nil {
return false
}
return metadata.DockerVPNGatewayEnabled || metadata.VPNGatewayEnabled
}
func artifactURLs(artifact ReleaseArtifact) []string {
out := make([]string, 0, 1+len(artifact.URLs))
for _, raw := range append([]string{artifact.URL}, artifact.URLs...) {
@@ -596,6 +596,18 @@ func TestArtifactImageDerivesDockerTagFromProductAndVersion(t *testing.T) {
}
}
func TestArtifactDockerVPNGatewayEnabledFromMetadata(t *testing.T) {
if !artifactDockerVPNGatewayEnabled(ReleaseArtifact{Metadata: json.RawMessage(`{"docker_vpn_gateway_enabled":true}`)}) {
t.Fatal("expected docker vpn gateway metadata to enable gateway runtime")
}
if !artifactDockerVPNGatewayEnabled(ReleaseArtifact{Metadata: json.RawMessage(`{"vpn_gateway_enabled":true}`)}) {
t.Fatal("expected legacy vpn gateway metadata to enable gateway runtime")
}
if artifactDockerVPNGatewayEnabled(ReleaseArtifact{Metadata: json.RawMessage(`{"docker_vpn_gateway_enabled":false}`)}) {
t.Fatal("expected disabled metadata to remain disabled")
}
}
func serverArtifactURL(r *http.Request) string {
scheme := "http"
if r.TLS != nil {
@@ -41,6 +41,9 @@ type RemoteWorkspaceFrameProbeSink struct {
mailboxAfterSequenceReadTotal int64
mailboxReturnedTotal int64
mailboxSkippedTotal int64
mailboxPreflightTotal int64
mailboxPreflightAckTotal int64
mailboxPreflightCheckpointTotal int64
mailboxConsumerReadTotal int64
mailboxConsumerAckTotal int64
mailboxConsumerResetTotal int64
@@ -57,6 +60,31 @@ type RemoteWorkspaceFrameProbeSink struct {
lastMailboxAfterSequence int64
lastMailboxSkippedCount int
lastMailboxReturnedCount int
lastMailboxPreflightAt string
lastMailboxPreflightAdapterSessionID string
lastMailboxPreflightConsumerID string
lastMailboxPreflightResumeFrom string
lastMailboxPreflightResumeSequence int64
lastMailboxPreflightAfterSequence int64
lastMailboxPreflightAvailableCount int
lastMailboxPreflightReturnedCount int
lastMailboxPreflightSkippedCount int
lastMailboxPreflightFirstSequence int64
lastMailboxPreflightLastSequence int64
lastMailboxPreflightFirstRetained int64
lastMailboxPreflightLastRetained int64
lastMailboxPreflightMailboxDropped int64
lastMailboxPreflightDiagnosticState string
lastMailboxPreflightStaleCursor bool
lastMailboxPreflightMissingDropped int
lastMailboxPreflightRecommendedAction string
lastMailboxPreflightActionHints []string
lastMailboxPreflightActionReason string
lastMailboxPreflightActionContext map[string]any
lastMailboxPreflightOperatorSummary string
lastMailboxPreflightOperatorStatus string
lastMailboxPreflightOperatorSeverity string
lastMailboxPreflightOperatorFields map[string]any
lastMailboxConsumerID string
lastMailboxConsumerAdapterSessionID string
lastMailboxConsumerReadAt string
@@ -104,6 +132,11 @@ type remoteWorkspaceAdapterProbeSession struct {
MailboxAfterSequenceRead int64
MailboxReturnedTotal int64
MailboxSkippedTotal int64
MailboxPreflightTotal int64
MailboxPreflightAckTotal int64
MailboxPreflightCheckpointTotal int64
MailboxPreflightOperatorStatusCounts map[string]int64
MailboxPreflightOperatorSeverityCounts map[string]int64
MailboxConsumers map[string]*remoteWorkspaceAdapterMailboxConsumerState
MailboxConsumerReadTotal int64
MailboxConsumerAckTotal int64
@@ -125,6 +158,30 @@ type remoteWorkspaceAdapterProbeSession struct {
LastMailboxAfterSequence int64
LastMailboxSkippedCount int
LastMailboxReturnedCount int
LastMailboxPreflightAt time.Time
LastMailboxPreflightConsumerID string
LastMailboxPreflightResumeFrom string
LastMailboxPreflightResumeSequence int64
LastMailboxPreflightAfterSequence int64
LastMailboxPreflightAvailableCount int
LastMailboxPreflightReturnedCount int
LastMailboxPreflightSkippedCount int
LastMailboxPreflightFirstSequence int64
LastMailboxPreflightLastSequence int64
LastMailboxPreflightFirstRetained int64
LastMailboxPreflightLastRetained int64
LastMailboxPreflightMailboxDropped int64
LastMailboxPreflightDiagnosticState string
LastMailboxPreflightStaleCursor bool
LastMailboxPreflightMissingDropped int
LastMailboxPreflightRecommendedAction string
LastMailboxPreflightActionHints []string
LastMailboxPreflightActionReason string
LastMailboxPreflightActionContext map[string]any
LastMailboxPreflightOperatorSummary string
LastMailboxPreflightOperatorStatus string
LastMailboxPreflightOperatorSeverity string
LastMailboxPreflightOperatorFields map[string]any
LastChannelID string
LastResourceID string
LastRouteID string
@@ -263,6 +320,7 @@ type RemoteWorkspaceAdapterMailboxPreflightSnapshot struct {
Limit int `json:"limit"`
MailboxDepth int `json:"mailbox_depth"`
MailboxEnqueued int64 `json:"mailbox_enqueued_total"`
MailboxDropped int64 `json:"mailbox_dropped_total"`
MailboxReadTotal int64 `json:"mailbox_read_total"`
ConsumerReadTotal int64 `json:"consumer_read_total"`
ConsumerAckTotal int64 `json:"consumer_ack_total"`
@@ -274,6 +332,19 @@ type RemoteWorkspaceAdapterMailboxPreflightSnapshot struct {
ExpectedSkippedCount int `json:"expected_skipped_count"`
FirstExpectedSequence int64 `json:"first_expected_sequence,omitempty"`
LastExpectedSequence int64 `json:"last_expected_sequence,omitempty"`
FirstRetainedSequence int64 `json:"first_retained_sequence,omitempty"`
LastRetainedSequence int64 `json:"last_retained_sequence,omitempty"`
DiagnosticState string `json:"diagnostic_state"`
StaleCursor bool `json:"stale_cursor"`
MissingDroppedCount int `json:"missing_dropped_count"`
RecommendedAction string `json:"recommended_action"`
ActionHints []string `json:"action_hints"`
ActionReason string `json:"action_reason"`
ActionContext map[string]any `json:"action_context"`
OperatorSummary string `json:"operator_summary"`
OperatorStatus string `json:"operator_status"`
OperatorSeverity string `json:"operator_severity"`
OperatorSummaryFields map[string]any `json:"operator_summary_fields"`
}
type RemoteWorkspaceAdapterSessionSnapshot struct {
@@ -651,6 +722,8 @@ func (s *RemoteWorkspaceFrameProbeSink) ensureSessionLocked(delivery RemoteWorks
CreatedAt: now,
LastActivityAt: now,
MailboxConsumers: map[string]*remoteWorkspaceAdapterMailboxConsumerState{},
MailboxPreflightOperatorStatusCounts: map[string]int64{},
MailboxPreflightOperatorSeverityCounts: map[string]int64{},
}
s.sessions[sessionID] = session
s.sessionCreatedTotal++
@@ -1180,7 +1253,74 @@ func (s *RemoteWorkspaceFrameProbeSink) PreflightAdapterSessionMailboxConsumerRe
firstExpected = session.Mailbox[startIndex].Sequence
lastExpected = session.Mailbox[startIndex+returned-1].Sequence
}
return RemoteWorkspaceAdapterMailboxPreflightSnapshot{
var firstRetained int64
var lastRetained int64
if len(session.Mailbox) > 0 {
firstRetained = session.Mailbox[0].Sequence
lastRetained = session.Mailbox[len(session.Mailbox)-1].Sequence
}
diagnosticState := "ready"
staleCursor := false
missingDropped := 0
recommendedAction := "resume_from_cursor"
actionHints := []string{"resume_from_requested_cursor"}
actionReason := "cursor_window_available"
if firstRetained > 0 && resumeSequence < firstRetained-1 {
diagnosticState = "stale_cursor_gap"
staleCursor = true
missingDropped = int(firstRetained - resumeSequence - 1)
recommendedAction = "reset_consumer_and_resync"
actionHints = []string{"reset_consumer_cursor", "request_full_adapter_resync", "resume_from_checkpoint_after_resync"}
actionReason = "consumer_cursor_before_first_retained_sequence"
} else if returned == 0 {
diagnosticState = "caught_up"
recommendedAction = "wait_for_new_mailbox_events"
actionHints = []string{"keep_consumer_cursor", "long_poll_after_sequence"}
actionReason = "cursor_caught_up_to_retained_mailbox"
}
actionContext := map[string]any{
"consumer_id": consumerID,
"resume_from": resumeFrom,
"resume_sequence": resumeSequence,
"first_retained_sequence": firstRetained,
"last_retained_sequence": lastRetained,
"mailbox_depth": len(session.Mailbox),
"mailbox_dropped_total": session.MailboxDropped,
"missing_dropped_count": missingDropped,
"expected_available_count": available,
"expected_returned_count": returned,
"expected_skipped_count": startIndex,
"consumer_checkpoint_sequence": consumer.CheckpointSequence,
"consumer_ack_sequence": consumer.AckSequence,
}
operatorSummary := "consumer cursor can resume from requested window"
operatorStatus := "ready_to_resume"
operatorSeverity := "ok"
if diagnosticState == "stale_cursor_gap" {
operatorSummary = "stale cursor gap: reset consumer and resync before resume"
operatorStatus = "resync_required"
operatorSeverity = "warn"
} else if diagnosticState == "caught_up" {
operatorSummary = "consumer cursor is caught up; wait for new mailbox events"
operatorStatus = "caught_up"
operatorSeverity = "info"
}
operatorSummaryFields := map[string]any{
"diagnostic_state": diagnosticState,
"recommended_action": recommendedAction,
"action_reason": actionReason,
"operator_status": operatorStatus,
"operator_severity": operatorSeverity,
"resume_from": resumeFrom,
"resume_sequence": resumeSequence,
"first_retained_sequence": firstRetained,
"last_retained_sequence": lastRetained,
"missing_dropped_count": missingDropped,
"expected_available_count": available,
"expected_returned_count": returned,
"expected_skipped_count": startIndex,
}
snapshot := RemoteWorkspaceAdapterMailboxPreflightSnapshot{
SchemaVersion: "rap.remote_workspace_adapter_mailbox_preflight.v1",
AdapterRuntimeID: RemoteWorkspaceFrameProbeSinkRuntimeID,
AdapterSessionID: adapterSessionID,
@@ -1193,6 +1333,7 @@ func (s *RemoteWorkspaceFrameProbeSink) PreflightAdapterSessionMailboxConsumerRe
Limit: limit,
MailboxDepth: len(session.Mailbox),
MailboxEnqueued: session.MailboxEnqueued,
MailboxDropped: session.MailboxDropped,
MailboxReadTotal: session.MailboxRead,
ConsumerReadTotal: session.MailboxConsumerReadTotal,
ConsumerAckTotal: session.MailboxConsumerAckTotal,
@@ -1204,7 +1345,236 @@ func (s *RemoteWorkspaceFrameProbeSink) PreflightAdapterSessionMailboxConsumerRe
ExpectedSkippedCount: startIndex,
FirstExpectedSequence: firstExpected,
LastExpectedSequence: lastExpected,
}, nil
FirstRetainedSequence: firstRetained,
LastRetainedSequence: lastRetained,
DiagnosticState: diagnosticState,
StaleCursor: staleCursor,
MissingDroppedCount: missingDropped,
RecommendedAction: recommendedAction,
ActionHints: actionHints,
ActionReason: actionReason,
ActionContext: actionContext,
OperatorSummary: operatorSummary,
OperatorStatus: operatorStatus,
OperatorSeverity: operatorSeverity,
OperatorSummaryFields: operatorSummaryFields,
}
s.recordAdapterSessionMailboxPreflightLocked(session, snapshot, now)
return snapshot, nil
}
func (s *RemoteWorkspaceFrameProbeSink) recordAdapterSessionMailboxPreflightLocked(session *remoteWorkspaceAdapterProbeSession, snapshot RemoteWorkspaceAdapterMailboxPreflightSnapshot, now time.Time) {
s.mailboxPreflightTotal++
if snapshot.ResumeFrom == "ack" {
s.mailboxPreflightAckTotal++
}
if snapshot.ResumeFrom == "checkpoint" {
s.mailboxPreflightCheckpointTotal++
}
s.lastMailboxPreflightAt = now.Format(time.RFC3339Nano)
s.lastMailboxPreflightAdapterSessionID = snapshot.AdapterSessionID
s.lastMailboxPreflightConsumerID = snapshot.ConsumerID
s.lastMailboxPreflightResumeFrom = snapshot.ResumeFrom
s.lastMailboxPreflightResumeSequence = snapshot.ResumeSequence
s.lastMailboxPreflightAfterSequence = snapshot.AfterSequence
s.lastMailboxPreflightAvailableCount = snapshot.ExpectedAvailableCount
s.lastMailboxPreflightReturnedCount = snapshot.ExpectedReturnedCount
s.lastMailboxPreflightSkippedCount = snapshot.ExpectedSkippedCount
s.lastMailboxPreflightFirstSequence = snapshot.FirstExpectedSequence
s.lastMailboxPreflightLastSequence = snapshot.LastExpectedSequence
s.lastMailboxPreflightFirstRetained = snapshot.FirstRetainedSequence
s.lastMailboxPreflightLastRetained = snapshot.LastRetainedSequence
s.lastMailboxPreflightMailboxDropped = snapshot.MailboxDropped
s.lastMailboxPreflightDiagnosticState = snapshot.DiagnosticState
s.lastMailboxPreflightStaleCursor = snapshot.StaleCursor
s.lastMailboxPreflightMissingDropped = snapshot.MissingDroppedCount
s.lastMailboxPreflightRecommendedAction = snapshot.RecommendedAction
s.lastMailboxPreflightActionHints = append([]string(nil), snapshot.ActionHints...)
s.lastMailboxPreflightActionReason = snapshot.ActionReason
s.lastMailboxPreflightActionContext = cloneStringAnyMap(snapshot.ActionContext)
s.lastMailboxPreflightOperatorSummary = snapshot.OperatorSummary
s.lastMailboxPreflightOperatorStatus = snapshot.OperatorStatus
s.lastMailboxPreflightOperatorSeverity = snapshot.OperatorSeverity
s.lastMailboxPreflightOperatorFields = cloneStringAnyMap(snapshot.OperatorSummaryFields)
if session == nil {
return
}
session.MailboxPreflightTotal++
if snapshot.ResumeFrom == "ack" {
session.MailboxPreflightAckTotal++
}
if snapshot.ResumeFrom == "checkpoint" {
session.MailboxPreflightCheckpointTotal++
}
incrementStringInt64Map(&session.MailboxPreflightOperatorStatusCounts, snapshot.OperatorStatus)
incrementStringInt64Map(&session.MailboxPreflightOperatorSeverityCounts, snapshot.OperatorSeverity)
session.LastMailboxPreflightAt = now
session.LastMailboxPreflightConsumerID = snapshot.ConsumerID
session.LastMailboxPreflightResumeFrom = snapshot.ResumeFrom
session.LastMailboxPreflightResumeSequence = snapshot.ResumeSequence
session.LastMailboxPreflightAfterSequence = snapshot.AfterSequence
session.LastMailboxPreflightAvailableCount = snapshot.ExpectedAvailableCount
session.LastMailboxPreflightReturnedCount = snapshot.ExpectedReturnedCount
session.LastMailboxPreflightSkippedCount = snapshot.ExpectedSkippedCount
session.LastMailboxPreflightFirstSequence = snapshot.FirstExpectedSequence
session.LastMailboxPreflightLastSequence = snapshot.LastExpectedSequence
session.LastMailboxPreflightFirstRetained = snapshot.FirstRetainedSequence
session.LastMailboxPreflightLastRetained = snapshot.LastRetainedSequence
session.LastMailboxPreflightMailboxDropped = snapshot.MailboxDropped
session.LastMailboxPreflightDiagnosticState = snapshot.DiagnosticState
session.LastMailboxPreflightStaleCursor = snapshot.StaleCursor
session.LastMailboxPreflightMissingDropped = snapshot.MissingDroppedCount
session.LastMailboxPreflightRecommendedAction = snapshot.RecommendedAction
session.LastMailboxPreflightActionHints = append([]string(nil), snapshot.ActionHints...)
session.LastMailboxPreflightActionReason = snapshot.ActionReason
session.LastMailboxPreflightActionContext = cloneStringAnyMap(snapshot.ActionContext)
session.LastMailboxPreflightOperatorSummary = snapshot.OperatorSummary
session.LastMailboxPreflightOperatorStatus = snapshot.OperatorStatus
session.LastMailboxPreflightOperatorSeverity = snapshot.OperatorSeverity
session.LastMailboxPreflightOperatorFields = cloneStringAnyMap(snapshot.OperatorSummaryFields)
}
func cloneStringAnyMap(source map[string]any) map[string]any {
if source == nil {
return nil
}
clone := make(map[string]any, len(source))
for key, value := range source {
clone[key] = value
}
return clone
}
func cloneStringInt64Map(source map[string]int64) map[string]int64 {
if source == nil {
return nil
}
clone := make(map[string]int64, len(source))
for key, value := range source {
clone[key] = value
}
return clone
}
func incrementStringInt64Map(target *map[string]int64, key string) {
key = strings.TrimSpace(key)
if key == "" || target == nil {
return
}
if *target == nil {
*target = map[string]int64{}
}
(*target)[key]++
}
func remoteWorkspacePreflightAttentionStatus(statusCounts map[string]int64, severityCounts map[string]int64) string {
resyncCount := statusCounts["resync_required"]
warnCount := severityCounts["warn"]
if resyncCount > 1 || warnCount > 1 {
return "repeated_resync_required"
}
if resyncCount > 0 || warnCount > 0 {
return "needs_attention"
}
if statusCounts["ready_to_resume"] > 0 || statusCounts["caught_up"] > 0 || severityCounts["ok"] > 0 || severityCounts["info"] > 0 {
return "clean"
}
return "unknown"
}
func remoteWorkspacePreflightAttentionReason(status string, statusCounts map[string]int64, severityCounts map[string]int64) string {
switch status {
case "repeated_resync_required":
return "resync_required_preflight_repeated"
case "needs_attention":
if statusCounts["resync_required"] > 0 {
return "resync_required_preflight_observed"
}
if severityCounts["warn"] > 0 {
return "warn_preflight_observed"
}
return "attention_preflight_observed"
case "clean":
return "no_resync_required_preflight_observed"
default:
return "no_preflight_observed"
}
}
func remoteWorkspacePreflightRemediationChecklist(operatorStatus string, actionHints []string) []map[string]any {
hints := map[string]bool{}
for _, hint := range actionHints {
hints[hint] = true
}
if operatorStatus == "resync_required" {
return []map[string]any{
{
"step": "reset_consumer_cursor",
"required": true,
"satisfied": false,
"source_hint": hints["reset_consumer_cursor"],
},
{
"step": "request_full_adapter_resync",
"required": true,
"satisfied": false,
"source_hint": hints["request_full_adapter_resync"],
},
{
"step": "resume_from_checkpoint_after_resync",
"required": true,
"satisfied": false,
"source_hint": hints["resume_from_checkpoint_after_resync"],
},
}
}
if operatorStatus == "ready_to_resume" {
return []map[string]any{{
"step": "resume_from_requested_cursor",
"required": true,
"satisfied": true,
"source_hint": hints["resume_from_requested_cursor"],
}}
}
return []map[string]any{{
"step": "wait_for_new_mailbox_events",
"required": true,
"satisfied": false,
"source_hint": hints["long_poll_after_sequence"] || hints["keep_consumer_cursor"],
}}
}
func remoteWorkspacePreflightRemediationChecklistSummary(checklist []map[string]any) map[string]any {
total := len(checklist)
required := 0
satisfied := 0
for _, item := range checklist {
itemRequired, _ := item["required"].(bool)
itemSatisfied, _ := item["satisfied"].(bool)
if itemRequired {
required++
if itemSatisfied {
satisfied++
}
}
}
pending := required - satisfied
if pending < 0 {
pending = 0
}
status := "not_required"
if required > 0 && pending == 0 {
status = "ready"
} else if pending > 0 {
status = "action_required"
}
return map[string]any{
"status": status,
"total_count": total,
"required_count": required,
"satisfied_count": satisfied,
"pending_count": pending,
}
}
func (s *RemoteWorkspaceFrameProbeSink) evictOldestMailboxConsumerLocked(session *remoteWorkspaceAdapterProbeSession) bool {
@@ -1270,11 +1640,36 @@ func remoteWorkspaceAdapterRuntimeReadinessLocked(s *RemoteWorkspaceFrameProbeSi
"consumer_capacity": DefaultRemoteWorkspaceAdapterMailboxConsumerCapacity,
"mailbox_read_total": s.mailboxReadTotal,
"mailbox_resume_total": s.mailboxResumeReadTotal,
"mailbox_preflight_total": s.mailboxPreflightTotal,
}
if session == nil {
if s.sequence == 0 {
readiness["no_session_summary"] = map[string]any{
"schema_version": "rap.remote_workspace_adapter_no_session_summary.v1",
"summary_contract": []string{"status", "diagnostic_state", "active_session_count", "terminal_session_count"},
"summary_features": map[string]bool{"status": true, "diagnostic_state": true, "active_session_count": true, "terminal_session_count": true},
"status": "idle",
"diagnostic_state": "waiting_for_session",
"active_session_count": len(s.sessions),
"terminal_session_count": len(s.terminalSessions),
}
}
if s.sequence > 0 {
readiness["last_adapter_session_id"] = s.last.AdapterSessionID
readiness["last_session_state"] = s.last.SessionState
lastSessionState := s.last.SessionState
if terminal, ok := s.terminalSessions[s.last.AdapterSessionID]; ok {
lastSessionState = terminal.State
readiness["terminal_session_summary"] = map[string]any{
"schema_version": "rap.remote_workspace_adapter_terminal_session_summary.v1",
"summary_contract": []string{"adapter_session_id", "session_state", "reason", "controlled_at"},
"summary_features": map[string]bool{"adapter_session_id": true, "session_state": true, "reason": true, "controlled_at": true},
"adapter_session_id": s.last.AdapterSessionID,
"session_state": terminal.State,
"reason": terminal.Reason,
"controlled_at": terminal.ControlledAt.Format(time.RFC3339Nano),
}
}
readiness["last_session_state"] = lastSessionState
readiness["diagnostic_state"] = "last_session_terminal_or_expired"
}
return readiness
@@ -1299,6 +1694,13 @@ func remoteWorkspaceAdapterRuntimeReadinessLocked(s *RemoteWorkspaceFrameProbeSi
readiness["mailbox_enqueued_total"] = session.MailboxEnqueued
readiness["mailbox_read_total"] = session.MailboxRead
readiness["mailbox_resume_read_total"] = session.MailboxResumeRead
readiness["mailbox_preflight_total"] = session.MailboxPreflightTotal
readiness["mailbox_preflight_operator_status_counts"] = cloneStringInt64Map(session.MailboxPreflightOperatorStatusCounts)
readiness["mailbox_preflight_operator_severity_counts"] = cloneStringInt64Map(session.MailboxPreflightOperatorSeverityCounts)
preflightAttentionStatus := remoteWorkspacePreflightAttentionStatus(session.MailboxPreflightOperatorStatusCounts, session.MailboxPreflightOperatorSeverityCounts)
preflightAttentionReason := remoteWorkspacePreflightAttentionReason(preflightAttentionStatus, session.MailboxPreflightOperatorStatusCounts, session.MailboxPreflightOperatorSeverityCounts)
readiness["preflight_attention_status"] = preflightAttentionStatus
readiness["preflight_attention_reason"] = preflightAttentionReason
readiness["mailbox_after_sequence_read_total"] = session.MailboxAfterSequenceRead
readiness["mailbox_returned_total"] = session.MailboxReturnedTotal
readiness["mailbox_skipped_total"] = session.MailboxSkippedTotal
@@ -1315,6 +1717,66 @@ func remoteWorkspaceAdapterRuntimeReadinessLocked(s *RemoteWorkspaceFrameProbeSi
readiness["last_after_sequence"] = session.LastMailboxAfterSequence
readiness["last_returned_count"] = session.LastMailboxReturnedCount
readiness["last_skipped_count"] = session.LastMailboxSkippedCount
readiness["last_preflight_consumer_id"] = session.LastMailboxPreflightConsumerID
readiness["last_preflight_resume_from"] = session.LastMailboxPreflightResumeFrom
readiness["last_preflight_resume_sequence"] = session.LastMailboxPreflightResumeSequence
readiness["last_preflight_available_count"] = session.LastMailboxPreflightAvailableCount
readiness["last_preflight_returned_count"] = session.LastMailboxPreflightReturnedCount
readiness["last_preflight_skipped_count"] = session.LastMailboxPreflightSkippedCount
readiness["last_preflight_diagnostic_state"] = session.LastMailboxPreflightDiagnosticState
readiness["last_preflight_stale_cursor"] = session.LastMailboxPreflightStaleCursor
readiness["last_preflight_missing_dropped_count"] = session.LastMailboxPreflightMissingDropped
readiness["last_preflight_recommended_action"] = session.LastMailboxPreflightRecommendedAction
readiness["last_preflight_action_hints"] = append([]string(nil), session.LastMailboxPreflightActionHints...)
readiness["last_preflight_action_reason"] = session.LastMailboxPreflightActionReason
readiness["last_preflight_action_context"] = cloneStringAnyMap(session.LastMailboxPreflightActionContext)
readiness["last_preflight_operator_summary"] = session.LastMailboxPreflightOperatorSummary
readiness["last_preflight_operator_status"] = session.LastMailboxPreflightOperatorStatus
readiness["last_preflight_operator_severity"] = session.LastMailboxPreflightOperatorSeverity
readiness["last_preflight_operator_summary_fields"] = cloneStringAnyMap(session.LastMailboxPreflightOperatorFields)
if session.MailboxPreflightTotal > 0 {
remediationChecklist := remoteWorkspacePreflightRemediationChecklist(session.LastMailboxPreflightOperatorStatus, session.LastMailboxPreflightActionHints)
remediationChecklistSummary := remoteWorkspacePreflightRemediationChecklistSummary(remediationChecklist)
readiness["last_preflight"] = map[string]any{
"diagnostics_schema_version": "rap.remote_workspace_adapter_mailbox_preflight_diagnostics.v1",
"diagnostics_contract": []string{"retained_window", "remediation_checklist", "attention", "operator_counts"},
"diagnostics_features": map[string]bool{"retained_window": true, "remediation_checklist": true, "attention": true, "operator_counts": true},
"observed_at": session.LastMailboxPreflightAt.Format(time.RFC3339Nano),
"consumer_id": session.LastMailboxPreflightConsumerID,
"resume_from": session.LastMailboxPreflightResumeFrom,
"resume_sequence": session.LastMailboxPreflightResumeSequence,
"after_sequence": session.LastMailboxPreflightAfterSequence,
"available_count": session.LastMailboxPreflightAvailableCount,
"returned_count": session.LastMailboxPreflightReturnedCount,
"skipped_count": session.LastMailboxPreflightSkippedCount,
"first_sequence": session.LastMailboxPreflightFirstSequence,
"last_sequence": session.LastMailboxPreflightLastSequence,
"first_retained_sequence": session.LastMailboxPreflightFirstRetained,
"last_retained_sequence": session.LastMailboxPreflightLastRetained,
"mailbox_dropped_total": session.LastMailboxPreflightMailboxDropped,
"diagnostic_state": session.LastMailboxPreflightDiagnosticState,
"stale_cursor": session.LastMailboxPreflightStaleCursor,
"missing_dropped_count": session.LastMailboxPreflightMissingDropped,
"recommended_action": session.LastMailboxPreflightRecommendedAction,
"action_hints": append([]string(nil), session.LastMailboxPreflightActionHints...),
"action_reason": session.LastMailboxPreflightActionReason,
"action_context": cloneStringAnyMap(session.LastMailboxPreflightActionContext),
"remediation_checklist": remediationChecklist,
"remediation_checklist_status": remediationChecklistSummary["status"],
"remediation_checklist_counts": remediationChecklistSummary,
"operator_summary": session.LastMailboxPreflightOperatorSummary,
"operator_status": session.LastMailboxPreflightOperatorStatus,
"operator_severity": session.LastMailboxPreflightOperatorSeverity,
"operator_summary_fields": cloneStringAnyMap(session.LastMailboxPreflightOperatorFields),
"mailbox_preflight_total": session.MailboxPreflightTotal,
"mailbox_preflight_ack_total": session.MailboxPreflightAckTotal,
"mailbox_preflight_checkpoint_total": session.MailboxPreflightCheckpointTotal,
"preflight_attention_status": preflightAttentionStatus,
"preflight_attention_reason": preflightAttentionReason,
"operator_status_counts": cloneStringInt64Map(session.MailboxPreflightOperatorStatusCounts),
"operator_severity_counts": cloneStringInt64Map(session.MailboxPreflightOperatorSeverityCounts),
}
}
if !session.LastActivityAt.IsZero() {
readiness["last_activity_at"] = session.LastActivityAt.Format(time.RFC3339Nano)
}
@@ -1327,6 +1789,9 @@ func remoteWorkspaceAdapterRuntimeReadinessLocked(s *RemoteWorkspaceFrameProbeSi
if !session.LastMailboxConsumerAckAt.IsZero() {
readiness["last_consumer_ack_at"] = session.LastMailboxConsumerAckAt.Format(time.RFC3339Nano)
}
if !session.LastMailboxPreflightAt.IsZero() {
readiness["last_preflight_at"] = session.LastMailboxPreflightAt.Format(time.RFC3339Nano)
}
return readiness
}
@@ -1445,6 +1910,9 @@ func (s *RemoteWorkspaceFrameProbeSink) Report(now time.Time) map[string]any {
report["mailbox_after_sequence_read_total"] = s.mailboxAfterSequenceReadTotal
report["mailbox_returned_total"] = s.mailboxReturnedTotal
report["mailbox_skipped_total"] = s.mailboxSkippedTotal
report["mailbox_preflight_total"] = s.mailboxPreflightTotal
report["mailbox_preflight_ack_total"] = s.mailboxPreflightAckTotal
report["mailbox_preflight_checkpoint_total"] = s.mailboxPreflightCheckpointTotal
report["mailbox_consumer_capacity"] = DefaultRemoteWorkspaceAdapterMailboxConsumerCapacity
report["mailbox_consumer_count"] = countMailboxConsumersLocked(s.sessions)
report["mailbox_consumer_read_total"] = s.mailboxConsumerReadTotal
@@ -1467,6 +1935,30 @@ func (s *RemoteWorkspaceFrameProbeSink) Report(now time.Time) map[string]any {
report["last_mailbox_resume_sequence"] = s.lastMailboxResumeSequence
report["last_mailbox_resume_consumer_id"] = s.lastMailboxResumeConsumerID
}
if s.mailboxPreflightTotal > 0 {
report["last_mailbox_preflight_at"] = s.lastMailboxPreflightAt
report["last_mailbox_preflight_adapter_session_id"] = s.lastMailboxPreflightAdapterSessionID
report["last_mailbox_preflight_consumer_id"] = s.lastMailboxPreflightConsumerID
report["last_mailbox_preflight_resume_from"] = s.lastMailboxPreflightResumeFrom
report["last_mailbox_preflight_resume_sequence"] = s.lastMailboxPreflightResumeSequence
report["last_mailbox_preflight_after_sequence"] = s.lastMailboxPreflightAfterSequence
report["last_mailbox_preflight_available_count"] = s.lastMailboxPreflightAvailableCount
report["last_mailbox_preflight_returned_count"] = s.lastMailboxPreflightReturnedCount
report["last_mailbox_preflight_skipped_count"] = s.lastMailboxPreflightSkippedCount
report["last_mailbox_preflight_first_sequence"] = s.lastMailboxPreflightFirstSequence
report["last_mailbox_preflight_last_sequence"] = s.lastMailboxPreflightLastSequence
report["last_mailbox_preflight_diagnostic_state"] = s.lastMailboxPreflightDiagnosticState
report["last_mailbox_preflight_stale_cursor"] = s.lastMailboxPreflightStaleCursor
report["last_mailbox_preflight_missing_dropped_count"] = s.lastMailboxPreflightMissingDropped
report["last_mailbox_preflight_recommended_action"] = s.lastMailboxPreflightRecommendedAction
report["last_mailbox_preflight_action_hints"] = append([]string(nil), s.lastMailboxPreflightActionHints...)
report["last_mailbox_preflight_action_reason"] = s.lastMailboxPreflightActionReason
report["last_mailbox_preflight_action_context"] = cloneStringAnyMap(s.lastMailboxPreflightActionContext)
report["last_mailbox_preflight_operator_summary"] = s.lastMailboxPreflightOperatorSummary
report["last_mailbox_preflight_operator_status"] = s.lastMailboxPreflightOperatorStatus
report["last_mailbox_preflight_operator_severity"] = s.lastMailboxPreflightOperatorSeverity
report["last_mailbox_preflight_operator_summary_fields"] = cloneStringAnyMap(s.lastMailboxPreflightOperatorFields)
}
if s.mailboxConsumerReadTotal > 0 {
report["last_mailbox_consumer_id"] = s.lastMailboxConsumerID
report["last_mailbox_consumer_read_at"] = s.lastMailboxConsumerReadAt
@@ -1520,6 +2012,11 @@ func (s *RemoteWorkspaceFrameProbeSink) Report(now time.Time) map[string]any {
report["current_session_mailbox_after_sequence_read_total"] = session.MailboxAfterSequenceRead
report["current_session_mailbox_returned_total"] = session.MailboxReturnedTotal
report["current_session_mailbox_skipped_total"] = session.MailboxSkippedTotal
report["current_session_mailbox_preflight_total"] = session.MailboxPreflightTotal
report["current_session_mailbox_preflight_ack_total"] = session.MailboxPreflightAckTotal
report["current_session_mailbox_preflight_checkpoint_total"] = session.MailboxPreflightCheckpointTotal
report["current_session_mailbox_preflight_operator_status_counts"] = cloneStringInt64Map(session.MailboxPreflightOperatorStatusCounts)
report["current_session_mailbox_preflight_operator_severity_counts"] = cloneStringInt64Map(session.MailboxPreflightOperatorSeverityCounts)
report["current_session_mailbox_consumer_count"] = len(session.MailboxConsumers)
report["current_session_mailbox_consumer_read_total"] = session.MailboxConsumerReadTotal
report["current_session_mailbox_consumer_ack_total"] = session.MailboxConsumerAckTotal
@@ -1549,6 +2046,29 @@ func (s *RemoteWorkspaceFrameProbeSink) Report(now time.Time) map[string]any {
report["current_session_last_mailbox_resume_sequence"] = session.LastMailboxResumeSequence
report["current_session_last_mailbox_resume_consumer_id"] = session.LastMailboxResumeConsumerID
}
if session.MailboxPreflightTotal > 0 {
report["current_session_last_mailbox_preflight_at"] = session.LastMailboxPreflightAt.Format(time.RFC3339Nano)
report["current_session_last_mailbox_preflight_consumer_id"] = session.LastMailboxPreflightConsumerID
report["current_session_last_mailbox_preflight_resume_from"] = session.LastMailboxPreflightResumeFrom
report["current_session_last_mailbox_preflight_resume_sequence"] = session.LastMailboxPreflightResumeSequence
report["current_session_last_mailbox_preflight_after_sequence"] = session.LastMailboxPreflightAfterSequence
report["current_session_last_mailbox_preflight_available_count"] = session.LastMailboxPreflightAvailableCount
report["current_session_last_mailbox_preflight_returned_count"] = session.LastMailboxPreflightReturnedCount
report["current_session_last_mailbox_preflight_skipped_count"] = session.LastMailboxPreflightSkippedCount
report["current_session_last_mailbox_preflight_first_sequence"] = session.LastMailboxPreflightFirstSequence
report["current_session_last_mailbox_preflight_last_sequence"] = session.LastMailboxPreflightLastSequence
report["current_session_last_mailbox_preflight_diagnostic_state"] = session.LastMailboxPreflightDiagnosticState
report["current_session_last_mailbox_preflight_stale_cursor"] = session.LastMailboxPreflightStaleCursor
report["current_session_last_mailbox_preflight_missing_dropped_count"] = session.LastMailboxPreflightMissingDropped
report["current_session_last_mailbox_preflight_recommended_action"] = session.LastMailboxPreflightRecommendedAction
report["current_session_last_mailbox_preflight_action_hints"] = append([]string(nil), session.LastMailboxPreflightActionHints...)
report["current_session_last_mailbox_preflight_action_reason"] = session.LastMailboxPreflightActionReason
report["current_session_last_mailbox_preflight_action_context"] = cloneStringAnyMap(session.LastMailboxPreflightActionContext)
report["current_session_last_mailbox_preflight_operator_summary"] = session.LastMailboxPreflightOperatorSummary
report["current_session_last_mailbox_preflight_operator_status"] = session.LastMailboxPreflightOperatorStatus
report["current_session_last_mailbox_preflight_operator_severity"] = session.LastMailboxPreflightOperatorSeverity
report["current_session_last_mailbox_preflight_operator_summary_fields"] = cloneStringAnyMap(session.LastMailboxPreflightOperatorFields)
}
if !session.LastBackpressureAt.IsZero() {
report["current_session_last_backpressure_at"] = session.LastBackpressureAt.Format(time.RFC3339Nano)
report["current_session_last_backpressure_reason"] = session.LastReason
@@ -1643,6 +1643,44 @@ func TestRemoteWorkspaceAdapterSessionControlEndpointClosesSession(t *testing.T)
report["last_session_control_state"] != "closed" {
t.Fatalf("control report = %+v", report)
}
readiness, ok := report["adapter_runtime_readiness"].(map[string]any)
if !ok {
t.Fatalf("adapter runtime readiness missing from control report = %+v", report)
}
if readiness["schema_version"] != "rap.remote_workspace_adapter_runtime_readiness.v1" ||
readiness["status"] != "idle" ||
readiness["diagnostic_state"] != "last_session_terminal_or_expired" ||
readiness["ready"] != false ||
readiness["active_session_count"] != 0 ||
readiness["last_adapter_session_id"] != "rap-rw-adapter-session-aaaaaaaaaaaaaaaaaaaaaaaa" ||
readiness["last_session_state"] != "closed" {
t.Fatalf("invalid no-active-session readiness after close = %+v", readiness)
}
if _, ok := readiness["adapter_session_id"]; ok {
t.Fatalf("adapter_session_id should be absent without active session = %+v", readiness)
}
if _, ok := readiness["last_preflight"]; ok {
t.Fatalf("last_preflight should be absent without active session = %+v", readiness)
}
terminalSummary, ok := readiness["terminal_session_summary"].(map[string]any)
if !ok {
t.Fatalf("terminal session summary missing after close = %+v", readiness)
}
if terminalSummary["adapter_session_id"] != "rap-rw-adapter-session-aaaaaaaaaaaaaaaaaaaaaaaa" ||
terminalSummary["schema_version"] != "rap.remote_workspace_adapter_terminal_session_summary.v1" ||
!stringAnySliceContains(terminalSummary["summary_contract"], "adapter_session_id") ||
!stringAnySliceContains(terminalSummary["summary_contract"], "session_state") ||
!stringAnySliceContains(terminalSummary["summary_contract"], "reason") ||
!stringAnySliceContains(terminalSummary["summary_contract"], "controlled_at") ||
!boolMapValue(terminalSummary["summary_features"], "adapter_session_id") ||
!boolMapValue(terminalSummary["summary_features"], "session_state") ||
!boolMapValue(terminalSummary["summary_features"], "reason") ||
!boolMapValue(terminalSummary["summary_features"], "controlled_at") ||
terminalSummary["session_state"] != "closed" ||
terminalSummary["reason"] != "unit test close" ||
terminalSummary["controlled_at"] == "" {
t.Fatalf("invalid terminal session summary after close = %+v", terminalSummary)
}
resp, err = http.Post(controlURL, "application/json", bytes.NewReader([]byte(`{"action":"close","reason":"repeat close"}`)))
if err != nil {
@@ -1665,6 +1703,255 @@ func TestRemoteWorkspaceAdapterSessionControlEndpointClosesSession(t *testing.T)
}
}
func TestRemoteWorkspaceAdapterReadinessBeforeAnySessionHasNoTerminalSummary(t *testing.T) {
sink := NewRemoteWorkspaceFrameProbeSink()
report := sink.Report(time.Now().UTC())
readiness, ok := report["adapter_runtime_readiness"].(map[string]any)
if !ok {
t.Fatalf("adapter runtime readiness missing from report = %+v", report)
}
if readiness["schema_version"] != "rap.remote_workspace_adapter_runtime_readiness.v1" ||
readiness["status"] != "idle" ||
readiness["diagnostic_state"] != "waiting_for_session" ||
readiness["ready"] != false ||
readiness["active_session_count"] != 0 ||
readiness["terminal_session_count"] != 0 {
t.Fatalf("invalid empty readiness = %+v", readiness)
}
if _, ok := readiness["last_adapter_session_id"]; ok {
t.Fatalf("last_adapter_session_id should be absent before any session = %+v", readiness)
}
if _, ok := readiness["last_session_state"]; ok {
t.Fatalf("last_session_state should be absent before any session = %+v", readiness)
}
if _, ok := readiness["terminal_session_summary"]; ok {
t.Fatalf("terminal_session_summary should be absent before terminal history = %+v", readiness)
}
noSessionSummary, ok := readiness["no_session_summary"].(map[string]any)
if !ok {
t.Fatalf("no_session_summary should be present before any session = %+v", readiness)
}
if noSessionSummary["schema_version"] != "rap.remote_workspace_adapter_no_session_summary.v1" ||
!stringAnySliceContains(noSessionSummary["summary_contract"], "status") ||
!stringAnySliceContains(noSessionSummary["summary_contract"], "diagnostic_state") ||
!stringAnySliceContains(noSessionSummary["summary_contract"], "active_session_count") ||
!stringAnySliceContains(noSessionSummary["summary_contract"], "terminal_session_count") ||
!boolMapValue(noSessionSummary["summary_features"], "status") ||
!boolMapValue(noSessionSummary["summary_features"], "diagnostic_state") ||
!boolMapValue(noSessionSummary["summary_features"], "active_session_count") ||
!boolMapValue(noSessionSummary["summary_features"], "terminal_session_count") ||
noSessionSummary["status"] != "idle" ||
noSessionSummary["diagnostic_state"] != "waiting_for_session" ||
noSessionSummary["active_session_count"] != 0 ||
noSessionSummary["terminal_session_count"] != 0 {
t.Fatalf("invalid no-session summary before any session = %+v", noSessionSummary)
}
if _, ok := readiness["last_preflight"]; ok {
t.Fatalf("last_preflight should be absent before any session = %+v", readiness)
}
}
func TestRemoteWorkspaceAdapterReadinessSummaryExclusivity(t *testing.T) {
sink := NewRemoteWorkspaceFrameProbeSink()
freshReport := sink.Report(time.Now().UTC())
freshReadiness, ok := freshReport["adapter_runtime_readiness"].(map[string]any)
if !ok {
t.Fatalf("fresh readiness missing from report = %+v", freshReport)
}
if _, ok := freshReadiness["no_session_summary"]; !ok {
t.Fatalf("fresh readiness should include no_session_summary = %+v", freshReadiness)
}
if _, ok := freshReadiness["terminal_session_summary"]; ok {
t.Fatalf("fresh readiness should not include terminal_session_summary = %+v", freshReadiness)
}
sessionID := "rap-rw-adapter-session-d1d1d1d1d1d1d1d1d1d1d1d1"
delivery := RemoteWorkspaceFrameBatchDelivery{
ClusterID: "cluster-1",
ChannelID: "channel-rw",
ResourceID: "workspace-exclusivity",
ServiceClass: FabricServiceClassRemoteWorkspace,
ChannelClass: FabricServiceChannelInteractive,
AdapterContractID: "rap.rdp_worker.remote_workspace_adapter_contract_probe.v1",
AdapterSessionID: sessionID,
Frames: []RemoteWorkspaceFrameProbeRecord{{
Channel: "display",
Direction: "adapter_to_client",
Droppable: true,
}},
}
if _, err := sink.AcceptRemoteWorkspaceFrameBatchProbe(context.Background(), delivery); err != nil {
t.Fatalf("accept frame batch: %v", err)
}
activeReport := sink.Report(time.Now().UTC())
activeReadiness, ok := activeReport["adapter_runtime_readiness"].(map[string]any)
if !ok {
t.Fatalf("active readiness missing from report = %+v", activeReport)
}
if activeReadiness["adapter_session_id"] != sessionID ||
activeReadiness["active_session_count"] != 1 {
t.Fatalf("invalid active readiness = %+v", activeReadiness)
}
if _, ok := activeReadiness["no_session_summary"]; ok {
t.Fatalf("active readiness should not include no_session_summary = %+v", activeReadiness)
}
if _, ok := activeReadiness["terminal_session_summary"]; ok {
t.Fatalf("active readiness should not include terminal_session_summary = %+v", activeReadiness)
}
server := httptest.NewServer(Server{RemoteWorkspaceFrameSink: sink}.Handler())
defer server.Close()
body := bytes.NewReader([]byte(`{"action":"close","reason":"unit summary exclusivity close"}`))
resp, err := http.Post(server.URL+"/mesh/v1/remote-workspace/adapter-sessions/"+sessionID+"/control", "application/json", body)
if err != nil {
t.Fatalf("post control: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
raw, _ := io.ReadAll(resp.Body)
t.Fatalf("status = %d body=%s", resp.StatusCode, string(raw))
}
terminalReport := sink.Report(time.Now().UTC())
terminalReadiness, ok := terminalReport["adapter_runtime_readiness"].(map[string]any)
if !ok {
t.Fatalf("terminal readiness missing from report = %+v", terminalReport)
}
if _, ok := terminalReadiness["terminal_session_summary"]; !ok {
t.Fatalf("terminal readiness should include terminal_session_summary = %+v", terminalReadiness)
}
if _, ok := terminalReadiness["no_session_summary"]; ok {
t.Fatalf("terminal readiness should not include no_session_summary = %+v", terminalReadiness)
}
}
func TestRemoteWorkspaceAdapterSessionControlTerminalReadinessStates(t *testing.T) {
tests := []struct {
action string
sessionID string
wantState string
wantClosed int64
wantExpired int64
wantReset int64
wantPrevState string
}{
{
action: "expire",
sessionID: "rap-rw-adapter-session-b0b0b0b0b0b0b0b0b0b0b0b0",
wantState: "expired",
wantClosed: 1,
wantExpired: 1,
wantPrevState: "probe_bound",
},
{
action: "reset",
sessionID: "rap-rw-adapter-session-c0c0c0c0c0c0c0c0c0c0c0c0",
wantState: "reset",
wantClosed: 1,
wantReset: 1,
wantPrevState: "probe_bound",
},
}
for _, tt := range tests {
t.Run(tt.action, func(t *testing.T) {
sink := NewRemoteWorkspaceFrameProbeSink()
delivery := RemoteWorkspaceFrameBatchDelivery{
ClusterID: "cluster-1",
ChannelID: "channel-rw",
ResourceID: "workspace-" + tt.action,
ServiceClass: FabricServiceClassRemoteWorkspace,
ChannelClass: FabricServiceChannelInteractive,
AdapterContractID: "rap.rdp_worker.remote_workspace_adapter_contract_probe.v1",
AdapterSessionID: tt.sessionID,
Frames: []RemoteWorkspaceFrameProbeRecord{{
Channel: "display",
Direction: "adapter_to_client",
Droppable: true,
}},
}
if _, err := sink.AcceptRemoteWorkspaceFrameBatchProbe(context.Background(), delivery); err != nil {
t.Fatalf("accept frame batch: %v", err)
}
server := httptest.NewServer(Server{RemoteWorkspaceFrameSink: sink}.Handler())
defer server.Close()
body := bytes.NewReader([]byte(fmt.Sprintf(`{"action":%q,"reason":"unit terminal readiness"}`, tt.action)))
resp, err := http.Post(server.URL+"/mesh/v1/remote-workspace/adapter-sessions/"+tt.sessionID+"/control", "application/json", body)
if err != nil {
t.Fatalf("post control: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
raw, _ := io.ReadAll(resp.Body)
t.Fatalf("status = %d body=%s", resp.StatusCode, string(raw))
}
var result RemoteWorkspaceAdapterSessionControlResult
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
t.Fatalf("decode control result: %v", err)
}
if !result.Accepted ||
result.Action != tt.action ||
result.AdapterSessionID != tt.sessionID ||
result.PreviousState != tt.wantPrevState ||
result.SessionState != tt.wantState ||
result.ActiveSessions != 0 {
t.Fatalf("control result = %+v", result)
}
report := sink.Report(time.Now().UTC())
if report["active_session_count"] != 0 ||
report["session_closed_total"] != tt.wantClosed ||
report["session_expired_total"] != tt.wantExpired ||
report["session_reset_total"] != tt.wantReset ||
report["last_controlled_adapter_session_id"] != tt.sessionID ||
report["last_session_control_action"] != tt.action ||
report["last_session_control_state"] != tt.wantState {
t.Fatalf("terminal control report = %+v", report)
}
readiness, ok := report["adapter_runtime_readiness"].(map[string]any)
if !ok {
t.Fatalf("adapter runtime readiness missing from report = %+v", report)
}
if readiness["status"] != "idle" ||
readiness["diagnostic_state"] != "last_session_terminal_or_expired" ||
readiness["ready"] != false ||
readiness["active_session_count"] != 0 ||
readiness["last_adapter_session_id"] != tt.sessionID ||
readiness["last_session_state"] != tt.wantState {
t.Fatalf("invalid terminal readiness = %+v", readiness)
}
if _, ok := readiness["adapter_session_id"]; ok {
t.Fatalf("adapter_session_id should be absent without active session = %+v", readiness)
}
if _, ok := readiness["last_preflight"]; ok {
t.Fatalf("last_preflight should be absent without active session = %+v", readiness)
}
if _, ok := readiness["no_session_summary"]; ok {
t.Fatalf("no_session_summary should be absent for terminal session history = %+v", readiness)
}
terminalSummary, ok := readiness["terminal_session_summary"].(map[string]any)
if !ok {
t.Fatalf("terminal session summary missing = %+v", readiness)
}
if terminalSummary["adapter_session_id"] != tt.sessionID ||
terminalSummary["schema_version"] != "rap.remote_workspace_adapter_terminal_session_summary.v1" ||
!stringAnySliceContains(terminalSummary["summary_contract"], "adapter_session_id") ||
!stringAnySliceContains(terminalSummary["summary_contract"], "session_state") ||
!stringAnySliceContains(terminalSummary["summary_contract"], "reason") ||
!stringAnySliceContains(terminalSummary["summary_contract"], "controlled_at") ||
!boolMapValue(terminalSummary["summary_features"], "adapter_session_id") ||
!boolMapValue(terminalSummary["summary_features"], "session_state") ||
!boolMapValue(terminalSummary["summary_features"], "reason") ||
!boolMapValue(terminalSummary["summary_features"], "controlled_at") ||
terminalSummary["session_state"] != tt.wantState ||
terminalSummary["reason"] != "unit terminal readiness" ||
terminalSummary["controlled_at"] == "" {
t.Fatalf("invalid terminal session summary = %+v", terminalSummary)
}
})
}
}
func TestRemoteWorkspaceAdapterSessionControlRejectsInvalidRequests(t *testing.T) {
sink := NewRemoteWorkspaceFrameProbeSink()
server := httptest.NewServer(Server{RemoteWorkspaceFrameSink: sink}.Handler())
@@ -3064,6 +3351,19 @@ func TestRemoteWorkspaceAdapterSessionMailboxPreflightIsReadOnly(t *testing.T) {
}
if preflight.ResumeFrom != "checkpoint" ||
preflight.ResumeSequence != 2 ||
preflight.DiagnosticState != "ready" ||
preflight.RecommendedAction != "resume_from_cursor" ||
preflight.ActionReason != "cursor_window_available" ||
preflight.OperatorSummary != "consumer cursor can resume from requested window" ||
preflight.OperatorStatus != "ready_to_resume" ||
preflight.OperatorSeverity != "ok" ||
anyInt64(preflight.ActionContext["resume_sequence"]) != 2 ||
anyInt64(preflight.ActionContext["first_retained_sequence"]) != 1 ||
preflight.OperatorSummaryFields["diagnostic_state"] != "ready" ||
preflight.OperatorSummaryFields["recommended_action"] != "resume_from_cursor" ||
preflight.OperatorSummaryFields["operator_status"] != "ready_to_resume" ||
preflight.OperatorSummaryFields["operator_severity"] != "ok" ||
!stringSliceContains(preflight.ActionHints, "resume_from_requested_cursor") ||
preflight.ExpectedAvailableCount != 1 ||
preflight.ExpectedReturnedCount != 1 ||
preflight.ExpectedSkippedCount != 2 ||
@@ -3079,6 +3379,547 @@ func TestRemoteWorkspaceAdapterSessionMailboxPreflightIsReadOnly(t *testing.T) {
reportAfter["current_session_mailbox_consumer_ack_total"] != reportBefore["current_session_mailbox_consumer_ack_total"] {
t.Fatalf("preflight mutated report before=%+v after=%+v", reportBefore, reportAfter)
}
if reportAfter["mailbox_preflight_total"] != int64(2) ||
reportAfter["mailbox_preflight_ack_total"] != int64(1) ||
reportAfter["mailbox_preflight_checkpoint_total"] != int64(1) ||
reportAfter["last_mailbox_preflight_adapter_session_id"] != sessionID ||
reportAfter["last_mailbox_preflight_consumer_id"] != "rdp-worker-probe" ||
reportAfter["last_mailbox_preflight_resume_from"] != "checkpoint" ||
reportAfter["last_mailbox_preflight_resume_sequence"] != int64(2) ||
reportAfter["last_mailbox_preflight_available_count"] != 1 ||
reportAfter["last_mailbox_preflight_returned_count"] != 1 ||
reportAfter["last_mailbox_preflight_skipped_count"] != 2 ||
reportAfter["current_session_mailbox_preflight_total"] != int64(2) ||
reportAfter["current_session_mailbox_preflight_ack_total"] != int64(1) ||
reportAfter["current_session_mailbox_preflight_checkpoint_total"] != int64(1) ||
mapInt64Value(reportAfter["current_session_mailbox_preflight_operator_status_counts"], "ready_to_resume") != 2 ||
mapInt64Value(reportAfter["current_session_mailbox_preflight_operator_severity_counts"], "ok") != 2 ||
reportAfter["current_session_last_mailbox_preflight_resume_from"] != "checkpoint" ||
reportAfter["current_session_last_mailbox_preflight_resume_sequence"] != int64(2) ||
reportAfter["current_session_last_mailbox_preflight_returned_count"] != 1 ||
reportAfter["current_session_last_mailbox_preflight_recommended_action"] != "resume_from_cursor" ||
reportAfter["last_mailbox_preflight_operator_summary"] != "consumer cursor can resume from requested window" ||
reportAfter["last_mailbox_preflight_operator_status"] != "ready_to_resume" ||
reportAfter["last_mailbox_preflight_operator_severity"] != "ok" ||
reportAfter["current_session_last_mailbox_preflight_operator_summary"] != "consumer cursor can resume from requested window" ||
reportAfter["current_session_last_mailbox_preflight_operator_status"] != "ready_to_resume" ||
reportAfter["current_session_last_mailbox_preflight_operator_severity"] != "ok" {
t.Fatalf("invalid preflight telemetry report = %+v", reportAfter)
}
readiness, ok := reportAfter["adapter_runtime_readiness"].(map[string]any)
if !ok {
t.Fatalf("adapter runtime readiness missing from report = %+v", reportAfter)
}
if readiness["mailbox_preflight_total"] != int64(2) ||
readiness["last_preflight_consumer_id"] != "rdp-worker-probe" ||
readiness["last_preflight_resume_from"] != "checkpoint" ||
readiness["last_preflight_resume_sequence"] != int64(2) ||
readiness["last_preflight_returned_count"] != 1 ||
readiness["last_preflight_skipped_count"] != 2 ||
readiness["last_preflight_recommended_action"] != "resume_from_cursor" ||
readiness["last_preflight_action_reason"] != "cursor_window_available" ||
readiness["last_preflight_operator_summary"] != "consumer cursor can resume from requested window" ||
readiness["last_preflight_operator_status"] != "ready_to_resume" ||
readiness["last_preflight_operator_severity"] != "ok" ||
mapInt64Value(readiness["mailbox_preflight_operator_status_counts"], "ready_to_resume") != 2 ||
mapInt64Value(readiness["mailbox_preflight_operator_severity_counts"], "ok") != 2 ||
readiness["preflight_attention_status"] != "clean" ||
readiness["preflight_attention_reason"] != "no_resync_required_preflight_observed" {
t.Fatalf("invalid preflight readiness = %+v", readiness)
}
lastPreflight, ok := readiness["last_preflight"].(map[string]any)
if !ok {
t.Fatalf("last preflight rollup missing from readiness = %+v", readiness)
}
if lastPreflight["consumer_id"] != "rdp-worker-probe" ||
lastPreflight["diagnostics_schema_version"] != "rap.remote_workspace_adapter_mailbox_preflight_diagnostics.v1" ||
!stringAnySliceContains(lastPreflight["diagnostics_contract"], "retained_window") ||
!stringAnySliceContains(lastPreflight["diagnostics_contract"], "remediation_checklist") ||
!stringAnySliceContains(lastPreflight["diagnostics_contract"], "attention") ||
!stringAnySliceContains(lastPreflight["diagnostics_contract"], "operator_counts") ||
!boolMapValue(lastPreflight["diagnostics_features"], "retained_window") ||
!boolMapValue(lastPreflight["diagnostics_features"], "remediation_checklist") ||
!boolMapValue(lastPreflight["diagnostics_features"], "attention") ||
!boolMapValue(lastPreflight["diagnostics_features"], "operator_counts") ||
lastPreflight["resume_from"] != "checkpoint" ||
lastPreflight["operator_status"] != "ready_to_resume" ||
lastPreflight["operator_severity"] != "ok" ||
lastPreflight["recommended_action"] != "resume_from_cursor" ||
!preflightChecklistContains(lastPreflight["remediation_checklist"], "resume_from_requested_cursor", true, true) ||
lastPreflight["remediation_checklist_status"] != "ready" ||
anyInt64(preflightChecklistCountsValue(lastPreflight["remediation_checklist_counts"], "required_count")) != 1 ||
anyInt64(preflightChecklistCountsValue(lastPreflight["remediation_checklist_counts"], "satisfied_count")) != 1 ||
anyInt64(preflightChecklistCountsValue(lastPreflight["remediation_checklist_counts"], "pending_count")) != 0 ||
mapInt64Value(lastPreflight["operator_status_counts"], "ready_to_resume") != 2 ||
mapInt64Value(lastPreflight["operator_severity_counts"], "ok") != 2 ||
lastPreflight["preflight_attention_status"] != "clean" ||
lastPreflight["preflight_attention_reason"] != "no_resync_required_preflight_observed" ||
anyInt64(lastPreflight["resume_sequence"]) != 2 ||
anyInt64(lastPreflight["first_retained_sequence"]) != 1 ||
anyInt64(lastPreflight["last_retained_sequence"]) != 3 ||
anyInt64(lastPreflight["mailbox_dropped_total"]) != 0 ||
anyInt64(lastPreflight["mailbox_preflight_total"]) != 2 {
t.Fatalf("invalid last preflight rollup = %+v", lastPreflight)
}
}
func TestRemoteWorkspaceAdapterSessionReadinessBeforePreflight(t *testing.T) {
sink := NewRemoteWorkspaceFrameProbeSink()
sessionID := "rap-rw-adapter-session-a0a0a0a0a0a0a0a0a0a0a0a0"
delivery := RemoteWorkspaceFrameBatchDelivery{
ClusterID: "cluster-1",
ChannelID: "channel-rw",
ResourceID: "workspace-before-preflight",
ServiceClass: FabricServiceClassRemoteWorkspace,
ChannelClass: FabricServiceChannelInteractive,
AdapterContractID: "rap.rdp_worker.remote_workspace_adapter_contract_probe.v1",
AdapterSessionID: sessionID,
Frames: []RemoteWorkspaceFrameProbeRecord{{
Channel: "display",
Direction: "adapter_to_client",
Droppable: true,
}},
}
if _, err := sink.AcceptRemoteWorkspaceFrameBatchProbe(context.Background(), delivery); err != nil {
t.Fatalf("accept frame batch: %v", err)
}
report := sink.Report(time.Now().UTC())
if report["mailbox_preflight_total"] != int64(0) ||
report["current_session_mailbox_preflight_total"] != int64(0) {
t.Fatalf("unexpected preflight totals before preflight = %+v", report)
}
readiness, ok := report["adapter_runtime_readiness"].(map[string]any)
if !ok {
t.Fatalf("adapter runtime readiness missing from report = %+v", report)
}
if readiness["adapter_session_id"] != sessionID ||
readiness["mailbox_preflight_total"] != int64(0) ||
readiness["preflight_attention_status"] != "unknown" ||
readiness["preflight_attention_reason"] != "no_preflight_observed" {
t.Fatalf("invalid no-preflight readiness = %+v", readiness)
}
if _, ok := readiness["last_preflight"]; ok {
t.Fatalf("last preflight rollup should be absent before preflight = %+v", readiness["last_preflight"])
}
if readiness["last_preflight_diagnostic_state"] != "" ||
readiness["last_preflight_recommended_action"] != "" ||
len(readiness["last_preflight_action_hints"].([]string)) != 0 {
t.Fatalf("last preflight flat fields should be empty before preflight = %+v", readiness)
}
}
func TestRemoteWorkspaceAdapterSessionMailboxPreflightReportsStaleCursorGap(t *testing.T) {
sink := NewRemoteWorkspaceFrameProbeSink()
sessionID := "rap-rw-adapter-session-adadadadadadadadadadadad"
delivery := RemoteWorkspaceFrameBatchDelivery{
ClusterID: "cluster-1",
ChannelID: "channel-rw",
ResourceID: "workspace-stale-0",
ServiceClass: FabricServiceClassRemoteWorkspace,
ChannelClass: FabricServiceChannelInteractive,
AdapterContractID: "rap.rdp_worker.remote_workspace_adapter_contract_probe.v1",
AdapterSessionID: sessionID,
Frames: []RemoteWorkspaceFrameProbeRecord{{
Channel: "display",
Direction: "adapter_to_client",
Droppable: true,
}},
}
if _, err := sink.AcceptRemoteWorkspaceFrameBatchProbe(context.Background(), delivery); err != nil {
t.Fatalf("accept initial frame batch: %v", err)
}
server := httptest.NewServer(Server{RemoteWorkspaceFrameSink: sink}.Handler())
defer server.Close()
resp, err := http.Get(server.URL + "/mesh/v1/remote-workspace/adapter-sessions/" + sessionID + "/mailbox?consumer_id=rdp-worker-probe&ack_sequence=1&limit=1")
if err != nil {
t.Fatalf("seed ack cursor: %v", err)
}
resp.Body.Close()
for i := 1; i <= DefaultRemoteWorkspaceAdapterMailboxCapacity+2; i++ {
delivery.ResourceID = fmt.Sprintf("workspace-stale-%d", i)
if _, err := sink.AcceptRemoteWorkspaceFrameBatchProbe(context.Background(), delivery); err != nil {
t.Fatalf("accept overflow frame batch %d: %v", i, err)
}
}
resp, err = http.Get(server.URL + "/mesh/v1/remote-workspace/adapter-sessions/" + sessionID + "/mailbox/preflight?consumer_id=rdp-worker-probe&resume_from=ack&limit=3")
if err != nil {
t.Fatalf("get stale preflight: %v", err)
}
defer resp.Body.Close()
var preflight RemoteWorkspaceAdapterMailboxPreflightSnapshot
if err := json.NewDecoder(resp.Body).Decode(&preflight); err != nil {
t.Fatalf("decode stale preflight: %v", err)
}
if preflight.ResumeFrom != "ack" ||
preflight.ResumeSequence != 1 ||
preflight.MailboxDepth != DefaultRemoteWorkspaceAdapterMailboxCapacity ||
preflight.MailboxDropped != 3 ||
preflight.ExpectedAvailableCount != DefaultRemoteWorkspaceAdapterMailboxCapacity ||
preflight.ExpectedReturnedCount != 3 ||
preflight.ExpectedSkippedCount != 0 ||
preflight.FirstExpectedSequence != 4 ||
preflight.LastExpectedSequence != 6 ||
preflight.FirstRetainedSequence != 4 ||
preflight.LastRetainedSequence != 19 ||
preflight.DiagnosticState != "stale_cursor_gap" ||
!preflight.StaleCursor ||
preflight.MissingDroppedCount != 2 ||
preflight.RecommendedAction != "reset_consumer_and_resync" ||
preflight.ActionReason != "consumer_cursor_before_first_retained_sequence" ||
preflight.OperatorSummary != "stale cursor gap: reset consumer and resync before resume" ||
preflight.OperatorStatus != "resync_required" ||
preflight.OperatorSeverity != "warn" ||
anyInt64(preflight.ActionContext["resume_sequence"]) != 1 ||
anyInt64(preflight.ActionContext["first_retained_sequence"]) != 4 ||
anyInt64(preflight.ActionContext["missing_dropped_count"]) != 2 ||
preflight.OperatorSummaryFields["diagnostic_state"] != "stale_cursor_gap" ||
preflight.OperatorSummaryFields["recommended_action"] != "reset_consumer_and_resync" ||
preflight.OperatorSummaryFields["operator_status"] != "resync_required" ||
preflight.OperatorSummaryFields["operator_severity"] != "warn" ||
anyInt64(preflight.OperatorSummaryFields["missing_dropped_count"]) != 2 ||
!stringSliceContains(preflight.ActionHints, "reset_consumer_cursor") ||
!stringSliceContains(preflight.ActionHints, "request_full_adapter_resync") ||
!stringSliceContains(preflight.ActionHints, "resume_from_checkpoint_after_resync") {
t.Fatalf("stale preflight = %+v", preflight)
}
resp, err = http.Get(server.URL + "/mesh/v1/remote-workspace/adapter-sessions/" + sessionID + "/mailbox/preflight?consumer_id=rdp-worker-probe&resume_from=ack&limit=3")
if err != nil {
t.Fatalf("get repeated stale preflight: %v", err)
}
resp.Body.Close()
report := sink.Report(time.Now().UTC())
if report["last_mailbox_preflight_diagnostic_state"] != "stale_cursor_gap" ||
report["last_mailbox_preflight_stale_cursor"] != true ||
report["last_mailbox_preflight_missing_dropped_count"] != 2 ||
report["last_mailbox_preflight_recommended_action"] != "reset_consumer_and_resync" ||
report["last_mailbox_preflight_action_reason"] != "consumer_cursor_before_first_retained_sequence" ||
report["last_mailbox_preflight_operator_summary"] != "stale cursor gap: reset consumer and resync before resume" ||
report["last_mailbox_preflight_operator_status"] != "resync_required" ||
report["last_mailbox_preflight_operator_severity"] != "warn" ||
report["current_session_last_mailbox_preflight_diagnostic_state"] != "stale_cursor_gap" ||
report["current_session_last_mailbox_preflight_stale_cursor"] != true ||
report["current_session_last_mailbox_preflight_missing_dropped_count"] != 2 ||
report["current_session_last_mailbox_preflight_recommended_action"] != "reset_consumer_and_resync" ||
report["current_session_last_mailbox_preflight_operator_summary"] != "stale cursor gap: reset consumer and resync before resume" ||
report["current_session_last_mailbox_preflight_operator_status"] != "resync_required" ||
report["current_session_last_mailbox_preflight_operator_severity"] != "warn" ||
mapInt64Value(report["current_session_mailbox_preflight_operator_status_counts"], "resync_required") != 2 ||
mapInt64Value(report["current_session_mailbox_preflight_operator_severity_counts"], "warn") != 2 {
t.Fatalf("stale preflight report = %+v", report)
}
readiness, ok := report["adapter_runtime_readiness"].(map[string]any)
if !ok {
t.Fatalf("adapter runtime readiness missing from report = %+v", report)
}
if readiness["last_preflight_diagnostic_state"] != "stale_cursor_gap" ||
readiness["last_preflight_stale_cursor"] != true ||
readiness["last_preflight_missing_dropped_count"] != 2 ||
readiness["last_preflight_recommended_action"] != "reset_consumer_and_resync" ||
readiness["last_preflight_action_reason"] != "consumer_cursor_before_first_retained_sequence" ||
readiness["last_preflight_operator_summary"] != "stale cursor gap: reset consumer and resync before resume" ||
readiness["last_preflight_operator_status"] != "resync_required" ||
readiness["last_preflight_operator_severity"] != "warn" ||
mapInt64Value(readiness["mailbox_preflight_operator_status_counts"], "resync_required") != 2 ||
mapInt64Value(readiness["mailbox_preflight_operator_severity_counts"], "warn") != 2 ||
readiness["preflight_attention_status"] != "repeated_resync_required" ||
readiness["preflight_attention_reason"] != "resync_required_preflight_repeated" {
t.Fatalf("stale preflight readiness = %+v", readiness)
}
lastPreflight, ok := readiness["last_preflight"].(map[string]any)
if !ok {
t.Fatalf("stale last preflight rollup missing from readiness = %+v", readiness)
}
if lastPreflight["diagnostic_state"] != "stale_cursor_gap" ||
lastPreflight["diagnostics_schema_version"] != "rap.remote_workspace_adapter_mailbox_preflight_diagnostics.v1" ||
!stringAnySliceContains(lastPreflight["diagnostics_contract"], "retained_window") ||
!stringAnySliceContains(lastPreflight["diagnostics_contract"], "remediation_checklist") ||
!stringAnySliceContains(lastPreflight["diagnostics_contract"], "attention") ||
!stringAnySliceContains(lastPreflight["diagnostics_contract"], "operator_counts") ||
!boolMapValue(lastPreflight["diagnostics_features"], "retained_window") ||
!boolMapValue(lastPreflight["diagnostics_features"], "remediation_checklist") ||
!boolMapValue(lastPreflight["diagnostics_features"], "attention") ||
!boolMapValue(lastPreflight["diagnostics_features"], "operator_counts") ||
lastPreflight["operator_status"] != "resync_required" ||
lastPreflight["operator_severity"] != "warn" ||
lastPreflight["recommended_action"] != "reset_consumer_and_resync" ||
lastPreflight["action_reason"] != "consumer_cursor_before_first_retained_sequence" ||
!preflightChecklistContains(lastPreflight["remediation_checklist"], "reset_consumer_cursor", true, false) ||
!preflightChecklistContains(lastPreflight["remediation_checklist"], "request_full_adapter_resync", true, false) ||
!preflightChecklistContains(lastPreflight["remediation_checklist"], "resume_from_checkpoint_after_resync", true, false) ||
lastPreflight["remediation_checklist_status"] != "action_required" ||
anyInt64(preflightChecklistCountsValue(lastPreflight["remediation_checklist_counts"], "required_count")) != 3 ||
anyInt64(preflightChecklistCountsValue(lastPreflight["remediation_checklist_counts"], "satisfied_count")) != 0 ||
anyInt64(preflightChecklistCountsValue(lastPreflight["remediation_checklist_counts"], "pending_count")) != 3 ||
mapInt64Value(lastPreflight["operator_status_counts"], "resync_required") != 2 ||
mapInt64Value(lastPreflight["operator_severity_counts"], "warn") != 2 ||
lastPreflight["preflight_attention_status"] != "repeated_resync_required" ||
lastPreflight["preflight_attention_reason"] != "resync_required_preflight_repeated" ||
anyInt64(lastPreflight["missing_dropped_count"]) != 2 ||
anyInt64(lastPreflight["first_retained_sequence"]) != 4 ||
anyInt64(lastPreflight["last_retained_sequence"]) != 19 ||
anyInt64(lastPreflight["mailbox_dropped_total"]) != 3 ||
anyInt64(lastPreflight["resume_sequence"]) != 1 {
t.Fatalf("invalid stale last preflight rollup = %+v", lastPreflight)
}
}
func preflightChecklistCountsValue(value any, key string) any {
switch counts := value.(type) {
case map[string]any:
return counts[key]
default:
return nil
}
}
func mapInt64Value(value any, key string) int64 {
switch items := value.(type) {
case map[string]int64:
return items[key]
case map[string]any:
return anyInt64(items[key])
default:
return 0
}
}
func boolMapValue(value any, key string) bool {
switch items := value.(type) {
case map[string]bool:
return items[key]
case map[string]any:
item, _ := items[key].(bool)
return item
default:
return false
}
}
func preflightDiagnosticsContractCompatible(rollup map[string]any) bool {
for _, feature := range []string{"retained_window", "remediation_checklist", "attention", "operator_counts"} {
if !stringAnySliceContains(rollup["diagnostics_contract"], feature) || !boolMapValue(rollup["diagnostics_features"], feature) {
return false
}
}
return true
}
func terminalSessionSummaryContractCompatible(summary map[string]any) bool {
for _, feature := range []string{"adapter_session_id", "session_state", "reason", "controlled_at"} {
if !stringAnySliceContains(summary["summary_contract"], feature) || !boolMapValue(summary["summary_features"], feature) {
return false
}
}
return true
}
func noSessionSummaryContractCompatible(summary map[string]any) bool {
for _, feature := range []string{"status", "diagnostic_state", "active_session_count", "terminal_session_count"} {
if !stringAnySliceContains(summary["summary_contract"], feature) || !boolMapValue(summary["summary_features"], feature) {
return false
}
}
return true
}
func stringAnySliceContains(value any, want string) bool {
switch items := value.(type) {
case []string:
for _, item := range items {
if item == want {
return true
}
}
case []any:
for _, item := range items {
if item == want {
return true
}
}
}
return false
}
func preflightChecklistContains(value any, step string, required bool, satisfied bool) bool {
switch items := value.(type) {
case []map[string]any:
for _, item := range items {
if item["step"] == step && item["required"] == required && item["satisfied"] == satisfied && item["source_hint"] == true {
return true
}
}
case []any:
for _, raw := range items {
item, ok := raw.(map[string]any)
if !ok {
continue
}
if item["step"] == step && item["required"] == required && item["satisfied"] == satisfied && item["source_hint"] == true {
return true
}
}
}
return false
}
func stringSliceContains(items []string, want string) bool {
for _, item := range items {
if item == want {
return true
}
}
return false
}
func anyInt64(value any) int64 {
switch v := value.(type) {
case int:
return int64(v)
case int64:
return v
case float64:
return int64(v)
default:
return 0
}
}
func TestRemoteWorkspacePreflightDiagnosticsContractCompatibility(t *testing.T) {
compatible := map[string]any{
"diagnostics_contract": []string{"retained_window", "remediation_checklist", "attention", "operator_counts"},
"diagnostics_features": map[string]bool{"retained_window": true, "remediation_checklist": true, "attention": true, "operator_counts": true},
}
if !preflightDiagnosticsContractCompatible(compatible) {
t.Fatalf("expected contract/features to be compatible")
}
tests := []struct {
name string
rollup map[string]any
}{
{
name: "missing contract item",
rollup: map[string]any{
"diagnostics_contract": []string{"retained_window", "remediation_checklist", "attention"},
"diagnostics_features": map[string]bool{"retained_window": true, "remediation_checklist": true, "attention": true, "operator_counts": true},
},
},
{
name: "missing feature flag",
rollup: map[string]any{
"diagnostics_contract": []string{"retained_window", "remediation_checklist", "attention", "operator_counts"},
"diagnostics_features": map[string]bool{"retained_window": true, "remediation_checklist": true, "attention": true},
},
},
{
name: "false feature flag",
rollup: map[string]any{
"diagnostics_contract": []string{"retained_window", "remediation_checklist", "attention", "operator_counts"},
"diagnostics_features": map[string]bool{"retained_window": true, "remediation_checklist": true, "attention": true, "operator_counts": false},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if preflightDiagnosticsContractCompatible(tt.rollup) {
t.Fatalf("expected incompatible contract/features for %+v", tt.rollup)
}
})
}
}
func TestRemoteWorkspaceTerminalSessionSummaryContractCompatibility(t *testing.T) {
compatible := map[string]any{
"summary_contract": []string{"adapter_session_id", "session_state", "reason", "controlled_at"},
"summary_features": map[string]bool{"adapter_session_id": true, "session_state": true, "reason": true, "controlled_at": true},
}
if !terminalSessionSummaryContractCompatible(compatible) {
t.Fatalf("expected summary contract/features to be compatible")
}
tests := []struct {
name string
summary map[string]any
}{
{
name: "missing contract item",
summary: map[string]any{
"summary_contract": []string{"adapter_session_id", "session_state", "reason"},
"summary_features": map[string]bool{"adapter_session_id": true, "session_state": true, "reason": true, "controlled_at": true},
},
},
{
name: "missing feature flag",
summary: map[string]any{
"summary_contract": []string{"adapter_session_id", "session_state", "reason", "controlled_at"},
"summary_features": map[string]bool{"adapter_session_id": true, "session_state": true, "reason": true},
},
},
{
name: "false feature flag",
summary: map[string]any{
"summary_contract": []string{"adapter_session_id", "session_state", "reason", "controlled_at"},
"summary_features": map[string]bool{"adapter_session_id": true, "session_state": true, "reason": true, "controlled_at": false},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if terminalSessionSummaryContractCompatible(tt.summary) {
t.Fatalf("expected incompatible summary contract/features for %+v", tt.summary)
}
})
}
}
func TestRemoteWorkspaceNoSessionSummaryContractCompatibility(t *testing.T) {
compatible := map[string]any{
"summary_contract": []string{"status", "diagnostic_state", "active_session_count", "terminal_session_count"},
"summary_features": map[string]bool{"status": true, "diagnostic_state": true, "active_session_count": true, "terminal_session_count": true},
}
if !noSessionSummaryContractCompatible(compatible) {
t.Fatalf("expected no-session summary contract/features to be compatible")
}
tests := []struct {
name string
summary map[string]any
}{
{
name: "missing contract item",
summary: map[string]any{
"summary_contract": []string{"status", "diagnostic_state", "active_session_count"},
"summary_features": map[string]bool{"status": true, "diagnostic_state": true, "active_session_count": true, "terminal_session_count": true},
},
},
{
name: "missing feature flag",
summary: map[string]any{
"summary_contract": []string{"status", "diagnostic_state", "active_session_count", "terminal_session_count"},
"summary_features": map[string]bool{"status": true, "diagnostic_state": true, "active_session_count": true},
},
},
{
name: "false feature flag",
summary: map[string]any{
"summary_contract": []string{"status", "diagnostic_state", "active_session_count", "terminal_session_count"},
"summary_features": map[string]bool{"status": true, "diagnostic_state": true, "active_session_count": true, "terminal_session_count": false},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if noSessionSummaryContractCompatible(tt.summary) {
t.Fatalf("expected incompatible no-session summary contract/features for %+v", tt.summary)
}
})
}
}
func TestRemoteWorkspaceAdapterSessionMailboxPreflightRejectsInvalidRequests(t *testing.T) {
@@ -3145,6 +3986,57 @@ func TestRemoteWorkspaceAdapterSessionMailboxPreflightRejectsInvalidRequests(t *
}
}
func TestRemoteWorkspacePreflightAttentionReasonSummaries(t *testing.T) {
tests := []struct {
name string
statusCounts map[string]int64
severityCounts map[string]int64
wantStatus string
wantReason string
}{
{
name: "clean ready",
statusCounts: map[string]int64{"ready_to_resume": 1},
severityCounts: map[string]int64{"ok": 1},
wantStatus: "clean",
wantReason: "no_resync_required_preflight_observed",
},
{
name: "single resync",
statusCounts: map[string]int64{"resync_required": 1},
severityCounts: map[string]int64{"warn": 1},
wantStatus: "needs_attention",
wantReason: "resync_required_preflight_observed",
},
{
name: "repeated resync",
statusCounts: map[string]int64{"resync_required": 2},
severityCounts: map[string]int64{"warn": 2},
wantStatus: "repeated_resync_required",
wantReason: "resync_required_preflight_repeated",
},
{
name: "none observed",
statusCounts: map[string]int64{},
severityCounts: map[string]int64{},
wantStatus: "unknown",
wantReason: "no_preflight_observed",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
status := remoteWorkspacePreflightAttentionStatus(tt.statusCounts, tt.severityCounts)
if status != tt.wantStatus {
t.Fatalf("status=%q want %q", status, tt.wantStatus)
}
reason := remoteWorkspacePreflightAttentionReason(status, tt.statusCounts, tt.severityCounts)
if reason != tt.wantReason {
t.Fatalf("reason=%q want %q", reason, tt.wantReason)
}
})
}
}
func TestFabricServiceChannelVPNPacketIngressHonorsDisabledBackendRelayPolicy(t *testing.T) {
publicKey, privateKey, err := ed25519.GenerateKey(nil)
if err != nil {
@@ -14,6 +14,14 @@ type Supervisor interface {
type StubSupervisor struct {
Version string
RemoteWorkspaceRealAdapter RemoteWorkspaceRealAdapterConfig
}
type RemoteWorkspaceRealAdapterConfig struct {
EnabledRequested bool
Command string
ArgsJSON string
WorkDir string
}
func (s StubSupervisor) Apply(_ context.Context, desired []client.DesiredWorkload) ([]client.WorkloadStatusRequest, error) {
@@ -85,6 +93,7 @@ func (s StubSupervisor) applyOne(workload client.DesiredWorkload) client.Workloa
payload["backend_relay_steady_state"] = false
payload["channels"] = remoteWorkspaceAdapterChannels()
payload["frame_batch_contract"] = remoteWorkspaceFrameBatchContract()
payload["real_adapter_supervision"] = remoteWorkspaceRealAdapterSupervisionContract(s.RemoteWorkspaceRealAdapter)
payload["traffic"] = "none"
return client.WorkloadStatusRequest{
ReportedState: "running",
@@ -93,6 +102,20 @@ func (s StubSupervisor) applyOne(workload client.DesiredWorkload) client.Workloa
StatusPayload: payload,
}
}
if serviceType == "rdp-worker" && runtimeMode == "native" && boolConfig(workload.Config, "real_adapter_supervision") {
payload["reason"] = "remote_workspace_real_adapter_supervision_disabled"
payload["execution_mode"] = "real_adapter_supervision_disabled"
payload["service_class"] = "remote_workspace"
payload["traffic"] = "blocked"
payload["payload_traffic"] = "none"
payload["real_adapter_supervision"] = remoteWorkspaceRealAdapterSupervisionContract(s.RemoteWorkspaceRealAdapter)
return client.WorkloadStatusRequest{
ReportedState: "degraded",
RuntimeMode: runtimeMode,
Version: version,
StatusPayload: payload,
}
}
payload["reason"] = "service_runtime_not_implemented"
payload["traffic"] = "blocked"
return client.WorkloadStatusRequest{
@@ -152,6 +175,166 @@ func remoteWorkspaceFrameBatchContract() map[string]any {
}
}
func remoteWorkspaceRealAdapterSupervisionContract(configs ...RemoteWorkspaceRealAdapterConfig) map[string]any {
var config RemoteWorkspaceRealAdapterConfig
if len(configs) > 0 {
config = configs[0]
}
return map[string]any{
"schema_version": "rap.remote_workspace_real_adapter_supervision.v1",
"enabled": false,
"activation_state": "disabled_until_real_runtime_stage",
"execution_mode": "real_adapter_supervision_disabled",
"payload_traffic": "none",
"process_model": "external_rdp_worker_process",
"config_projection": remoteWorkspaceRealAdapterConfigProjection(config),
"activation_decision": remoteWorkspaceRealAdapterActivationDecision(config),
"process_supervisor_preconditions": remoteWorkspaceRealAdapterProcessSupervisorPreconditions(config),
"process_health_probe": remoteWorkspaceRealAdapterProcessHealthProbe(),
"features": map[string]any{
"config_projection": true,
"activation_decision": true,
"missing_gates": true,
"process_health_probe": true,
"process_health_probe_disabled": true,
"process_supervisor_preconditions": true,
"process_supervisor_start_disabled": true,
"raw_values_redacted": true,
},
"config_env": []string{
"RAP_REMOTE_WORKSPACE_REAL_ADAPTER_ENABLED",
"RAP_REMOTE_WORKSPACE_REAL_ADAPTER_COMMAND",
"RAP_REMOTE_WORKSPACE_REAL_ADAPTER_ARGS_JSON",
"RAP_REMOTE_WORKSPACE_REAL_ADAPTER_WORKDIR",
},
"status_contract": []string{
"schema_version",
"enabled",
"activation_state",
"execution_mode",
"payload_traffic",
"process_model",
"config_projection",
"activation_decision",
"process_supervisor_preconditions",
"process_health_probe",
"features",
"config_env",
"status_contract",
},
"guardrails": []string{
"contract_probe_remains_default",
"no_payload_forwarding_until_real_runtime_stage",
"backend_relay_not_steady_state",
"fabric_service_channel_required",
},
}
}
func remoteWorkspaceRealAdapterProcessHealthProbe() map[string]any {
return map[string]any{
"schema_version": "rap.remote_workspace_real_adapter_process_health_probe.v1",
"health_probe_enabled": false,
"reason": "disabled_until_real_runtime_stage",
"payload_traffic": "none",
"probe_model": "external_process_health",
"required_signals": []string{
"process_started",
"process_exit_status",
"adapter_control_channel_ready",
"fabric_service_channel_bound",
"payload_forwarding_contract_ready",
},
"missing_signals": []string{
"process_started",
"process_exit_status",
"adapter_control_channel_ready",
"fabric_service_channel_bound",
"payload_forwarding_contract_ready",
},
}
}
func remoteWorkspaceRealAdapterProcessSupervisorPreconditions(config RemoteWorkspaceRealAdapterConfig) map[string]any {
return map[string]any{
"schema_version": "rap.remote_workspace_real_adapter_process_supervisor_preconditions.v1",
"process_start_allowed": false,
"reason": "disabled_until_real_runtime_stage",
"command_config_present": strings.TrimSpace(config.Command) != "",
"workdir_config_present": strings.TrimSpace(config.WorkDir) != "",
"args_config_present": strings.TrimSpace(config.ArgsJSON) != "",
"required_checks": []string{
"real_runtime_stage_enabled",
"command_config_validated",
"workdir_config_validated",
"process_identity_policy_bound",
"fabric_service_channel_runtime_ready",
"payload_forwarding_contract_enabled",
"health_probe_contract_enabled",
},
"missing_checks": []string{
"real_runtime_stage_enabled",
"command_config_validated",
"workdir_config_validated",
"process_identity_policy_bound",
"fabric_service_channel_runtime_ready",
"payload_forwarding_contract_enabled",
"health_probe_contract_enabled",
},
}
}
func remoteWorkspaceRealAdapterActivationDecision(config RemoteWorkspaceRealAdapterConfig) map[string]any {
return map[string]any{
"schema_version": "rap.remote_workspace_real_adapter_activation_decision.v1",
"decision": "blocked",
"reason": "real_runtime_stage_not_enabled",
"enabled_requested": config.EnabledRequested,
"activation_allowed": false,
"payload_traffic": "none",
"required_gates": []string{
"real_runtime_stage_enabled",
"fabric_service_channel_runtime_ready",
"adapter_process_supervisor_enabled",
"payload_forwarding_contract_enabled",
},
"missing_gates": []string{
"real_runtime_stage_enabled",
"fabric_service_channel_runtime_ready",
"adapter_process_supervisor_enabled",
"payload_forwarding_contract_enabled",
},
}
}
func remoteWorkspaceRealAdapterConfigProjection(config RemoteWorkspaceRealAdapterConfig) map[string]any {
return map[string]any{
"schema_version": "rap.remote_workspace_real_adapter_config_projection.v1",
"enabled_requested": config.EnabledRequested,
"activation_allowed": false,
"command_present": strings.TrimSpace(config.Command) != "",
"args_json_present": strings.TrimSpace(config.ArgsJSON) != "",
"args_json_shape": remoteWorkspaceArgsJSONShape(config.ArgsJSON),
"workdir_present": strings.TrimSpace(config.WorkDir) != "",
"raw_values_redacted": true,
}
}
func remoteWorkspaceArgsJSONShape(value string) string {
trimmed := strings.TrimSpace(value)
if trimmed == "" {
return "absent"
}
switch {
case strings.HasPrefix(trimmed, "["):
return "json_array"
case strings.HasPrefix(trimmed, "{"):
return "json_object"
default:
return "opaque"
}
}
func serviceTrafficMode(serviceType string) string {
switch serviceType {
case "core-mesh":
@@ -130,4 +130,469 @@ func TestStubSupervisorRunsRDPWorkerAdapterContractProbeOnly(t *testing.T) {
frameBatch["service_class"] != "remote_workspace" {
t.Fatalf("unexpected frame batch contract: %#v", frameBatch)
}
realAdapter, ok := statuses[0].StatusPayload["real_adapter_supervision"].(map[string]any)
if !ok {
t.Fatalf("real_adapter_supervision = %#v", statuses[0].StatusPayload["real_adapter_supervision"])
}
if realAdapter["schema_version"] != "rap.remote_workspace_real_adapter_supervision.v1" ||
realAdapter["enabled"] != false ||
realAdapter["activation_state"] != "disabled_until_real_runtime_stage" ||
realAdapter["payload_traffic"] != "none" {
t.Fatalf("unexpected real adapter supervision contract: %#v", realAdapter)
}
if !realAdapterSupervisionContractCompatible(realAdapter) {
t.Fatalf("real adapter supervision contract is not compatible: %#v", realAdapter)
}
}
func TestStubSupervisorKeepsContractProbePrecedenceWhenRealAdapterAlsoRequested(t *testing.T) {
statuses, err := (StubSupervisor{
Version: "test",
RemoteWorkspaceRealAdapter: RemoteWorkspaceRealAdapterConfig{
EnabledRequested: true,
Command: "/opt/rap/bin/rdp-worker",
ArgsJSON: `["--future-probe"]`,
WorkDir: "/var/lib/rap-node-agent/rdp-worker",
},
}).Apply(context.Background(), []client.DesiredWorkload{
{
ServiceType: "rdp-worker",
DesiredState: "enabled",
RuntimeMode: "native",
Config: map[string]any{
"adapter_contract_probe": true,
"real_adapter_supervision": true,
},
},
})
if err != nil {
t.Fatalf("apply desired workload: %v", err)
}
if statuses[0].ReportedState != "running" {
t.Fatalf("ReportedState = %q", statuses[0].ReportedState)
}
payload := statuses[0].StatusPayload
if payload["execution_mode"] != "contract_probe" ||
payload["reason"] != "remote_workspace_adapter_contract_probe_ready" ||
payload["traffic"] != "none" {
t.Fatalf("contract probe did not retain precedence: %#v", payload)
}
realAdapter, ok := payload["real_adapter_supervision"].(map[string]any)
if !ok || !realAdapterSupervisionContractCompatible(realAdapter) {
t.Fatalf("real_adapter_supervision = %#v", payload["real_adapter_supervision"])
}
decision := realAdapter["activation_decision"].(map[string]any)
if realAdapter["enabled"] != false ||
decision["decision"] != "blocked" ||
decision["reason"] != "real_runtime_stage_not_enabled" ||
decision["payload_traffic"] != "none" {
t.Fatalf("unexpected activation decision under contract-probe precedence: %#v", realAdapter)
}
}
func TestStubSupervisorKeepsRealAdapterSupervisionDisabled(t *testing.T) {
statuses, err := (StubSupervisor{
Version: "test",
RemoteWorkspaceRealAdapter: RemoteWorkspaceRealAdapterConfig{
EnabledRequested: true,
Command: "/opt/rap/bin/rdp-worker",
ArgsJSON: `["--future-probe"]`,
WorkDir: "/var/lib/rap-node-agent/rdp-worker",
},
}).Apply(context.Background(), []client.DesiredWorkload{
{
ServiceType: "rdp-worker",
DesiredState: "enabled",
RuntimeMode: "native",
Config: map[string]any{
"real_adapter_supervision": true,
},
},
})
if err != nil {
t.Fatalf("apply desired workload: %v", err)
}
if statuses[0].ReportedState != "degraded" {
t.Fatalf("ReportedState = %q", statuses[0].ReportedState)
}
if statuses[0].StatusPayload["reason"] != "remote_workspace_real_adapter_supervision_disabled" ||
statuses[0].StatusPayload["execution_mode"] != "real_adapter_supervision_disabled" ||
statuses[0].StatusPayload["traffic"] != "blocked" ||
statuses[0].StatusPayload["payload_traffic"] != "none" {
t.Fatalf("unexpected real adapter disabled payload: %#v", statuses[0].StatusPayload)
}
realAdapter, ok := statuses[0].StatusPayload["real_adapter_supervision"].(map[string]any)
if !ok || !realAdapterSupervisionContractCompatible(realAdapter) {
t.Fatalf("real adapter supervision contract = %#v", statuses[0].StatusPayload["real_adapter_supervision"])
}
projection, ok := realAdapter["config_projection"].(map[string]any)
if !ok {
t.Fatalf("config_projection = %#v", realAdapter["config_projection"])
}
if realAdapter["enabled"] != false ||
projection["enabled_requested"] != true ||
projection["activation_allowed"] != false ||
projection["command_present"] != true ||
projection["args_json_present"] != true ||
projection["args_json_shape"] != "json_array" ||
projection["workdir_present"] != true ||
projection["raw_values_redacted"] != true {
t.Fatalf("unexpected config projection: %#v", projection)
}
decision, ok := realAdapter["activation_decision"].(map[string]any)
if !ok {
t.Fatalf("activation_decision = %#v", realAdapter["activation_decision"])
}
if decision["decision"] != "blocked" ||
decision["reason"] != "real_runtime_stage_not_enabled" ||
decision["enabled_requested"] != true ||
decision["activation_allowed"] != false ||
decision["payload_traffic"] != "none" {
t.Fatalf("unexpected activation decision: %#v", decision)
}
features, ok := realAdapter["features"].(map[string]any)
if !ok ||
features["config_projection"] != true ||
features["activation_decision"] != true ||
features["process_supervisor_preconditions"] != true ||
features["process_supervisor_start_disabled"] != true ||
features["missing_gates"] != true ||
features["raw_values_redacted"] != true {
t.Fatalf("unexpected real adapter features: %#v", realAdapter["features"])
}
preconditions, ok := realAdapter["process_supervisor_preconditions"].(map[string]any)
if !ok ||
preconditions["schema_version"] != "rap.remote_workspace_real_adapter_process_supervisor_preconditions.v1" ||
preconditions["process_start_allowed"] != false ||
preconditions["command_config_present"] != true ||
preconditions["args_config_present"] != true ||
preconditions["workdir_config_present"] != true {
t.Fatalf("unexpected process supervisor preconditions: %#v", realAdapter["process_supervisor_preconditions"])
}
healthProbe, ok := realAdapter["process_health_probe"].(map[string]any)
if !ok ||
healthProbe["schema_version"] != "rap.remote_workspace_real_adapter_process_health_probe.v1" ||
healthProbe["health_probe_enabled"] != false ||
healthProbe["payload_traffic"] != "none" {
t.Fatalf("unexpected process health probe: %#v", realAdapter["process_health_probe"])
}
}
func TestRealAdapterSupervisionContractCompatibility(t *testing.T) {
compatible := remoteWorkspaceRealAdapterSupervisionContract()
if !realAdapterSupervisionContractCompatible(compatible) {
t.Fatalf("expected real adapter supervision contract to be compatible")
}
tests := []struct {
name string
contract map[string]any
}{
{
name: "enabled",
contract: map[string]any{
"schema_version": "rap.remote_workspace_real_adapter_supervision.v1",
"enabled": true,
"activation_state": "disabled_until_real_runtime_stage",
"payload_traffic": "none",
"config_projection": map[string]any{"schema_version": "rap.remote_workspace_real_adapter_config_projection.v1", "activation_allowed": false, "raw_values_redacted": true},
"activation_decision": map[string]any{"schema_version": "rap.remote_workspace_real_adapter_activation_decision.v1", "decision": "blocked", "reason": "real_runtime_stage_not_enabled", "activation_allowed": false, "payload_traffic": "none", "required_gates": []string{"real_runtime_stage_enabled", "fabric_service_channel_runtime_ready", "adapter_process_supervisor_enabled", "payload_forwarding_contract_enabled"}, "missing_gates": []string{"real_runtime_stage_enabled", "fabric_service_channel_runtime_ready", "adapter_process_supervisor_enabled", "payload_forwarding_contract_enabled"}},
"process_supervisor_preconditions": map[string]any{"schema_version": "rap.remote_workspace_real_adapter_process_supervisor_preconditions.v1", "process_start_allowed": false, "reason": "disabled_until_real_runtime_stage", "required_checks": []string{"real_runtime_stage_enabled", "command_config_validated", "workdir_config_validated", "process_identity_policy_bound", "fabric_service_channel_runtime_ready", "payload_forwarding_contract_enabled", "health_probe_contract_enabled"}, "missing_checks": []string{"real_runtime_stage_enabled", "command_config_validated", "workdir_config_validated", "process_identity_policy_bound", "fabric_service_channel_runtime_ready", "payload_forwarding_contract_enabled", "health_probe_contract_enabled"}},
"process_health_probe": map[string]any{"schema_version": "rap.remote_workspace_real_adapter_process_health_probe.v1", "health_probe_enabled": false, "reason": "disabled_until_real_runtime_stage", "payload_traffic": "none", "probe_model": "external_process_health", "required_signals": []string{"process_started", "process_exit_status", "adapter_control_channel_ready", "fabric_service_channel_bound", "payload_forwarding_contract_ready"}, "missing_signals": []string{"process_started", "process_exit_status", "adapter_control_channel_ready", "fabric_service_channel_bound", "payload_forwarding_contract_ready"}},
"features": map[string]any{"config_projection": true, "activation_decision": true, "missing_gates": true, "process_health_probe": true, "process_health_probe_disabled": true, "process_supervisor_preconditions": true, "process_supervisor_start_disabled": true, "raw_values_redacted": true},
"config_env": []string{"RAP_REMOTE_WORKSPACE_REAL_ADAPTER_ENABLED", "RAP_REMOTE_WORKSPACE_REAL_ADAPTER_COMMAND", "RAP_REMOTE_WORKSPACE_REAL_ADAPTER_ARGS_JSON", "RAP_REMOTE_WORKSPACE_REAL_ADAPTER_WORKDIR"},
"status_contract": []string{"schema_version", "enabled", "activation_state", "execution_mode", "payload_traffic", "process_model", "config_projection", "activation_decision", "process_supervisor_preconditions", "process_health_probe", "features", "config_env", "status_contract"},
"guardrails": []string{"contract_probe_remains_default", "no_payload_forwarding_until_real_runtime_stage", "backend_relay_not_steady_state", "fabric_service_channel_required"},
},
},
{
name: "missing env",
contract: map[string]any{
"schema_version": "rap.remote_workspace_real_adapter_supervision.v1",
"enabled": false,
"activation_state": "disabled_until_real_runtime_stage",
"payload_traffic": "none",
"config_projection": map[string]any{"schema_version": "rap.remote_workspace_real_adapter_config_projection.v1", "activation_allowed": false, "raw_values_redacted": true},
"activation_decision": map[string]any{"schema_version": "rap.remote_workspace_real_adapter_activation_decision.v1", "decision": "blocked", "reason": "real_runtime_stage_not_enabled", "activation_allowed": false, "payload_traffic": "none", "required_gates": []string{"real_runtime_stage_enabled", "fabric_service_channel_runtime_ready", "adapter_process_supervisor_enabled", "payload_forwarding_contract_enabled"}, "missing_gates": []string{"real_runtime_stage_enabled", "fabric_service_channel_runtime_ready", "adapter_process_supervisor_enabled", "payload_forwarding_contract_enabled"}},
"process_supervisor_preconditions": map[string]any{"schema_version": "rap.remote_workspace_real_adapter_process_supervisor_preconditions.v1", "process_start_allowed": false, "reason": "disabled_until_real_runtime_stage", "required_checks": []string{"real_runtime_stage_enabled", "command_config_validated", "workdir_config_validated", "process_identity_policy_bound", "fabric_service_channel_runtime_ready", "payload_forwarding_contract_enabled", "health_probe_contract_enabled"}, "missing_checks": []string{"real_runtime_stage_enabled", "command_config_validated", "workdir_config_validated", "process_identity_policy_bound", "fabric_service_channel_runtime_ready", "payload_forwarding_contract_enabled", "health_probe_contract_enabled"}},
"process_health_probe": map[string]any{"schema_version": "rap.remote_workspace_real_adapter_process_health_probe.v1", "health_probe_enabled": false, "reason": "disabled_until_real_runtime_stage", "payload_traffic": "none", "probe_model": "external_process_health", "required_signals": []string{"process_started", "process_exit_status", "adapter_control_channel_ready", "fabric_service_channel_bound", "payload_forwarding_contract_ready"}, "missing_signals": []string{"process_started", "process_exit_status", "adapter_control_channel_ready", "fabric_service_channel_bound", "payload_forwarding_contract_ready"}},
"features": map[string]any{"config_projection": true, "activation_decision": true, "missing_gates": true, "process_health_probe": true, "process_health_probe_disabled": true, "process_supervisor_preconditions": true, "process_supervisor_start_disabled": true, "raw_values_redacted": true},
"config_env": []string{"RAP_REMOTE_WORKSPACE_REAL_ADAPTER_ENABLED"},
"status_contract": []string{"schema_version", "enabled", "activation_state", "execution_mode", "payload_traffic", "process_model", "config_projection", "activation_decision", "process_supervisor_preconditions", "process_health_probe", "features", "config_env", "status_contract"},
"guardrails": []string{"contract_probe_remains_default", "no_payload_forwarding_until_real_runtime_stage", "backend_relay_not_steady_state", "fabric_service_channel_required"},
},
},
{
name: "missing guardrail",
contract: map[string]any{
"schema_version": "rap.remote_workspace_real_adapter_supervision.v1",
"enabled": false,
"activation_state": "disabled_until_real_runtime_stage",
"payload_traffic": "none",
"config_projection": map[string]any{"schema_version": "rap.remote_workspace_real_adapter_config_projection.v1", "activation_allowed": false, "raw_values_redacted": true},
"activation_decision": map[string]any{"schema_version": "rap.remote_workspace_real_adapter_activation_decision.v1", "decision": "blocked", "reason": "real_runtime_stage_not_enabled", "activation_allowed": false, "payload_traffic": "none", "required_gates": []string{"real_runtime_stage_enabled", "fabric_service_channel_runtime_ready", "adapter_process_supervisor_enabled", "payload_forwarding_contract_enabled"}, "missing_gates": []string{"real_runtime_stage_enabled", "fabric_service_channel_runtime_ready", "adapter_process_supervisor_enabled", "payload_forwarding_contract_enabled"}},
"process_supervisor_preconditions": map[string]any{"schema_version": "rap.remote_workspace_real_adapter_process_supervisor_preconditions.v1", "process_start_allowed": false, "reason": "disabled_until_real_runtime_stage", "required_checks": []string{"real_runtime_stage_enabled", "command_config_validated", "workdir_config_validated", "process_identity_policy_bound", "fabric_service_channel_runtime_ready", "payload_forwarding_contract_enabled", "health_probe_contract_enabled"}, "missing_checks": []string{"real_runtime_stage_enabled", "command_config_validated", "workdir_config_validated", "process_identity_policy_bound", "fabric_service_channel_runtime_ready", "payload_forwarding_contract_enabled", "health_probe_contract_enabled"}},
"process_health_probe": map[string]any{"schema_version": "rap.remote_workspace_real_adapter_process_health_probe.v1", "health_probe_enabled": false, "reason": "disabled_until_real_runtime_stage", "payload_traffic": "none", "probe_model": "external_process_health", "required_signals": []string{"process_started", "process_exit_status", "adapter_control_channel_ready", "fabric_service_channel_bound", "payload_forwarding_contract_ready"}, "missing_signals": []string{"process_started", "process_exit_status", "adapter_control_channel_ready", "fabric_service_channel_bound", "payload_forwarding_contract_ready"}},
"features": map[string]any{"config_projection": true, "activation_decision": true, "missing_gates": true, "process_health_probe": true, "process_health_probe_disabled": true, "process_supervisor_preconditions": true, "process_supervisor_start_disabled": true, "raw_values_redacted": true},
"config_env": []string{"RAP_REMOTE_WORKSPACE_REAL_ADAPTER_ENABLED", "RAP_REMOTE_WORKSPACE_REAL_ADAPTER_COMMAND", "RAP_REMOTE_WORKSPACE_REAL_ADAPTER_ARGS_JSON", "RAP_REMOTE_WORKSPACE_REAL_ADAPTER_WORKDIR"},
"status_contract": []string{"schema_version", "enabled", "activation_state", "execution_mode", "payload_traffic", "process_model", "config_projection", "activation_decision", "process_supervisor_preconditions", "process_health_probe", "features", "config_env", "status_contract"},
"guardrails": []string{"contract_probe_remains_default"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if realAdapterSupervisionContractCompatible(tt.contract) {
t.Fatalf("expected incompatible contract for %+v", tt.contract)
}
})
}
}
func TestRealAdapterConfigProjectionCompatibility(t *testing.T) {
tests := []struct {
name string
config RemoteWorkspaceRealAdapterConfig
enabledRequested bool
commandPresent bool
argsJSONPresent bool
argsJSONShape string
workdirPresent bool
}{
{
name: "default empty",
argsJSONShape: "absent",
},
{
name: "requested array args",
config: RemoteWorkspaceRealAdapterConfig{
EnabledRequested: true,
Command: "/opt/rap/bin/rdp-worker",
ArgsJSON: `["--future-probe"]`,
WorkDir: "/var/lib/rap-node-agent/rdp-worker",
},
enabledRequested: true,
commandPresent: true,
argsJSONPresent: true,
argsJSONShape: "json_array",
workdirPresent: true,
},
{
name: "object args shape",
config: RemoteWorkspaceRealAdapterConfig{
ArgsJSON: `{"arg":"value"}`,
},
argsJSONPresent: true,
argsJSONShape: "json_object",
},
{
name: "opaque args shape",
config: RemoteWorkspaceRealAdapterConfig{
ArgsJSON: "--future-probe",
},
argsJSONPresent: true,
argsJSONShape: "opaque",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
contract := remoteWorkspaceRealAdapterSupervisionContract(tt.config)
if !realAdapterSupervisionContractCompatible(contract) {
t.Fatalf("contract is not compatible: %#v", contract)
}
projection := contract["config_projection"].(map[string]any)
if projection["enabled_requested"] != tt.enabledRequested ||
projection["activation_allowed"] != false ||
projection["command_present"] != tt.commandPresent ||
projection["args_json_present"] != tt.argsJSONPresent ||
projection["args_json_shape"] != tt.argsJSONShape ||
projection["workdir_present"] != tt.workdirPresent ||
projection["raw_values_redacted"] != true {
t.Fatalf("unexpected config projection: %#v", projection)
}
})
}
}
func TestRealAdapterProjectionAndActivationDecisionStayAligned(t *testing.T) {
tests := []struct {
name string
config RemoteWorkspaceRealAdapterConfig
enabledRequested bool
}{
{name: "default"},
{
name: "requested",
config: RemoteWorkspaceRealAdapterConfig{
EnabledRequested: true,
Command: "/opt/rap/bin/rdp-worker",
ArgsJSON: `["--future-probe"]`,
WorkDir: "/var/lib/rap-node-agent/rdp-worker",
},
enabledRequested: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
contract := remoteWorkspaceRealAdapterSupervisionContract(tt.config)
projection := contract["config_projection"].(map[string]any)
decision := contract["activation_decision"].(map[string]any)
if projection["enabled_requested"] != decision["enabled_requested"] ||
projection["enabled_requested"] != tt.enabledRequested ||
projection["activation_allowed"] != false ||
decision["activation_allowed"] != false ||
contract["enabled"] != false ||
contract["payload_traffic"] != decision["payload_traffic"] {
t.Fatalf("projection and activation decision are not aligned: contract=%#v", contract)
}
})
}
}
func realAdapterSupervisionContractCompatible(contract map[string]any) bool {
if contract["schema_version"] != "rap.remote_workspace_real_adapter_supervision.v1" ||
contract["enabled"] != false ||
contract["activation_state"] != "disabled_until_real_runtime_stage" ||
contract["payload_traffic"] != "none" {
return false
}
projection, ok := contract["config_projection"].(map[string]any)
if !ok ||
projection["schema_version"] != "rap.remote_workspace_real_adapter_config_projection.v1" ||
projection["activation_allowed"] != false ||
projection["raw_values_redacted"] != true {
return false
}
decision, ok := contract["activation_decision"].(map[string]any)
if !ok ||
decision["schema_version"] != "rap.remote_workspace_real_adapter_activation_decision.v1" ||
decision["decision"] != "blocked" ||
decision["reason"] != "real_runtime_stage_not_enabled" ||
decision["activation_allowed"] != false ||
decision["payload_traffic"] != "none" {
return false
}
for _, item := range []string{
"real_runtime_stage_enabled",
"fabric_service_channel_runtime_ready",
"adapter_process_supervisor_enabled",
"payload_forwarding_contract_enabled",
} {
if !anyStringSliceContains(decision["required_gates"], item) || !anyStringSliceContains(decision["missing_gates"], item) {
return false
}
}
preconditions, ok := contract["process_supervisor_preconditions"].(map[string]any)
if !ok ||
preconditions["schema_version"] != "rap.remote_workspace_real_adapter_process_supervisor_preconditions.v1" ||
preconditions["process_start_allowed"] != false ||
preconditions["reason"] != "disabled_until_real_runtime_stage" {
return false
}
for _, item := range []string{
"real_runtime_stage_enabled",
"command_config_validated",
"workdir_config_validated",
"process_identity_policy_bound",
"fabric_service_channel_runtime_ready",
"payload_forwarding_contract_enabled",
"health_probe_contract_enabled",
} {
if !anyStringSliceContains(preconditions["required_checks"], item) || !anyStringSliceContains(preconditions["missing_checks"], item) {
return false
}
}
healthProbe, ok := contract["process_health_probe"].(map[string]any)
if !ok ||
healthProbe["schema_version"] != "rap.remote_workspace_real_adapter_process_health_probe.v1" ||
healthProbe["health_probe_enabled"] != false ||
healthProbe["reason"] != "disabled_until_real_runtime_stage" ||
healthProbe["payload_traffic"] != "none" ||
healthProbe["probe_model"] != "external_process_health" {
return false
}
for _, item := range []string{
"process_started",
"process_exit_status",
"adapter_control_channel_ready",
"fabric_service_channel_bound",
"payload_forwarding_contract_ready",
} {
if !anyStringSliceContains(healthProbe["required_signals"], item) || !anyStringSliceContains(healthProbe["missing_signals"], item) {
return false
}
}
features, ok := contract["features"].(map[string]any)
if !ok ||
features["config_projection"] != true ||
features["activation_decision"] != true ||
features["missing_gates"] != true ||
features["process_health_probe"] != true ||
features["process_health_probe_disabled"] != true ||
features["process_supervisor_preconditions"] != true ||
features["process_supervisor_start_disabled"] != true ||
features["raw_values_redacted"] != true {
return false
}
for _, item := range []string{
"RAP_REMOTE_WORKSPACE_REAL_ADAPTER_ENABLED",
"RAP_REMOTE_WORKSPACE_REAL_ADAPTER_COMMAND",
"RAP_REMOTE_WORKSPACE_REAL_ADAPTER_ARGS_JSON",
"RAP_REMOTE_WORKSPACE_REAL_ADAPTER_WORKDIR",
} {
if !anyStringSliceContains(contract["config_env"], item) {
return false
}
}
for _, item := range []string{
"schema_version",
"enabled",
"activation_state",
"execution_mode",
"payload_traffic",
"process_model",
"config_projection",
"activation_decision",
"process_supervisor_preconditions",
"process_health_probe",
"features",
"config_env",
"status_contract",
} {
if !anyStringSliceContains(contract["status_contract"], item) {
return false
}
}
for _, item := range []string{
"contract_probe_remains_default",
"no_payload_forwarding_until_real_runtime_stage",
"backend_relay_not_steady_state",
"fabric_service_channel_required",
} {
if !anyStringSliceContains(contract["guardrails"], item) {
return false
}
}
return true
}
func anyStringSliceContains(value any, want string) bool {
switch items := value.(type) {
case []string:
for _, item := range items {
if item == want {
return true
}
}
case []any:
for _, item := range items {
if item == want {
return true
}
}
}
return false
}
@@ -184,6 +184,9 @@ func (g *Gateway) Snapshot() map[string]any {
if !lastRuntimeActivityAt.IsZero() {
out["last_runtime_activity_at"] = lastRuntimeActivityAt.UTC().Format(time.RFC3339Nano)
}
if platform := gatewayPlatformSnapshot(g.InterfaceName, g.RouteCIDR); len(platform) > 0 {
out["platform"] = platform
}
return out
}
@@ -19,6 +19,8 @@ const (
iffNoPI = 0x1000
tunSetIFF = 0x400454ca
ifNameSize = 16
gatewayTunMTU = "1000"
gatewayTCPMSS = "900"
)
type tunDevice struct {
@@ -86,6 +88,9 @@ func configureGatewayInterface(name, addressCIDR, routeCIDR string) error {
if err := runCommand("ip", "addr", "replace", addressCIDR, "dev", name); err != nil {
return err
}
if err := runCommand("ip", "link", "set", "dev", name, "mtu", gatewayTunMTU); err != nil {
return err
}
if err := runCommand("ip", "link", "set", name, "up"); err != nil {
return err
}
@@ -118,11 +123,10 @@ func ensureMasqueradeRules(routeCIDR string) error {
}
func ensureMSSClampRule(interfaceName string) error {
err := ensureIPTablesRule("mangle", "FORWARD", "-i", interfaceName, "-p", "tcp", "--tcp-flags", "SYN,RST", "SYN", "-j", "TCPMSS", "--clamp-mss-to-pmtu")
if err == nil {
return nil
if err := ensureIPTablesRule("mangle", "FORWARD", "-i", interfaceName, "-p", "tcp", "--tcp-flags", "SYN,RST", "SYN", "-j", "TCPMSS", "--set-mss", gatewayTCPMSS); err != nil {
return err
}
return nil
return ensureIPTablesRule("mangle", "FORWARD", "-o", interfaceName, "-p", "tcp", "--tcp-flags", "SYN,RST", "SYN", "-j", "TCPMSS", "--set-mss", gatewayTCPMSS)
}
func defaultIPv4Interface() (string, error) {
@@ -204,3 +208,47 @@ func runCommand(name string, args ...string) error {
}
return nil
}
func gatewayPlatformSnapshot(interfaceName, routeCIDR string) map[string]any {
out := map[string]any{
"os": "linux",
"interface": interfaceName,
"route_cidr": routeCIDR,
}
if value, err := readTrimmedFile("/proc/sys/net/ipv4/ip_forward"); err == nil {
out["ipv4_forward"] = value
}
for _, key := range []string{"all", "default", interfaceName} {
if strings.TrimSpace(key) == "" {
continue
}
if value, err := readTrimmedFile(fmt.Sprintf("/proc/sys/net/ipv4/conf/%s/rp_filter", key)); err == nil {
out["rp_filter_"+key] = value
}
}
if interfaceName != "" {
out["forward_in_rule"] = iptablesRulePresent("filter", "FORWARD", "-i", interfaceName, "-j", "ACCEPT")
out["forward_out_established_rule"] = iptablesRulePresent("filter", "FORWARD", "-o", interfaceName, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT")
}
if routeCIDR != "" {
out["masquerade_rule"] = iptablesRulePresent("nat", "POSTROUTING", "-s", routeCIDR, "-j", "MASQUERADE")
if egress, err := defaultIPv4Interface(); err == nil && egress != "" {
out["default_egress"] = egress
out["egress_masquerade_rule"] = iptablesRulePresent("nat", "POSTROUTING", "-s", routeCIDR, "-o", egress, "-j", "MASQUERADE")
}
}
return out
}
func readTrimmedFile(path string) (string, error) {
payload, err := os.ReadFile(path)
if err != nil {
return "", err
}
return strings.TrimSpace(string(payload)), nil
}
func iptablesRulePresent(table, chain string, rule ...string) bool {
checkArgs := append([]string{"-t", table, "-C", chain}, rule...)
return exec.Command("iptables", checkArgs...).Run() == nil
}
@@ -21,3 +21,11 @@ func (d *tunDevice) Write(packet []byte) (int, error) {
func (d *tunDevice) Close() error {
return nil
}
func gatewayPlatformSnapshot(interfaceName, routeCIDR string) map[string]any {
return map[string]any{
"os": "unsupported",
"interface": interfaceName,
"route_cidr": routeCIDR,
}
}
@@ -2160,6 +2160,7 @@ type IssueFabricServiceChannelLeaseInput struct {
Failover json.RawMessage
Metadata json.RawMessage
TTL time.Duration
BackendFallbackAllowed *bool
}
type UpdateFabricServiceChannelPoolPolicyInput struct {
@@ -2531,6 +2532,14 @@ type RenewNodeVPNAssignmentLeaseInput struct {
TTL time.Duration
}
type AcquireNodeVPNAssignmentLeaseInput struct {
ClusterID string
VPNConnectionID string
OwnerNodeID string
TTL time.Duration
Metadata json.RawMessage
}
type ReleaseVPNConnectionLeaseInput struct {
ActorUserID string
ClusterID string
@@ -147,6 +147,7 @@ func (m *Module) RegisterRoutes(router chi.Router) {
r.Post("/{clusterID}/vpn-connections/{vpnConnectionID}/leases/{leaseID}/release", m.releaseVPNConnectionLease)
r.Post("/{clusterID}/vpn-connections/{vpnConnectionID}/leases/{leaseID}/fence", m.fenceVPNConnectionLease)
r.Get("/{clusterID}/nodes/{nodeID}/vpn/assignments", m.listNodeVPNAssignments)
r.Post("/{clusterID}/nodes/{nodeID}/vpn/assignments/{vpnConnectionID}/lease/acquire", m.acquireNodeVPNAssignmentLease)
r.Post("/{clusterID}/nodes/{nodeID}/vpn/assignments/{vpnConnectionID}/lease/{leaseID}/renew", m.renewNodeVPNAssignmentLease)
r.Post("/{clusterID}/nodes/{nodeID}/vpn/assignments/{vpnConnectionID}/status", m.reportNodeVPNAssignmentStatus)
r.Get("/{clusterID}/vpn-connections/{vpnConnectionID}/tunnel/stats", m.getVPNPacketStats)
@@ -2072,6 +2073,35 @@ func (m *Module) listNodeVPNAssignments(w http.ResponseWriter, r *http.Request)
httpx.WriteJSON(w, http.StatusOK, map[string]any{"vpn_assignments": items})
}
func (m *Module) acquireNodeVPNAssignmentLease(w http.ResponseWriter, r *http.Request) {
var payload struct {
TTLSeconds int `json:"ttl_seconds"`
Metadata json.RawMessage `json:"metadata"`
}
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
httpx.WriteError(w, http.StatusBadRequest, "invalid vpn node lease acquire payload")
return
}
item, err := m.service.AcquireNodeVPNAssignmentLease(r.Context(), AcquireNodeVPNAssignmentLeaseInput{
ClusterID: chi.URLParam(r, "clusterID"),
VPNConnectionID: chi.URLParam(r, "vpnConnectionID"),
OwnerNodeID: chi.URLParam(r, "nodeID"),
TTL: time.Duration(payload.TTLSeconds) * time.Second,
Metadata: payload.Metadata,
})
if writeServiceError(w, err) {
return
}
httpx.WriteJSON(w, http.StatusCreated, map[string]any{"lease": NodeVPNAssignmentLease{
LeaseID: item.ID,
OwnerNodeID: item.OwnerNodeID,
LeaseGeneration: item.LeaseGeneration,
Status: item.Status,
RenewedAt: item.RenewedAt,
ExpiresAt: item.ExpiresAt,
}})
}
func (m *Module) renewNodeVPNAssignmentLease(w http.ResponseWriter, r *http.Request) {
var payload struct {
TTLSeconds int `json:"ttl_seconds"`
@@ -4758,7 +4758,6 @@ func (s *PostgresStore) vpnEntryEndpointCandidates(ctx context.Context, clusterI
}
func vpnEntryEndpointCandidatesFromHeartbeat(nodeID string, capabilities json.RawMessage, metadata json.RawMessage) []map[string]any {
localGatewayShortcut := heartbeatCapabilityEnabled(capabilities, "vpn_local_gateway_shortcut")
var payload struct {
MeshEndpointReport struct {
PeerEndpoint string `json:"peer_endpoint"`
@@ -4823,9 +4822,6 @@ func vpnEntryEndpointCandidatesFromHeartbeat(nodeID string, capabilities json.Ra
if apiBaseURL := vpnEntryAPIBaseURL(address); apiBaseURL != "" {
item["api_base_url"] = apiBaseURL
}
if localGatewayShortcut {
item["local_gateway_shortcut"] = true
}
out = append(out, item)
}
if len(out) == 0 {
@@ -4847,9 +4843,6 @@ func vpnEntryEndpointCandidatesFromHeartbeat(nodeID string, capabilities json.Ra
if apiBaseURL := vpnEntryAPIBaseURL(address); apiBaseURL != "" {
item["api_base_url"] = apiBaseURL
}
if localGatewayShortcut {
item["local_gateway_shortcut"] = true
}
out = append(out, item)
}
}
@@ -5129,10 +5122,15 @@ func enrichVPNClientFabricRoute(item VPNClientConnection, preferredEntryNodeID,
cfg["vpn_fabric_route"] = map[string]any{
"schema_version": "rap.vpn_fabric_route.v1",
"status": status,
"preferred_data_plane": "fabric_mesh",
"fallback_data_plane": "backend_relay",
"backend_relay_fallback": true,
"selection_mode": "entry_to_fastest_exit",
"preferred_data_plane": "fabric_service_channel",
"fallback_data_plane": "none",
"backend_relay_fallback": false,
"selection_mode": "farm_authoritative_entry_to_exit",
"route_authority": "fabric_farm",
"vpn_builds_routes": false,
"vpn_builds_tunnels": false,
"farm_builds_routes": true,
"farm_builds_tunnels": true,
"entry_pool_node_ids": entryPool,
"exit_pool_node_ids": exitPool,
"selected_entry_node_id": selectedEntry,
@@ -5147,20 +5145,28 @@ func enrichVPNClientFabricRoute(item VPNClientConnection, preferredEntryNodeID,
"tunnel_type": "universal_ip_packet",
"application_protocol_agnostic": true,
"packet_forwarding_channel": "vpn_packet",
"control_plane_packet_relay_mode": "lab_fallback_only",
"control_plane_packet_relay_mode": "fabric_service_channel_only",
"route_authority": "fabric_farm",
"backend_relay_allowed": false,
"requires_fabric_service_channel": true,
"vpn_builds_routes": false,
"vpn_builds_tunnels": false,
"farm_builds_routes": true,
"farm_builds_tunnels": true,
"traffic_contract": map[string]any{
"all_ip_traffic": true,
"protocol_specific_routing": false,
"diagnostics_only_protocol_summaries": true,
},
"route_selection": map[string]any{
"mode": "lowest_latency_healthy_route",
"mode": "farm_authoritative_lowest_latency_healthy_route",
"selected_entry_node_id": selectedEntry,
"selected_exit_node_id": selectedExit,
"route_candidates": routeCandidates,
},
"failover": map[string]any{
"enabled": true,
"owner": "fabric_farm",
"client_topology_hidden": true,
"preserve_vpn_connection_id": true,
"alternate_route_count": alternateVPNRouteCount(routeCandidates, selectedEntry, selectedExit),
@@ -5178,8 +5184,8 @@ func enrichVPNClientFabricRoute(item VPNClientConnection, preferredEntryNodeID,
"drop_policy": "drop_only_when_all_routes_unavailable_or_queue_full",
"bulk_and_realtime": "same_packet_path",
"flow_isolation": "hash_by_ip_protocol_and_ports",
"target_dataplane": "entry_node_to_exit_node_fabric",
"temporary_fallback": "backend_http_packet_relay",
"target_dataplane": "fabric_farm_entry_to_exit_service_channel",
"temporary_fallback": "none",
},
}
out, err := json.Marshal(cfg)
@@ -52,7 +52,7 @@ func TestEnrichVPNClientFabricRoutePrefersPlacementEntryAndActiveExit(t *testing
if !ok {
t.Fatalf("missing vpn_fabric_route in %#v", cfg)
}
if route["preferred_data_plane"] != "fabric_mesh" || route["fallback_data_plane"] != "backend_relay" {
if route["preferred_data_plane"] != "fabric_service_channel" || route["fallback_data_plane"] != "none" || route["backend_relay_fallback"] != false {
t.Fatalf("unexpected data-plane route contract: %#v", route)
}
if route["selected_entry_node_id"] != "entry-2" || route["selected_exit_node_id"] != "exit-active" {
@@ -158,8 +158,8 @@ func TestEnrichVPNClientEntryEndpointCandidatesAddsReportedEntryAPI(t *testing.T
if candidate["node_id"] != "entry-1" || candidate["api_base_url"] != "http://entry.example.test:19131/api/v1" {
t.Fatalf("unexpected endpoint candidate: %#v", candidate)
}
if candidate["local_gateway_shortcut"] != true {
t.Fatalf("local gateway shortcut missing: %#v", candidate)
if _, ok := candidate["local_gateway_shortcut"]; ok {
t.Fatalf("local gateway shortcut must not be advertised in farm-owned VPN mode: %#v", candidate)
}
if candidate["selected_entry"] != true || candidate["source"] != "node_latest_heartbeat.mesh_endpoint_report.endpoint_candidates" {
t.Fatalf("unexpected endpoint metadata: %#v", candidate)
+124 -31
View File
@@ -4015,8 +4015,8 @@ func (s *Service) IssueFabricServiceChannelLease(ctx context.Context, input Issu
if ttl <= 0 {
ttl = time.Minute
}
if ttl > 5*time.Minute {
ttl = 5 * time.Minute
if ttl > 6*time.Hour {
ttl = 6 * time.Hour
}
now := s.now().UTC()
expiresAt := now.Add(ttl)
@@ -4031,6 +4031,9 @@ func (s *Service) IssueFabricServiceChannelLease(ctx context.Context, input Issu
return FabricServiceChannelLease{}, err
}
poolPolicy := fabricServiceChannelPoolPolicyFromCluster(cluster)
if input.BackendFallbackAllowed != nil {
poolPolicy.BackendFallbackAllowed = *input.BackendFallbackAllowed
}
entryNodeIDs := fabricServiceChannelEffectivePool(input.EntryNodeIDs, poolPolicy.EntryPoolNodeIDs)
exitNodeIDs := fabricServiceChannelEffectivePool(input.ExitNodeIDs, poolPolicy.ExitPoolNodeIDs)
if len(entryNodeIDs) == 0 || len(exitNodeIDs) == 0 {
@@ -7303,8 +7306,10 @@ func (s *Service) GetNodeSyntheticMeshConfig(ctx context.Context, input GetNodeS
if feedback, ok := serviceChannelFeedback[route.RouteID]; ok && feedback.Fenced {
replacementDecision := s.serviceChannelRouteReplacementDecision(input, route, intents, serviceChannelFeedback, cfg.ConfigVersion)
routePathDecisions = append(routePathDecisions, replacementDecision)
if replacementDecision.DecisionSource != "service_channel_feedback_no_alternate_keep_primary" {
continue
}
}
reportedPeers, reportedCandidates, err := s.reportedEndpointConfig(ctx, input.ClusterID, input.NodeID, route.Hops, localPerspective)
if err != nil {
return NodeSyntheticMeshConfig{}, err
@@ -8700,6 +8705,98 @@ func (s *Service) RenewNodeVPNAssignmentLease(ctx context.Context, input RenewNo
return item, nil
}
func (s *Service) AcquireNodeVPNAssignmentLease(ctx context.Context, input AcquireNodeVPNAssignmentLeaseInput) (VPNConnectionLease, error) {
input.ClusterID = strings.TrimSpace(input.ClusterID)
input.VPNConnectionID = strings.TrimSpace(input.VPNConnectionID)
input.OwnerNodeID = strings.TrimSpace(input.OwnerNodeID)
if input.ClusterID == "" || input.VPNConnectionID == "" || input.OwnerNodeID == "" {
return VPNConnectionLease{}, ErrInvalidPayload
}
conn, err := s.store.GetVPNConnection(ctx, input.ClusterID, input.VPNConnectionID)
if errors.Is(err, pgx.ErrNoRows) {
return VPNConnectionLease{}, ErrInvalidVPNConnection
}
if err != nil {
return VPNConnectionLease{}, err
}
if conn.Mode != VPNConnectionModeSingleActive || conn.DesiredState != VPNConnectionDesiredEnabled {
return VPNConnectionLease{}, errors.New("vpn connection must be enabled single_active before lease acquisition")
}
if err := s.ensureVPNLeaseOwnerEligible(ctx, input.ClusterID, input.VPNConnectionID, input.OwnerNodeID); err != nil {
return VPNConnectionLease{}, err
}
assignments, err := s.store.ListNodeVPNAssignments(ctx, input.ClusterID, input.OwnerNodeID)
if err != nil {
return VPNConnectionLease{}, err
}
visibleCandidate := false
for _, assignment := range assignments {
if assignment.VPNConnectionID != input.VPNConnectionID {
continue
}
if assignment.DesiredState != "" && assignment.DesiredState != VPNConnectionDesiredEnabled {
return VPNConnectionLease{}, ErrVPNLeaseOwnerNotAllowed
}
if assignment.AssignmentReason == "active_owner" &&
assignment.ActiveLease != nil &&
assignment.ActiveLease.OwnerNodeID == input.OwnerNodeID {
return VPNConnectionLease{
ID: assignment.ActiveLease.LeaseID,
VPNConnectionID: assignment.VPNConnectionID,
ClusterID: assignment.ClusterID,
OwnerNodeID: assignment.ActiveLease.OwnerNodeID,
LeaseGeneration: assignment.ActiveLease.LeaseGeneration,
Status: assignment.ActiveLease.Status,
RenewedAt: assignment.ActiveLease.RenewedAt,
ExpiresAt: assignment.ActiveLease.ExpiresAt,
}, nil
}
if assignment.AssignmentReason == "eligible_candidate" {
visibleCandidate = true
break
}
}
if !visibleCandidate {
return VPNConnectionLease{}, ErrVPNLeaseOwnerNotAllowed
}
if input.TTL <= 0 {
input.TTL = 2 * time.Minute
}
input.Metadata = defaultJSON(input.Metadata, `{}`)
if !json.Valid(input.Metadata) {
return VPNConnectionLease{}, errors.New("lease metadata must be valid json")
}
token, err := generateFencingToken()
if err != nil {
return VPNConnectionLease{}, err
}
item, err := s.store.AcquireVPNConnectionLease(ctx, AcquireVPNConnectionLeaseInput{
ClusterID: input.ClusterID,
VPNConnectionID: input.VPNConnectionID,
OwnerNodeID: input.OwnerNodeID,
TTL: input.TTL,
Metadata: input.Metadata,
}, s.now().Add(input.TTL), token)
if errors.Is(err, pgx.ErrNoRows) {
return VPNConnectionLease{}, ErrInvalidVPNLease
}
if errors.Is(err, ErrVPNLeaseAlreadyActive) {
return VPNConnectionLease{}, ErrVPNLeaseAlreadyActive
}
if err != nil {
return VPNConnectionLease{}, err
}
_ = s.store.RecordAudit(ctx, ClusterAuditEvent{
ClusterID: &input.ClusterID,
EventType: "vpn_connection.lease_acquired_by_node",
TargetType: "vpn_connection",
TargetID: &input.VPNConnectionID,
Payload: json.RawMessage(`{"node_agent_runtime_requested":true}`),
CreatedAt: s.now(),
})
return item, nil
}
func (s *Service) ReleaseVPNConnectionLease(ctx context.Context, input ReleaseVPNConnectionLeaseInput) (VPNConnectionLease, error) {
if err := s.ensurePlatformAdmin(ctx, input.ActorUserID); err != nil {
return VPNConnectionLease{}, err
@@ -8910,6 +9007,7 @@ func (s *Service) attachVPNFabricServiceChannelLeases(ctx context.Context, profi
if len(exitPool) == 0 {
exitPool = dedupeStrings(append([]string{route.SelectedExitNodeID, connection.ExitNodeID}, connection.AllowedNodeIDs...))
}
backendFallbackAllowed := false
lease, err := s.IssueFabricServiceChannelLease(ctx, IssueFabricServiceChannelLeaseInput{
ClusterID: profile.ClusterID,
OrganizationID: profile.OrganizationID,
@@ -8921,7 +9019,8 @@ func (s *Service) attachVPNFabricServiceChannelLeases(ctx context.Context, profi
PreferredEntryNodeID: route.SelectedEntryNodeID,
PreferredExitNodeID: route.SelectedExitNodeID,
AllowedChannels: []string{"vpn_packet", "fabric_control", FabricChannelBulk, FabricChannelControl},
TTL: time.Minute,
TTL: 6 * time.Hour,
BackendFallbackAllowed: &backendFallbackAllowed,
})
if err != nil {
profile.Connections[i].ClientConfig = attachVPNFabricServiceChannelError(connection.ClientConfig, err)
@@ -8996,8 +9095,10 @@ func enrichVPNDataplaneSession(profile VPNClientProfile, connection VPNClientCon
"vpn_connection_id": connection.ID,
"entry_node_id": route.SelectedEntryNodeID,
"exit_node_id": route.SelectedExitNodeID,
"preferred_transport": "fabric_packet_quic_v1",
"fallback_transport": "backend_http_packet_relay",
"preferred_transport": "fabric_service_channel_v1",
"fallback_transport": "none",
"route_authority": "fabric_farm",
"backend_relay_allowed": false,
"packet_contract": map[string]any{
"tunnel_type": "universal_ip_packet",
"application_protocol_agnostic": true,
@@ -9089,10 +9190,12 @@ func vpnConcreteEntryCandidatesFromClientConfig(cfg map[string]any) []map[string
func vpnDataplaneTransportCandidates(route vpnClientFabricRoute, entryCandidates []map[string]any) []map[string]any {
candidates := []map[string]any{
{
"type": "fabric_packet_quic_v1",
"type": "fabric_service_channel_v1",
"status": "contract_ready_listener_pending",
"entry_node_id": route.SelectedEntryNodeID,
"exit_node_id": route.SelectedExitNodeID,
"route_authority": "fabric_farm",
"backend_relay_allowed": false,
"entry_candidates": entryCandidates,
"application_protocols": []string{"ip"},
},
@@ -9100,11 +9203,6 @@ func vpnDataplaneTransportCandidates(route vpnClientFabricRoute, entryCandidates
if direct := vpnDirectHTTPEntryTransportCandidate(route, entryCandidates); direct != nil {
candidates = append(candidates, direct)
}
candidates = append(candidates, map[string]any{
"type": "backend_http_packet_relay",
"status": "active_fallback",
"description": "current safe dataplane until entry listener is available",
})
return candidates
}
@@ -9112,7 +9210,6 @@ func vpnDirectHTTPEntryTransportCandidate(route vpnClientFabricRoute, entryCandi
var selected []map[string]any
hasPublic := false
hasHTTP := false
hasLocalGatewayShortcut := false
for _, candidate := range entryCandidates {
nodeID, _ := candidate["node_id"].(string)
if route.SelectedEntryNodeID != "" && nodeID != route.SelectedEntryNodeID {
@@ -9132,9 +9229,6 @@ func vpnDirectHTTPEntryTransportCandidate(route vpnClientFabricRoute, entryCandi
if strings.EqualFold(reachability, "public") {
hasPublic = true
}
if value, ok := candidate["local_gateway_shortcut"].(bool); ok && value {
hasLocalGatewayShortcut = true
}
selected = append(selected, candidate)
}
if len(selected) == 0 {
@@ -9148,13 +9242,8 @@ func vpnDirectHTTPEntryTransportCandidate(route vpnClientFabricRoute, entryCandi
}
safeClientSwitch := hasPublic
if route.SelectedEntryNodeID != "" && route.SelectedEntryNodeID == route.SelectedExitNodeID {
if hasPublic && hasLocalGatewayShortcut {
status = "available_local_gateway_shortcut"
safeClientSwitch = true
} else {
status = "available_local_gateway_shortcut_pending"
safeClientSwitch = false
}
status = "available_farm_local_route"
safeClientSwitch = hasPublic
}
return map[string]any{
"type": "entry_direct_http_v1",
@@ -9275,9 +9364,13 @@ func vpnFabricRouteIntentPolicy(sourceNodeID, destinationNodeID string, expiresA
"route_version": version,
"policy_version": version,
"peer_directory_version": version,
"backend_relay_fallback": true,
"data_plane_preference": "fabric_mesh",
"route_owner": "vpn_client_profile",
"backend_relay_fallback": false,
"data_plane_preference": "fabric_service_channel",
"route_owner": "fabric_farm",
"vpn_builds_routes": false,
"vpn_builds_tunnels": false,
"farm_builds_routes": true,
"farm_builds_tunnels": true,
"route_refresh_required": true,
"route_refresh_threshold": "24h",
}
@@ -11387,11 +11480,11 @@ func (s *Service) serviceChannelRouteReplacementDecision(input GetNodeSyntheticM
SourceNodeID: fencedRoute.SourceNodeID,
DestinationNodeID: fencedRoute.DestinationNodeID,
OriginalHops: append([]string{}, fencedRoute.Hops...),
EffectiveHops: []string{},
DecisionSource: "service_channel_feedback_no_alternate",
EffectiveHops: append([]string{}, fencedRoute.Hops...),
DecisionSource: "service_channel_feedback_no_alternate_keep_primary",
Generation: generation,
PathScore: 0,
ScoreReasons: []string{"service_channel_fenced_route", "no_unfenced_alternate_route"},
PathScore: serviceChannelReplacementRouteScore(fencedRoute),
ScoreReasons: []string{"service_channel_fenced_route", "no_unfenced_alternate_route", "primary_route_retained_until_rebuild"},
ControlPlaneOnly: true,
ProductionForwarding: false,
ExpiresAt: fencedRoute.ExpiresAt.UTC(),
@@ -11399,10 +11492,10 @@ func (s *Service) serviceChannelRouteReplacementDecision(input GetNodeSyntheticM
applyServiceChannelFeedbackCorrelationToDecision(&decision, routeFeedback)
if serviceChannelFeedbackRequestsRebuild(routeFeedback) {
decision.RebuildRequestID = serviceChannelRebuildRequestID(fencedRoute.RouteID, input.NodeID, generation)
decision.RebuildStatus = "pending_degraded_fallback"
decision.RebuildStatus = "requested"
decision.RebuildReason = "service_channel_feedback_rebuild_requested"
decision.RebuildAttempt = routeFeedback.ConsecutiveFailures
decision.ScoreReasons = append(decision.ScoreReasons, "service_channel_rebuild_requested", "backend_relay_degraded_fallback_until_rebuild")
decision.ScoreReasons = append(decision.ScoreReasons, "service_channel_rebuild_requested")
if routeFeedback.DegradedFallbackRecommended {
decision.ScoreReasons = append(decision.ScoreReasons, "service_channel_degraded_fallback_recommended")
}
@@ -732,7 +732,7 @@ func TestGetVPNClientProfileEnsuresFabricVPNPacketRouteIntents(t *testing.T) {
if !ok {
t.Fatalf("missing vpn_dataplane_session in %#v", cfg)
}
if session["preferred_transport"] != "fabric_packet_quic_v1" || session["fallback_transport"] != "backend_http_packet_relay" {
if session["preferred_transport"] != "fabric_service_channel_v1" || session["fallback_transport"] != "none" || session["backend_relay_allowed"] != false {
t.Fatalf("unexpected dataplane session transports: %#v", session)
}
if session["entry_node_id"] != "entry-1" || session["exit_node_id"] != "exit-1" {
@@ -811,7 +811,7 @@ func TestGetVPNClientProfileForwardsPreferredExit(t *testing.T) {
}
}
func TestVPNDirectHTTPEntryTransportWaitsForLocalGatewayShortcutWhenEntryIsExit(t *testing.T) {
func TestVPNDirectHTTPEntryTransportUsesFarmLocalRouteWhenEntryIsExit(t *testing.T) {
candidate := vpnDirectHTTPEntryTransportCandidate(vpnClientFabricRoute{
SelectedEntryNodeID: "node-1",
SelectedExitNodeID: "node-1",
@@ -823,12 +823,12 @@ func TestVPNDirectHTTPEntryTransportWaitsForLocalGatewayShortcutWhenEntryIsExit(
if candidate == nil {
t.Fatal("candidate is nil")
}
if candidate["safe_client_switch"] != false || candidate["status"] != "available_local_gateway_shortcut_pending" {
t.Fatalf("unexpected local shortcut guard: %#v", candidate)
if candidate["safe_client_switch"] != true || candidate["status"] != "available_farm_local_route" {
t.Fatalf("unexpected farm local route guard: %#v", candidate)
}
}
func TestVPNDirectHTTPEntryTransportAllowsLocalGatewayShortcutWhenReported(t *testing.T) {
func TestVPNDirectHTTPEntryTransportIgnoresLegacyLocalGatewayShortcut(t *testing.T) {
candidate := vpnDirectHTTPEntryTransportCandidate(vpnClientFabricRoute{
SelectedEntryNodeID: "node-1",
SelectedExitNodeID: "node-1",
@@ -841,8 +841,8 @@ func TestVPNDirectHTTPEntryTransportAllowsLocalGatewayShortcutWhenReported(t *te
if candidate == nil {
t.Fatal("candidate is nil")
}
if candidate["safe_client_switch"] != true || candidate["status"] != "available_local_gateway_shortcut" {
t.Fatalf("unexpected local shortcut candidate: %#v", candidate)
if candidate["safe_client_switch"] != true || candidate["status"] != "available_farm_local_route" {
t.Fatalf("unexpected farm route candidate: %#v", candidate)
}
}
@@ -3152,6 +3152,68 @@ func TestListNodeVPNAssignmentsDoesNotRequirePlatformAdmin(t *testing.T) {
}
}
func TestAcquireNodeVPNAssignmentLeaseAllowsEligibleCandidateWithoutPlatformAdmin(t *testing.T) {
store := &fakeRepository{
platformRole: "user",
vpnConnection: VPNConnection{
ID: "vpn-1",
ClusterID: "cluster-1",
Mode: VPNConnectionModeSingleActive,
DesiredState: VPNConnectionDesiredEnabled,
},
nodeVPNAssignments: []NodeVPNAssignment{
{
VPNConnectionID: "vpn-1",
ClusterID: "cluster-1",
OrganizationID: "org-1",
DesiredState: VPNConnectionDesiredEnabled,
AssignmentReason: "eligible_candidate",
},
},
}
service := NewService(store)
lease, err := service.AcquireNodeVPNAssignmentLease(context.Background(), AcquireNodeVPNAssignmentLeaseInput{
ClusterID: "cluster-1",
VPNConnectionID: "vpn-1",
OwnerNodeID: "node-1",
TTL: time.Minute,
Metadata: json.RawMessage(`{"reason":"test"}`),
})
if err != nil {
t.Fatalf("acquire node vpn assignment lease: %v", err)
}
if lease.OwnerNodeID != "node-1" || lease.VPNConnectionID != "vpn-1" || lease.Status != VPNLeaseStatusActive {
t.Fatalf("unexpected lease: %+v", lease)
}
}
func TestAcquireNodeVPNAssignmentLeaseRejectsInvisibleAssignment(t *testing.T) {
store := &fakeRepository{
platformRole: "user",
vpnConnection: VPNConnection{
ID: "vpn-1",
ClusterID: "cluster-1",
Mode: VPNConnectionModeSingleActive,
DesiredState: VPNConnectionDesiredEnabled,
},
nodeVPNAssignments: []NodeVPNAssignment{
{VPNConnectionID: "other-vpn", ClusterID: "cluster-1", AssignmentReason: "eligible_candidate"},
},
}
service := NewService(store)
_, err := service.AcquireNodeVPNAssignmentLease(context.Background(), AcquireNodeVPNAssignmentLeaseInput{
ClusterID: "cluster-1",
VPNConnectionID: "vpn-1",
OwnerNodeID: "node-1",
TTL: time.Minute,
})
if !errors.Is(err, ErrVPNLeaseOwnerNotAllowed) {
t.Fatalf("err = %v, want ErrVPNLeaseOwnerNotAllowed", err)
}
}
func TestRenewNodeVPNAssignmentLeaseAllowsActiveOwnerWithoutPlatformAdmin(t *testing.T) {
store := &fakeRepository{
platformRole: "user",
@@ -6051,18 +6113,24 @@ func TestGetNodeSyntheticMeshConfigReportsRebuildPendingWhenNoAlternateExists(t
if err != nil {
t.Fatalf("synthetic config: %v", err)
}
if containsRouteID(cfg.Routes, "route-bad") {
t.Fatalf("fenced route should be withheld while rebuild is pending: %+v", cfg.Routes)
if !containsRouteID(cfg.Routes, "route-bad") {
t.Fatalf("fenced route should be retained until an alternate exists: %+v", cfg.Routes)
}
if cfg.RoutePathDecisions == nil || cfg.RoutePathDecisions.RebuildRequestCount != 1 || cfg.RoutePathDecisions.DegradedDecisionCount != 1 {
if cfg.RoutePathDecisions == nil || cfg.RoutePathDecisions.RebuildRequestCount != 1 || cfg.RoutePathDecisions.DegradedDecisionCount != 0 {
t.Fatalf("expected rebuild/degraded decision counts: %+v", cfg.RoutePathDecisions)
}
decision := cfg.RoutePathDecisions.Decisions[0]
if decision.DecisionSource != "service_channel_feedback_no_alternate" ||
decision.RebuildStatus != "pending_degraded_fallback" ||
var decision RoutePathDecision
for _, item := range cfg.RoutePathDecisions.Decisions {
if item.DecisionSource == "service_channel_feedback_no_alternate_keep_primary" {
decision = item
break
}
}
if decision.DecisionSource != "service_channel_feedback_no_alternate_keep_primary" ||
decision.RebuildStatus != "requested" ||
decision.RebuildRequestID == "" ||
decision.RebuildAttempt != 3 ||
!containsString(decision.ScoreReasons, "backend_relay_degraded_fallback_until_rebuild") {
!containsString(decision.ScoreReasons, "primary_route_retained_until_rebuild") {
t.Fatalf("unexpected rebuild decision: %+v", decision)
}
}
+3 -3
View File
@@ -22,7 +22,7 @@ android {
return (value == null ? "" : value.toString()).replace("\\", "\\\\").replace("\"", "\\\"")
}
def defaultBackendUrl = project.findProperty("RAP_ANDROID_DEFAULT_BACKEND_URL") ?: "http://vpn.cin.su:19191/api/v1"
def defaultBackendUrl = project.findProperty("RAP_ANDROID_DEFAULT_BACKEND_URL") ?: "https://vpn.cin.su/api/v1"
def defaultClusterId = project.findProperty("RAP_ANDROID_DEFAULT_CLUSTER_ID") ?: "cfc0743d-d960-49fb-9de8-96e063d5e4aa"
def defaultOrganizationId = project.findProperty("RAP_ANDROID_DEFAULT_ORGANIZATION_ID") ?: "125ff8b2-5ac1-4406-9bbb-ebbe18f7c7ed"
@@ -30,8 +30,8 @@ android {
applicationId "su.cin.rapvpn"
minSdk 26
targetSdk 35
versionCode 159
versionName "0.2.159"
versionCode 182
versionName "0.2.182"
buildConfigField "String", "DEFAULT_BACKEND_URL", "\"${normalizeGradleString(defaultBackendUrl)}\""
buildConfigField "String", "DEFAULT_CLUSTER_ID", "\"${normalizeGradleString(defaultClusterId)}\""
buildConfigField "String", "DEFAULT_ORGANIZATION_ID", "\"${normalizeGradleString(defaultOrganizationId)}\""
@@ -25,6 +25,7 @@ import java.util.Locale;
public class MainActivity extends Activity {
private static final String APP_VERSION = BuildConfig.VERSION_NAME;
private static final String DEFAULT_BACKEND_URL = BuildConfig.DEFAULT_BACKEND_URL;
private static final String PUBLIC_FABRIC_BACKEND_URL = "https://vpn.cin.su/api/v1";
private static final String DEFAULT_CLUSTER_ID = BuildConfig.DEFAULT_CLUSTER_ID;
private static final String DEFAULT_ORGANIZATION_ID = BuildConfig.DEFAULT_ORGANIZATION_ID;
private static final String PREF_SELECTED_EXIT_NODE_ID = "selected_exit_node_id";
@@ -659,6 +660,16 @@ public class MainActivity extends Activity {
if (candidate.isEmpty()) {
return DEFAULT_BACKEND_URL;
}
String lower = candidate.toLowerCase(Locale.US);
if ("http://vpn.cin.su:19191/api/v1".equals(lower)
|| "http://vpn.cin.su/api/v1".equals(lower)
|| "https://vpn.cin.su:443/api/v1".equals(lower)
|| "http://94.141.118.222:19191/api/v1".equals(lower)
|| "http://195.123.240.88:19131/api/v1".equals(lower)
|| "http://192.168.200.61:18080/api/v1".equals(lower)
|| "http://docker-test.cin.su:18080/api/v1".equals(lower)) {
return PUBLIC_FABRIC_BACKEND_URL;
}
return candidate;
}
@@ -356,7 +356,7 @@ final class RapApiClient {
return new byte[0];
}
if (!response.isSuccessful()) {
throw new IllegalStateException("HTTP " + response.code());
throw new IllegalStateException(describeHttpFailure(response));
}
ResponseBody body = response.body();
return body == null ? new byte[0] : body.bytes();
@@ -377,15 +377,34 @@ final class RapApiClient {
Request request = builder.build();
try (Response response = httpClient.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IllegalStateException("HTTP " + response.code());
throw new IllegalStateException(describeHttpFailure(response));
}
}
}
private String clientPacketPath(String clusterId, String vpnConnectionId, String suffix) {
private String describeHttpFailure(Response response) {
StringBuilder message = new StringBuilder("HTTP ").append(response.code());
ResponseBody body = response.body();
if (body != null) {
try {
String text = body.string();
if (text != null && !text.trim().isEmpty()) {
text = text.replace('\n', ' ').replace('\r', ' ').trim();
if (text.length() > 240) {
text = text.substring(0, 240);
}
message.append(": ").append(text);
}
} catch (Exception ignored) {
}
}
return message.toString();
}
private String clientPacketPath(String clusterId, String vpnConnectionId, String suffix) throws IOException {
String path = fabricServiceChannel.packetPathForBase(baseUrl, clusterId, vpnConnectionId, false);
if (path.isEmpty()) {
path = "/clusters/" + clusterId + "/vpn-connections/" + vpnConnectionId + "/tunnel/client/packets";
throw new IOException("fabric service channel lease required for VPN packet dataplane");
}
return path + (suffix == null ? "" : suffix);
}
@@ -18,6 +18,7 @@ import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.provider.Settings;
import android.widget.Toast;
import org.json.JSONObject;
@@ -38,6 +39,7 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.UUID;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -51,6 +53,7 @@ public class RapDiagnosticService extends Service {
private static final String CHANNEL_ID = "rap-vpn-diagnostics";
private static final String APP_VERSION = BuildConfig.VERSION_NAME;
private static final String DEFAULT_BACKEND_URL = BuildConfig.DEFAULT_BACKEND_URL;
private static final String PUBLIC_FABRIC_BACKEND_URL = "https://vpn.cin.su/api/v1";
private static final String INTERNAL_BACKEND_URL = "http://192.168.200.61:18080/api/v1";
private static final String DEFAULT_CLUSTER_ID = BuildConfig.DEFAULT_CLUSTER_ID;
private static final String DEFAULT_ORGANIZATION_ID = BuildConfig.DEFAULT_ORGANIZATION_ID;
@@ -60,14 +63,22 @@ public class RapDiagnosticService extends Service {
private static final String PREF_REFRESH_TOKEN = "refresh_token";
private static final String PREF_USER_ID = "user_id";
private static final String PREF_DEVICE_ID = "device_id";
private static final String PREF_DIAGNOSTIC_DEVICE_ID = "diagnostic_device_id";
private static final String PREF_PROFILE_JSON = "profile_json";
private static final String PREF_VPN_CONNECTION_ID = "vpn_connection_id";
private static final long COMMAND_STALE_MS = 45000;
private static final long COMMAND_ORPHAN_MS = 60000;
private static final long POLL_FORCE_MS = 45000;
private volatile boolean running;
private Thread worker;
private Thread supervisor;
private String serviceState = "";
private String lastCommandType = "";
private String lastCommandResult = "";
private String lastCommandPollResult = "";
private String lastReceivedCommandID = "";
private String lastReceivedCommandType = "";
private long lastReceivedCommandAt = 0;
private long lastCommandAt = 0;
private long lastHeartbeatAt = 0;
private long lastCommandPollAt = 0;
@@ -129,6 +140,16 @@ public class RapDiagnosticService extends Service {
}
}
static void restart(android.content.Context context) {
Intent intent = new Intent(context, RapDiagnosticService.class);
intent.setAction(ACTION_RESTART);
if (Build.VERSION.SDK_INT >= 26) {
context.startForegroundService(intent);
} else {
context.startService(intent);
}
}
private void startWorker() {
if (worker != null && worker.isAlive()) {
long age = System.currentTimeMillis() - lastWorkerProgressAt;
@@ -211,8 +232,12 @@ public class RapDiagnosticService extends Service {
if (clusterId == null || clusterId.trim().isEmpty()) {
clusterId = DEFAULT_CLUSTER_ID;
}
String deviceId = prefs.getString(PREF_DEVICE_ID, "");
String deviceId = diagnosticDeviceId(prefs);
if (backendUrl.isEmpty() || clusterId.isEmpty() || deviceId.isEmpty()) {
serviceState = "waiting for config backend=" + !backendUrl.isEmpty()
+ " cluster=" + !clusterId.isEmpty()
+ " device=" + !deviceId.isEmpty();
writeLocalDiagnosticHeartbeat();
Thread.sleep(3000);
continue;
}
@@ -259,12 +284,33 @@ public class RapDiagnosticService extends Service {
commandPollStartedAt = 0;
serviceState = "diagnostic poll watchdog released stale poll";
}
if (commandPollInProgress.get() && commandPollStartedAt == 0 && lastCommandPollAt > 0 && now - lastCommandPollAt > 45000) {
commandPollInProgress.set(false);
serviceState = "diagnostic poll watchdog released orphan poll flag age_ms=" + (now - lastCommandPollAt);
}
if (!commandPollInProgress.get() && !commandInProgress.get() && lastCommandPollAt > 0 && now - lastCommandPollAt > POLL_FORCE_MS) {
serviceState = "diagnostic poll watchdog forcing command poll age_ms=" + (now - lastCommandPollAt);
lastWorkerProgressAt = now;
}
long commandAge = commandInProgress.get() && commandStartedAt > 0 ? now - commandStartedAt : 0;
if (commandAge > 120000) {
if (commandAge > COMMAND_STALE_MS) {
commandInProgress.set(false);
commandStartedAt = 0;
lastCommandType = lastReceivedCommandType.isEmpty() ? "command_timeout" : lastReceivedCommandType;
lastCommandResult = "command watchdog timed out age_ms=" + commandAge
+ " id=" + lastReceivedCommandID
+ " type=" + lastReceivedCommandType;
lastCommandAt = now;
serviceState = "diagnostic command watchdog released stale command age_ms=" + commandAge;
}
if (commandInProgress.get() && commandStartedAt == 0 && lastCommandAt > 0 && now - lastCommandAt > COMMAND_ORPHAN_MS) {
commandInProgress.set(false);
serviceState = "diagnostic command watchdog released orphan command flag age_ms=" + (now - lastCommandAt);
}
if (commandInProgress.get() && commandStartedAt == 0 && lastCommandPollAt > 0 && now - lastCommandPollAt > COMMAND_ORPHAN_MS) {
commandInProgress.set(false);
serviceState = "diagnostic command watchdog released poll-stalled command flag age_ms=" + (now - lastCommandPollAt);
}
}
private void startHeartbeatWorker(String backendUrl, String clusterId, String deviceId, SharedPreferences prefs) {
@@ -308,9 +354,14 @@ public class RapDiagnosticService extends Service {
JSONObject commandEnvelope = nextCommandWithFallback(backendUrl, clusterId, deviceId);
lastWorkerProgressAt = System.currentTimeMillis();
if (commandEnvelope != null) {
lastCommandPollResult = describeCommandEnvelope(commandEnvelope);
rememberReceivedCommand(commandEnvelope);
startCommandWorker(backendUrl, clusterId, deviceId, commandEnvelope);
} else {
lastCommandPollResult = "no_content";
}
} catch (Exception e) {
lastCommandPollResult = "error: " + e.getClass().getSimpleName();
serviceState = "command poll error: " + e.getMessage();
lastWorkerProgressAt = System.currentTimeMillis();
} finally {
@@ -323,11 +374,15 @@ public class RapDiagnosticService extends Service {
private void startCommandWorker(String backendUrl, String clusterId, String deviceId, JSONObject commandEnvelope) {
if (!commandInProgress.compareAndSet(false, true)) {
lastCommandPollResult = "worker_busy " + describeCommandEnvelope(commandEnvelope);
return;
}
Thread commandWorker = new Thread(() -> {
try {
commandStartedAt = System.currentTimeMillis();
lastCommandType = lastReceivedCommandType.isEmpty() ? "command_running" : lastReceivedCommandType;
lastCommandResult = "running id=" + lastReceivedCommandID + " type=" + lastReceivedCommandType;
lastCommandAt = commandStartedAt;
lastWorkerProgressAt = System.currentTimeMillis();
RapApiClient commandClient = controlClient(backendUrl);
controlNetworkMode = commandClient.networkMode();
@@ -363,6 +418,9 @@ public class RapDiagnosticService extends Service {
params = payload;
}
String result;
lastCommandType = type;
lastCommandResult = "running id=" + (command == null ? "" : command.optString("id", "")) + " type=" + type;
lastCommandAt = System.currentTimeMillis();
if ("start_vpn".equals(type)) {
result = startVPNFromSavedProfile();
} else if ("stop_vpn".equals(type)) {
@@ -411,6 +469,8 @@ public class RapDiagnosticService extends Service {
result = "remote_assist_end accepted";
} else if ("full_vpn_test".equals(type)) {
result = runFullVPNTest(client, clusterId, params);
} else if ("install_profile".equals(type) || "apply_profile".equals(type)) {
result = installProfileFromCommand(params);
} else if ("refresh_profile".equals(type)) {
result = refreshProfile();
} else {
@@ -418,9 +478,15 @@ public class RapDiagnosticService extends Service {
}
if (isRecoverableVPNProbe(type) && looksLikeVPNStall(result)) {
String firstResult = result;
Thread.sleep(1500);
String fastRetry = runVPNProbeCommand(type, params);
if (!looksLikeVPNStall(fastRetry)) {
result = firstResult + " | fast_retry=" + fastRetry;
} else {
String recovery = controlledRestartVPNRuntime(client, clusterId);
Thread.sleep(4000);
result = firstResult + " | recovery=" + recovery + " | retry=" + runVPNProbeCommand(type, params);
result = firstResult + " | fast_retry=" + fastRetry + " | recovery=" + recovery + " | retry=" + runVPNProbeCommand(type, params);
}
}
lastCommandType = type;
lastCommandResult = result;
@@ -623,7 +689,9 @@ public class RapDiagnosticService extends Service {
return "restart failed: queue reset failed: " + e.getMessage();
}
Thread.sleep(300);
return startVPNFromSavedProfile();
String refresh = refreshProfile();
String start = startVPNFromSavedProfile();
return start + " profile_refresh=" + refresh;
} catch (Exception e) {
return "restart failed: " + e.getClass().getSimpleName() + ": " + e.getMessage();
}
@@ -642,6 +710,10 @@ public class RapDiagnosticService extends Service {
}
serviceState = "upgrade restart " + lastVersion + " -> " + APP_VERSION;
try {
String refresh = refreshProfile();
if (refresh.startsWith("refresh_profile failed")) {
lastCommandResult = "vpn runtime profile refresh before upgrade restart failed: " + refresh;
}
try {
client.resetVPNPacketQueues(clusterId, connectionId);
} catch (Exception ignored) {
@@ -650,7 +722,7 @@ public class RapDiagnosticService extends Service {
startVPNFromSavedProfile();
prefs.edit().putString("vpn_runtime_app_version", APP_VERSION).apply();
lastCommandType = "auto_upgrade_restart";
lastCommandResult = "vpn runtime reinitialized after app upgrade " + lastVersion + " -> " + APP_VERSION;
lastCommandResult = "vpn runtime reinitialized after app upgrade " + lastVersion + " -> " + APP_VERSION + " profile_refresh=" + refresh;
lastCommandAt = System.currentTimeMillis();
} catch (Exception e) {
lastCommandType = "auto_upgrade_restart";
@@ -664,10 +736,23 @@ public class RapDiagnosticService extends Service {
try {
String refreshToken = new SecureTokenStore(this).get(PREF_REFRESH_TOKEN);
if (refreshToken.isEmpty()) {
return "refresh_profile skipped: refresh token missing";
String savedUserId = prefs.getString(PREF_USER_ID, "");
if (savedUserId == null || savedUserId.trim().isEmpty()) {
return "refresh_profile skipped: refresh token and saved user missing";
}
return refreshProfileForUser(prefs, savedUserId.trim(), null);
}
RapApiClient client = new RapApiClient(normalizeBackendUrl(prefs.getString("backend_url", "")), this, true);
RapApiClient.AuthContext auth = client.refresh(refreshToken);
new SecureTokenStore(this).put(PREF_REFRESH_TOKEN, auth.refreshToken);
return refreshProfileForUser(prefs, auth.userId, auth.deviceId);
} catch (Exception e) {
return "refresh_profile failed: " + e.getMessage();
}
}
private String refreshProfileForUser(SharedPreferences prefs, String userId, String trustedDeviceId) throws Exception {
String backendUrl = normalizeBackendUrl(prefs.getString("backend_url", DEFAULT_BACKEND_URL));
String organizationId = prefs.getString("organization_id", DEFAULT_ORGANIZATION_ID);
String clusterId = prefs.getString("cluster_id", DEFAULT_CLUSTER_ID);
if (clusterId == null || clusterId.trim().isEmpty()) {
@@ -677,31 +762,109 @@ public class RapDiagnosticService extends Service {
organizationId = DEFAULT_ORGANIZATION_ID;
}
String exitNodeId = prefs.getString(PREF_SELECTED_EXIT_NODE_ID, "");
String profileJson = client.vpnClientProfile(clusterId, organizationId, auth.userId, exitNodeId);
RapApiClient client = new RapApiClient(backendUrl, this, true);
String profileJson = client.vpnClientProfile(clusterId, organizationId, userId, exitNodeId);
JSONObject root = new JSONObject(profileJson);
JSONObject profile = root.getJSONObject("vpn_client_profile");
String connectionId = profile.getJSONArray("connections").getJSONObject(0).getString("id");
prefs.edit()
.putString(PREF_USER_ID, auth.userId)
.putString(PREF_DEVICE_ID, auth.deviceId)
SharedPreferences.Editor editor = prefs.edit()
.putString("backend_url", backendUrl)
.putString("cluster_id", clusterId)
.putString("organization_id", organizationId)
.putString(PREF_USER_ID, userId)
.putString(PREF_PROFILE_JSON, profileJson)
.putString(PREF_VPN_CONNECTION_ID, connectionId)
.apply();
new SecureTokenStore(this).put(PREF_REFRESH_TOKEN, auth.refreshToken);
.putString(PREF_VPN_CONNECTION_ID, connectionId);
if (trustedDeviceId != null && !trustedDeviceId.trim().isEmpty()) {
editor.putString(PREF_DEVICE_ID, trustedDeviceId.trim());
}
editor.apply();
return "refresh_profile ok " + connectionId;
}
private String installProfileFromCommand(JSONObject params) {
try {
String backendUrl = normalizeBackendUrl(params.optString("backend_url", DEFAULT_BACKEND_URL));
String clusterId = params.optString("cluster_id", DEFAULT_CLUSTER_ID).trim();
String organizationId = params.optString("organization_id", DEFAULT_ORGANIZATION_ID).trim();
String userId = params.optString("user_id", "").trim();
String trustedDeviceId = params.optString("trusted_device_id", "").trim();
String selectedExitNodeId = params.optString("selected_exit_node_id", params.optString("exit_node_id", "")).trim();
String profileJson = params.optString("profile_json", "").trim();
JSONObject root;
if (profileJson.isEmpty()) {
JSONObject profile = params.optJSONObject("vpn_client_profile");
if (profile == null) {
profile = params.optJSONObject("profile");
}
if (profile == null) {
return "install_profile skipped: profile missing";
}
root = new JSONObject();
root.put("vpn_client_profile", profile);
profileJson = root.toString();
} else {
root = new JSONObject(profileJson);
if (!root.has("vpn_client_profile")) {
JSONObject wrapped = new JSONObject();
wrapped.put("vpn_client_profile", root);
root = wrapped;
profileJson = wrapped.toString();
}
}
JSONObject profile = root.getJSONObject("vpn_client_profile");
if (clusterId.isEmpty()) {
clusterId = profile.optString("cluster_id", DEFAULT_CLUSTER_ID);
}
if (organizationId.isEmpty()) {
organizationId = profile.optString("organization_id", DEFAULT_ORGANIZATION_ID);
}
if (userId.isEmpty()) {
userId = profile.optString("user_id", "");
}
JSONObject connection = profile.getJSONArray("connections").getJSONObject(0);
String connectionId = params.optString("vpn_connection_id", connection.optString("id", "")).trim();
JSONObject config = connection.optJSONObject("client_config");
if (selectedExitNodeId.isEmpty() && config != null) {
JSONObject route = config.optJSONObject("vpn_fabric_route");
if (route != null) {
selectedExitNodeId = route.optString("selected_exit_node_id", "");
}
}
SharedPreferences.Editor editor = getSharedPreferences(PREFS, MODE_PRIVATE).edit()
.putString("backend_url", backendUrl)
.putString("cluster_id", clusterId)
.putString("organization_id", organizationId)
.putString(PREF_USER_ID, userId)
.putString(PREF_PROFILE_JSON, profileJson)
.putString(PREF_VPN_CONNECTION_ID, connectionId);
if (!trustedDeviceId.isEmpty()) {
editor.putString(PREF_DEVICE_ID, trustedDeviceId);
}
if (!selectedExitNodeId.isEmpty()) {
editor.putString(PREF_SELECTED_EXIT_NODE_ID, selectedExitNodeId);
}
editor.apply();
Intent stopIntent = new Intent(this, RapVpnService.class);
stopIntent.setAction(RapVpnService.ACTION_STOP);
startService(stopIntent);
Thread.sleep(300);
return "install_profile ok " + connectionId + " | " + startVPNFromSavedProfile();
} catch (Exception e) {
return "refresh_profile failed: " + e.getMessage();
return "install_profile failed: " + e.getClass().getSimpleName() + ": " + e.getMessage();
}
}
private JSONObject statusPayload(String event) throws Exception {
SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE);
String deviceId = diagnosticDeviceId(prefs);
JSONObject payload = new JSONObject();
payload.put("event", event);
payload.put("app_version", APP_VERSION);
payload.put("service", "diagnostic");
payload.put("user_id", prefs.getString(PREF_USER_ID, ""));
payload.put("device_id", prefs.getString(PREF_DEVICE_ID, ""));
payload.put("device_id", deviceId);
payload.put("trusted_device_id", prefs.getString(PREF_DEVICE_ID, ""));
payload.put("diagnostic_device_id", prefs.getString(PREF_DIAGNOSTIC_DEVICE_ID, ""));
payload.put("organization_id", prefs.getString("organization_id", ""));
payload.put("vpn_connection_id", prefs.getString(PREF_VPN_CONNECTION_ID, ""));
payload.put("backend_url", prefs.getString("backend_url", ""));
@@ -716,15 +879,86 @@ public class RapDiagnosticService extends Service {
payload.put("last_command_at", lastCommandAt);
payload.put("last_heartbeat_at", lastHeartbeatAt);
payload.put("last_command_poll_at", lastCommandPollAt);
payload.put("last_command_poll_result", lastCommandPollResult);
payload.put("last_received_command_id", lastReceivedCommandID);
payload.put("last_received_command_type", lastReceivedCommandType);
payload.put("last_received_command_at", lastReceivedCommandAt);
payload.put("heartbeat_in_progress", heartbeatInProgress.get());
payload.put("heartbeat_started_at", heartbeatStartedAt);
payload.put("command_poll_in_progress", commandPollInProgress.get());
payload.put("command_poll_started_at", commandPollStartedAt);
payload.put("command_in_progress", commandInProgress.get());
payload.put("command_started_at", commandStartedAt);
payload.put("browser_test", browserTestSnapshot());
return payload;
}
private String describeCommandEnvelope(JSONObject envelope) {
if (envelope == null) {
return "no_content";
}
JSONObject command = envelope.optJSONObject("vpn_client_diagnostic_command");
JSONObject payload = command == null ? envelope.optJSONObject("payload") : command.optJSONObject("payload");
String id = command == null ? "" : command.optString("id", "");
String type = payload == null ? "" : payload.optString("type", "");
String value = "received";
if (!type.isEmpty()) {
value += " " + type;
}
if (!id.isEmpty()) {
value += " " + id;
}
return value;
}
private void rememberReceivedCommand(JSONObject envelope) {
JSONObject command = envelope == null ? null : envelope.optJSONObject("vpn_client_diagnostic_command");
JSONObject payload = command == null ? (envelope == null ? null : envelope.optJSONObject("payload")) : command.optJSONObject("payload");
lastReceivedCommandID = command == null ? "" : command.optString("id", "");
lastReceivedCommandType = payload == null ? "" : payload.optString("type", "");
lastReceivedCommandAt = System.currentTimeMillis();
}
private String diagnosticDeviceId(SharedPreferences prefs) {
String trusted = prefs.getString(PREF_DEVICE_ID, "");
if (trusted != null && !trusted.trim().isEmpty()) {
return trusted.trim();
}
String cached = prefs.getString(PREF_DIAGNOSTIC_DEVICE_ID, "");
if (cached != null && !cached.trim().isEmpty()) {
return cached.trim();
}
String androidId = "";
try {
androidId = Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);
} catch (Exception ignored) {
}
String seed = androidId == null || androidId.trim().isEmpty()
? UUID.randomUUID().toString()
: androidId.trim();
String generated = "diag-" + seed.replaceAll("[^A-Za-z0-9_-]", "").toLowerCase();
if (generated.length() > 80) {
generated = generated.substring(0, 80);
}
prefs.edit().putString(PREF_DIAGNOSTIC_DEVICE_ID, generated).apply();
return generated;
}
private String normalizeBackendUrl(String value) {
String candidate = value == null ? "" : value.trim().replaceAll("/+$", "");
if (candidate.isEmpty()) {
return DEFAULT_BACKEND_URL;
}
String lower = candidate.toLowerCase();
if ("http://vpn.cin.su:19191/api/v1".equals(lower)
|| "http://vpn.cin.su/api/v1".equals(lower)
|| "https://vpn.cin.su:443/api/v1".equals(lower)
|| "http://94.141.118.222:19191/api/v1".equals(lower)
|| "http://195.123.240.88:19131/api/v1".equals(lower)
|| "http://192.168.200.61:18080/api/v1".equals(lower)
|| "http://docker-test.cin.su:18080/api/v1".equals(lower)) {
return PUBLIC_FABRIC_BACKEND_URL;
}
return candidate;
}
@@ -791,7 +1025,7 @@ public class RapDiagnosticService extends Service {
if (!connectionId.isEmpty()) {
report.put("packet_stats", client.vpnPacketStats(clusterId, connectionId));
}
client.reportVPNDiagnosticStatus(clusterId, getSharedPreferences(PREFS, MODE_PRIVATE).getString(PREF_DEVICE_ID, ""), report);
client.reportVPNDiagnosticStatus(clusterId, diagnosticDeviceId(getSharedPreferences(PREFS, MODE_PRIVATE)), report);
}
if (!connectionId.isEmpty()) {
result.append(" | stats=").append(compact(client.vpnPacketStats(clusterId, connectionId).toString(), 900));
@@ -818,7 +1052,7 @@ public class RapDiagnosticService extends Service {
private String runVPNDownloadTest(String target) {
try {
Network vpn = vpnNetwork();
Network vpn = waitForVPNNetwork(5000);
if (vpn == null) {
return "vpn_download_test " + target + " -> vpn network not found";
}
@@ -1009,6 +1243,13 @@ public class RapDiagnosticService extends Service {
payload.put("diagnostic_local_heartbeat_at", runtime.getLong("diagnostic_local_heartbeat_at", 0));
payload.put("diagnostic_local_state", runtime.getString("diagnostic_local_state", ""));
payload.put("diagnostic_local_app_version", runtime.getString("diagnostic_local_app_version", ""));
payload.put("diagnostic_watchdog_started_at", runtime.getLong("diagnostic_watchdog_started_at", 0));
payload.put("diagnostic_watchdog_last_ensure_at", runtime.getLong("diagnostic_watchdog_last_ensure_at", 0));
payload.put("diagnostic_watchdog_last_heartbeat_age_ms", runtime.getLong("diagnostic_watchdog_last_heartbeat_age_ms", 0));
payload.put("diagnostic_watchdog_last_action", runtime.getString("diagnostic_watchdog_last_action", ""));
payload.put("diagnostic_watchdog_last_error", runtime.getString("diagnostic_watchdog_last_error", ""));
payload.put("diagnostic_watchdog_ensure_requests", runtime.getLong("diagnostic_watchdog_ensure_requests", 0));
payload.put("diagnostic_watchdog_restart_requests", runtime.getLong("diagnostic_watchdog_restart_requests", 0));
payload.put("uplink_read", runtime.getLong("uplink_read", 0));
payload.put("uplink_sent", runtime.getLong("uplink_sent", 0));
payload.put("downlink_received", runtime.getLong("downlink_received", 0));
@@ -1044,8 +1285,10 @@ public class RapDiagnosticService extends Service {
payload.put("errors", runtime.getLong("errors", 0));
payload.put("uplink", runtimePrefix(runtime, "uplink"));
payload.put("uplink_sender", runtimePrefix(runtime, "uplink_sender"));
payload.put("uplink_tcp", runtimePrefix(runtime, "uplink_tcp"));
payload.put("downlink", runtimePrefix(runtime, "downlink"));
payload.put("downlink_writer", runtimePrefix(runtime, "downlink_writer"));
payload.put("downlink_tcp", runtimePrefix(runtime, "downlink_tcp"));
payload.put("relay", runtimePrefix(runtime, "relay"));
payload.put("uplink_worker_count", runtime.getInt("uplink_worker_count", 0));
payload.put("uplink_queue_depth_total", runtime.getInt("uplink_queue_depth_total", 0));
@@ -1266,7 +1509,7 @@ public class RapDiagnosticService extends Service {
}
long started = System.currentTimeMillis();
try {
Network vpn = vpnNetwork();
Network vpn = waitForVPNNetwork(5000);
if (vpn == null) {
return "vpn_tcp_connect " + host + ":" + port + " -> vpn network not found";
}
@@ -1283,6 +1526,24 @@ public class RapDiagnosticService extends Service {
}
}
private Network waitForVPNNetwork(int timeoutMs) {
long deadline = System.currentTimeMillis() + Math.max(0, timeoutMs);
Network vpn;
do {
vpn = vpnNetwork();
if (vpn != null) {
return vpn;
}
try {
Thread.sleep(150);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return null;
}
} while (System.currentTimeMillis() < deadline);
return null;
}
private FetchResult fetchVPNURL(Network vpn, URL url, int connectTimeoutMs, int readTimeoutMs, int maxBytes) throws Exception {
long started = System.currentTimeMillis();
HttpURLConnection connection = (HttpURLConnection) vpn.openConnection(url);
@@ -26,7 +26,9 @@ import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.Socket;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
@@ -48,10 +50,12 @@ public class RapVpnService extends VpnService {
private static final String CHANNEL_ID = "rap-vpn";
private static final String TAG = "RapVpnService";
private static final String PREFS = "rap-vpn-runtime";
private static final int DEFAULT_VPN_MTU = 1420;
private static final int DEFAULT_VPN_MTU = 1000;
private static final int VPN_TCP_MSS_CLAMP = 900;
private static final boolean PACKET_WEBSOCKET_DATAPLANE_ENABLED = false;
private static final int VPN_BATCH_MAX_PACKETS = 512;
private static final int VPN_BATCH_MAX_BYTES = 1024 * 1024;
private static final int UPLINK_WORKER_MAX_COUNT = 4;
private static final int UPLINK_WORKER_MAX_COUNT = 1;
private static final int UPLINK_QUEUE_CAPACITY = 32768;
private static final int PRIORITY_QUEUE_CAPACITY = 4096;
private static final int UPLINK_SEND_RETRY_COUNT = 2;
@@ -72,7 +76,12 @@ public class RapVpnService extends VpnService {
private static final int RUNTIME_WATCHDOG_STALE_SYNACK_MS = 15000;
private static final int RUNTIME_WATCHDOG_RECOVERY_COOLDOWN_MS = 60000;
private static final int RUNTIME_WATCHDOG_HARD_RESTART_COOLDOWN_MS = 180000;
private static final int DIAGNOSTIC_WATCHDOG_INTERVAL_MS = 5000;
private static final int DIAGNOSTIC_STALE_RESTART_MS = 30000;
private static final int DIAGNOSTIC_RESTART_COOLDOWN_MS = 15000;
private static final int VPN_START_WARMUP_TIMEOUT_MS = 6000;
private static final int VPN_TCP_WARMUP_CONNECT_TIMEOUT_MS = 2500;
private static final int VPN_TCP_WARMUP_PASSES = 3;
private static final String[] DEFAULT_DNS_PROBE_DOMAINS = new String[]{
"speedtest.rt.ru",
"2ip.ru",
@@ -97,6 +106,11 @@ public class RapVpnService extends VpnService {
"rline-host.qms.ru",
"timernet-host.qms.ru"
};
private static final String[] DEFAULT_TCP_WARMUP_TARGETS = new String[]{
"192.168.200.61:18080",
"192.168.200.95:3389",
"188.40.167.82:80"
};
private static final String PREF_NAME = "rap-vpn";
private static final String PREF_PROFILE_JSON = "profile_json";
private static final String PREF_BACKEND_URL = "backend_url";
@@ -112,6 +126,7 @@ public class RapVpnService extends VpnService {
private Thread downlinkThread;
private Thread downlinkWriterThread;
private Thread runtimeWatchdogThread;
private Thread diagnosticWatchdogThread;
private BlockingQueue<byte[]>[] uplinkQueues;
private BlockingQueue<byte[]>[] downlinkQueues;
private BlockingQueue<byte[]> uplinkPriorityQueue;
@@ -171,6 +186,8 @@ public class RapVpnService extends VpnService {
private final AtomicLong runtimeWatchdogRecoveries = new AtomicLong();
private final AtomicLong tcpHandshakeStalls = new AtomicLong();
private final AtomicLong runtimeWatchdogHardRestarts = new AtomicLong();
private final AtomicLong diagnosticEnsureRequests = new AtomicLong();
private final AtomicLong diagnosticRestartRequests = new AtomicLong();
private final AtomicBoolean hardRuntimeRestartInProgress = new AtomicBoolean();
private volatile boolean relaxedUplinkSourceValidation;
private volatile boolean relaxedDownlinkDestinationValidation;
@@ -180,6 +197,7 @@ public class RapVpnService extends VpnService {
private volatile int activePacketRelayIndex;
private volatile VpnPacketWebSocketRelay packetWebSocketRelay;
private volatile FabricServiceChannel activeFabricServiceChannel = new FabricServiceChannel();
private volatile String lastUplinkSendErrorMessage = "";
private final Object packetRelaySwitchLock = new Object();
private final Map<String, byte[]> clientSourceNat = new LinkedHashMap<String, byte[]>(4096, 0.75f, true) {
@Override
@@ -198,7 +216,7 @@ public class RapVpnService extends VpnService {
private volatile long lastRuntimeWatchdogHardRestartAt;
private volatile long lastDiagnosticEnsureAt;
private volatile long lastDiagnosticStatusEnsureAt;
private volatile boolean nextDiagnosticEnsureMayRestart;
private volatile long lastDiagnosticRestartAt;
private static final int ADDRESS_MISMATCH_TOLERANCE_PACKETS = 16;
@Override
@@ -257,8 +275,17 @@ public class RapVpnService extends VpnService {
List<String> packetRelayUrls = activePacketRelayUrlsByProfile == null || activePacketRelayUrlsByProfile.isEmpty()
? singletonUrl(activePacketRelayUrlByProfile)
: new ArrayList<>(activePacketRelayUrlsByProfile);
if (!activeFabricServiceChannel.enabled) {
shutdownReason = "fabric service channel lease required";
writeRuntimeStatus("error", "vpn not started: fabric service channel lease required", 0, 0, 0, 0);
stopSelf();
return START_NOT_STICKY;
}
if (packetRelayUrls.isEmpty()) {
packetRelayUrls.add(backendUrl);
shutdownReason = "missing farm entry endpoint";
writeRuntimeStatus("error", "vpn not started: missing farm entry endpoint", 0, 0, 0, 0);
stopSelf();
return START_NOT_STICKY;
}
startPacketRelay(backendUrl, packetRelayUrls, clusterId, vpnConnectionId);
if (!running) {
@@ -284,6 +311,7 @@ public class RapVpnService extends VpnService {
private void ensureDiagnosticServiceRunning() {
try {
RapDiagnosticService.start(this);
diagnosticEnsureRequests.incrementAndGet();
writeRuntimeDetail("diagnostic_start", "diagnostic service start requested by vpn runtime", "control", 0, 0, "", -1);
} catch (Exception e) {
Log.w(TAG, "diagnostic service start failed", e);
@@ -295,8 +323,15 @@ public class RapVpnService extends VpnService {
try {
SharedPreferences runtime = getSharedPreferences(PREFS, MODE_PRIVATE);
long lastLocalHeartbeat = runtime.getLong("diagnostic_local_heartbeat_at", 0);
long age = lastLocalHeartbeat <= 0 ? Long.MAX_VALUE : System.currentTimeMillis() - lastLocalHeartbeat;
boolean restart = nextDiagnosticEnsureMayRestart && age > 45000;
long now = System.currentTimeMillis();
long age = lastLocalHeartbeat <= 0 ? Long.MAX_VALUE : now - lastLocalHeartbeat;
boolean restart = age > DIAGNOSTIC_STALE_RESTART_MS
&& now - lastDiagnosticRestartAt >= DIAGNOSTIC_RESTART_COOLDOWN_MS;
diagnosticEnsureRequests.incrementAndGet();
if (restart) {
diagnosticRestartRequests.incrementAndGet();
lastDiagnosticRestartAt = now;
}
Intent intent = new Intent(this, RapDiagnosticService.class);
intent.setAction(restart ? RapDiagnosticService.ACTION_RESTART : RapDiagnosticService.ACTION_START);
if (Build.VERSION.SDK_INT >= 26) {
@@ -304,7 +339,13 @@ public class RapVpnService extends VpnService {
} else {
startService(intent);
}
nextDiagnosticEnsureMayRestart = true;
runtime.edit()
.putLong("diagnostic_watchdog_last_ensure_at", now)
.putLong("diagnostic_watchdog_last_heartbeat_age_ms", age)
.putString("diagnostic_watchdog_last_action", restart ? "restart" : "start")
.putLong("diagnostic_watchdog_ensure_requests", diagnosticEnsureRequests.get())
.putLong("diagnostic_watchdog_restart_requests", diagnosticRestartRequests.get())
.apply();
writeRuntimeDetail(
restart ? "diagnostic_restart" : "diagnostic_start",
(restart ? "diagnostic service restart requested age_ms=" : "diagnostic service start requested age_ms=") + age,
@@ -315,6 +356,11 @@ public class RapVpnService extends VpnService {
-1);
} catch (Exception e) {
Log.w(TAG, "diagnostic service health ensure failed", e);
getSharedPreferences(PREFS, MODE_PRIVATE).edit()
.putLong("diagnostic_watchdog_last_ensure_at", System.currentTimeMillis())
.putString("diagnostic_watchdog_last_action", "failed")
.putString("diagnostic_watchdog_last_error", e.getClass().getSimpleName() + ": " + e.getMessage())
.apply();
writeRuntimeDetail("diagnostic_start_failed", e.getMessage(), "control", 0, 1, e.getClass().getSimpleName(), -1);
}
}
@@ -775,6 +821,19 @@ public class RapVpnService extends VpnService {
}
}
private boolean isIPv6URLHost(String value) {
if (value == null || value.trim().isEmpty()) {
return false;
}
try {
URI uri = URI.create(value.trim());
String host = uri.getHost();
return host != null && host.contains(":");
} catch (Exception ignored) {
return false;
}
}
private void writeRuntimeConfig(VpnClientConfig config, boolean forceFullTunnel, boolean fastPathMode) {
try {
getSharedPreferences(PREFS, MODE_PRIVATE).edit()
@@ -935,11 +994,15 @@ public class RapVpnService extends VpnService {
+ " connection=" + present(vpnConnectionId), 0, 0, 0, 0);
return;
}
List<String> relayUrls = dedupeRelayUrls(candidateUrls, backendUrl);
List<String> relayUrls = activeFabricServiceChannel.enabled ? dedupeFabricRelayUrls(candidateUrls, backendUrl) : dedupeRelayUrls(candidateUrls, backendUrl);
String selectedRelayUrl = relayUrls.isEmpty() ? "" : relayUrls.get(0);
if (selectedRelayUrl == null || selectedRelayUrl.isEmpty()) {
if ((selectedRelayUrl == null || selectedRelayUrl.isEmpty()) && !activeFabricServiceChannel.enabled) {
selectedRelayUrl = backendUrl;
}
if (selectedRelayUrl == null || selectedRelayUrl.isEmpty()) {
writeRuntimeStatus("error", "relay not started: missing fabric farm entry endpoint", 0, 0, 0, 0);
return;
}
activePacketRelayUrlByProfile = selectedRelayUrl;
activePacketRelayUrlsByProfile = new ArrayList<>(relayUrls);
activePacketRelayIndex = Math.max(0, relayUrls.indexOf(selectedRelayUrl));
@@ -971,14 +1034,18 @@ public class RapVpnService extends VpnService {
downlinkQueues[i] = new ArrayBlockingQueue<>(downlinkPerFlowCapacity);
}
configureBackendBypass(selectedRelayUrl);
if (PACKET_WEBSOCKET_DATAPLANE_ENABLED) {
startPacketWebSocketRelay(selectedRelayUrl, clusterId, vpnConnectionId);
} else {
writeRuntimeDetail("http_packet_batch", "packet websocket disabled; using confirmed HTTP batches", "relay", 0, 0, "", -1);
}
Log.i(TAG, "packet relay starting: backend=" + selectedRelayUrl + " cluster=" + clusterId + " vpn_connection=" + vpnConnectionId);
writeRuntimeStatus("relay", "relay starting " + vpnConnectionId, 0, 0, 0, 0);
writeRuntimeDetail("running", "packet relay active", "relay", 0, 0, "");
final String resetRelayUrl = selectedRelayUrl;
Thread resetThread = new Thread(() -> {
final String legacyResetRelayUrl = selectedRelayUrl;
Thread resetThread = activeFabricServiceChannel.enabled ? null : new Thread(() -> {
try {
RapApiClient uplinkClient = packetRelayClientForUrl(resetRelayUrl);
RapApiClient uplinkClient = packetRelayClientForUrl(legacyResetRelayUrl);
JSONObject reset = uplinkClient.resetVPNPacketQueues(clusterId, vpnConnectionId);
Log.i(TAG, "packet relay queues reset: " + reset.toString());
writeRuntimeStatus("relay_reset", reset.toString(), 0, 0, 0, 0);
@@ -996,7 +1063,12 @@ public class RapVpnService extends VpnService {
downlinkThread = new Thread(() -> runDownlinkWithRestart(clusterId, vpnConnectionId), "rap-vpn-downlink-receiver");
downlinkWriterThread = new Thread(this::pumpDownlinkQueueToTun, "rap-vpn-downlink-writer");
runtimeWatchdogThread = new Thread(() -> runRuntimeWatchdog(clusterId, vpnConnectionId), "rap-vpn-runtime-watchdog");
diagnosticWatchdogThread = new Thread(this::runDiagnosticServiceWatchdog, "rap-vpn-diagnostic-watchdog");
if (resetThread != null) {
resetThread.start();
} else {
writeRuntimeStatus("farm_dataplane", "backend relay queue reset skipped; farm owns vpn packet routes", 0, 0, 0, 0);
}
uplinkThread.start();
for (Thread senderThread : uplinkSenderThreads) {
senderThread.start();
@@ -1004,6 +1076,7 @@ public class RapVpnService extends VpnService {
downlinkThread.start();
downlinkWriterThread.start();
runtimeWatchdogThread.start();
diagnosticWatchdogThread.start();
}
private List<String> singletonUrl(String value) {
@@ -1057,6 +1130,33 @@ public class RapVpnService extends VpnService {
return out;
}
private List<String> dedupeFabricRelayUrls(List<String> candidateUrls, String backendUrl) {
List<String> ipv4OrHost = new ArrayList<>();
List<String> ipv6 = new ArrayList<>();
boolean backendIsPrivate = isPrivateURLHost(backendUrl);
if (candidateUrls != null) {
for (String url : candidateUrls) {
String normalized = normalizeHTTPBaseUrl(url);
if (!backendIsPrivate && isPrivateURLHost(normalized)) {
continue;
}
if (isIPv6URLHost(normalized)) {
addUniqueUrl(ipv6, normalized);
} else {
addUniqueUrl(ipv4OrHost, normalized);
}
}
}
List<String> out = new ArrayList<>();
for (String url : ipv4OrHost) {
addUniqueUrl(out, url);
}
for (String url : ipv6) {
addUniqueUrl(out, url);
}
return out;
}
private void addUniqueUrl(List<String> urls, String value) {
if (urls == null || value == null) {
return;
@@ -1132,6 +1232,9 @@ public class RapVpnService extends VpnService {
if (next == null || next.isEmpty() || next.equals(normalizedFailed)) {
continue;
}
if (isIPv6URLHost(next) && hasNonIPv6RelayUrl(urls)) {
continue;
}
activePacketRelayIndex = nextIndex;
activePacketRelayUrlByProfile = next;
configureBackendBypass(next);
@@ -1145,6 +1248,18 @@ public class RapVpnService extends VpnService {
}
}
private boolean hasNonIPv6RelayUrl(List<String> urls) {
if (urls == null) {
return false;
}
for (String url : urls) {
if (url != null && !url.isEmpty() && !isIPv6URLHost(url)) {
return true;
}
}
return false;
}
private String selectReachablePacketRelayUrl(List<String> relayUrls, String clusterId, String vpnConnectionId) {
if (relayUrls == null || relayUrls.isEmpty()) {
return "";
@@ -1190,11 +1305,13 @@ public class RapVpnService extends VpnService {
interruptAndJoin(downlinkThread);
interruptAndJoin(downlinkWriterThread);
interruptAndJoin(runtimeWatchdogThread);
interruptAndJoin(diagnosticWatchdogThread);
uplinkThread = null;
uplinkSenderThreads = null;
downlinkThread = null;
downlinkWriterThread = null;
runtimeWatchdogThread = null;
diagnosticWatchdogThread = null;
uplinkWorkerCount = 0;
downlinkFlowQueueCount = 0;
uplinkQueues = null;
@@ -1411,10 +1528,6 @@ public class RapVpnService extends VpnService {
return;
}
long now = System.currentTimeMillis();
if (now - lastDiagnosticEnsureAt >= 10000) {
lastDiagnosticEnsureAt = now;
ensureDiagnosticServiceHealthy();
}
int stale = staleTCPHandshakeCount();
if (stale <= 0) {
continue;
@@ -1440,6 +1553,29 @@ public class RapVpnService extends VpnService {
}
}
private void runDiagnosticServiceWatchdog() {
getSharedPreferences(PREFS, MODE_PRIVATE).edit()
.putLong("diagnostic_watchdog_started_at", System.currentTimeMillis())
.putString("diagnostic_watchdog_last_action", "started")
.apply();
while (running) {
try {
ensureDiagnosticServiceHealthy();
Thread.sleep(DIAGNOSTIC_WATCHDOG_INTERVAL_MS);
} catch (InterruptedException e) {
if (!running) {
return;
}
} catch (Exception e) {
getSharedPreferences(PREFS, MODE_PRIVATE).edit()
.putLong("diagnostic_watchdog_last_ensure_at", System.currentTimeMillis())
.putString("diagnostic_watchdog_last_action", "failed")
.putString("diagnostic_watchdog_last_error", e.getClass().getSimpleName() + ": " + e.getMessage())
.apply();
}
}
}
private boolean shouldHardRestartRuntime(long now) {
if (runtimeWatchdogRecoveries.get() < 2) {
return false;
@@ -1636,14 +1772,13 @@ public class RapVpnService extends VpnService {
System.arraycopy(packet, 0, copy, 0, length);
if (!hasIPv4Source(copy, length)) {
long mismatch = uplinkSourceMismatchPackets.incrementAndGet();
String natKey = natKeyForOutboundReturn(copy, length);
if (natKey.isEmpty() || !rewriteIPv4SourceToVPN(copy, length, natKey)) {
Log.w(TAG, "vpn uplink source is not vpn address; dropping " + packetSummary(copy, length));
writeRuntimeDetail("source_drop", packetSummary(copy, length), "uplink", -1, mismatch, "SOURCE_MISMATCH");
recordUplinkDrop(length);
return;
}
writeRuntimeDetail("source_nat", packetSummary(copy, length), "uplink", -1, mismatch, "SOURCE_NAT");
if (clampIPv4TCPMSS(copy, length, VPN_TCP_MSS_CLAMP)) {
writeRuntimeDetail("tcp_mss_clamp", packetSummary(copy, length), "uplink_tcp", -1, -1, "");
}
recordOutboundTCPHandshake(copy, length);
if (handleLocalDnsQuery(copy, length)) {
@@ -1989,6 +2124,7 @@ public class RapVpnService extends VpnService {
return;
}
String key = tcpFlowKey(flow.srcIp, flow.srcPort, flow.dstPort);
writeRuntimeDetail("tcp_syn_ack", key, "downlink_tcp", downlinkReceivedPackets.get(), tcpHandshakeStalls.get(), "");
synchronized (pendingTcpHandshakes) {
if (pendingTcpHandshakes.containsKey(key)) {
pendingTcpHandshakes.put(key, -System.currentTimeMillis());
@@ -2014,7 +2150,11 @@ public class RapVpnService extends VpnService {
}
private boolean isTCPPriorityPacket(byte[] packet, int length) {
if (packet == null || length < 40 || ((packet[0] >> 4) & 0x0f) != 4) {
return false;
}
private boolean clampIPv4TCPMSS(byte[] packet, int length, int maxMss) {
if (packet == null || length < 40 || maxMss <= 0 || ((packet[0] >> 4) & 0x0f) != 4) {
return false;
}
int ihl = (packet[0] & 0x0f) * 4;
@@ -2025,17 +2165,47 @@ public class RapVpnService extends VpnService {
if (totalLength <= 0 || totalLength > length) {
totalLength = length;
}
int tcpHeaderLength = ((packet[ihl + 12] >> 4) & 0x0f) * 4;
if (tcpHeaderLength < 20 || ihl + tcpHeaderLength > totalLength) {
int tcpOffset = ihl;
int tcpHeaderLength = ((packet[tcpOffset + 12] >> 4) & 0x0f) * 4;
if (tcpHeaderLength < 20 || tcpOffset + tcpHeaderLength > totalLength) {
return false;
}
int flags = packet[ihl + 13] & 0xff;
int flags = packet[tcpOffset + 13] & 0xff;
boolean syn = (flags & 0x02) != 0;
boolean fin = (flags & 0x01) != 0;
boolean rst = (flags & 0x04) != 0;
boolean ack = (flags & 0x10) != 0;
int payloadLength = totalLength - ihl - tcpHeaderLength;
return syn || fin || rst || (ack && payloadLength == 0);
if (!syn || ack || tcpHeaderLength <= 20) {
return false;
}
int option = tcpOffset + 20;
int end = tcpOffset + tcpHeaderLength;
while (option < end) {
int kind = packet[option] & 0xff;
if (kind == 0) {
break;
}
if (kind == 1) {
option++;
continue;
}
if (option + 1 >= end) {
break;
}
int optionLength = packet[option + 1] & 0xff;
if (optionLength < 2 || option + optionLength > end) {
break;
}
if (kind == 2 && optionLength == 4) {
int current = u16(packet, option + 2);
if (current > maxMss) {
putU16(packet, option + 2, maxMss);
normalizeIPv4PacketChecksums(packet, length);
return true;
}
return false;
}
option += optionLength;
}
return false;
}
private String tcpFlowKey(String remoteIp, int remotePort, int localPort) {
@@ -2246,7 +2416,8 @@ public class RapVpnService extends VpnService {
}
recordUplinkDrop(Math.max(0, batchBytes - 4));
writeRuntimeStatus("degraded", "uplink send failed after retry; continuing", 0, sentPackets, 0, errors);
writeRuntimeDetail("error", "uplink send failed after retry batch=" + batch.size(), "uplink_sender", sentPackets, errors, "SEND_RETRY_EXHAUSTED", workerIndex);
String retryError = lastUplinkSendErrorMessage == null || lastUplinkSendErrorMessage.isEmpty() ? "" : " last_error=" + lastUplinkSendErrorMessage;
writeRuntimeDetail("error", "uplink send failed after retry batch=" + batch.size() + retryError, "uplink_sender", sentPackets, errors, "SEND_RETRY_EXHAUSTED", workerIndex);
continue;
}
sentPackets += batch.size();
@@ -2289,6 +2460,7 @@ public class RapVpnService extends VpnService {
private boolean sendUplinkBatchWithRetry(String clusterId, String vpnConnectionId, List<byte[]> batch, int workerIndex) {
Exception lastError = null;
lastUplinkSendErrorMessage = "";
int relayAttempts = Math.max(1, activePacketRelayUrlsByProfile == null ? 1 : activePacketRelayUrlsByProfile.size());
for (int relayAttempt = 0; relayAttempt < relayAttempts && running; relayAttempt++) {
String relayUrl = currentPacketRelayUrl();
@@ -2308,7 +2480,8 @@ public class RapVpnService extends VpnService {
return true;
} catch (Exception e) {
lastError = e;
writeRuntimeDetail("retry", "uplink send retry worker=" + workerIndex + " relay=" + relayUrl + " attempt=" + attempt + " error=" + e.getClass().getSimpleName(), "uplink_sender", -1, -1, e.getClass().getSimpleName(), workerIndex);
lastUplinkSendErrorMessage = compactException(e);
writeRuntimeDetail("retry", "uplink send retry worker=" + workerIndex + " relay=" + relayUrl + " attempt=" + attempt + " error=" + lastUplinkSendErrorMessage, "uplink_sender", -1, -1, e.getClass().getSimpleName(), workerIndex);
sleepQuietly(UPLINK_SEND_RETRY_SLEEP_MS * (attempt + 1L));
}
}
@@ -2323,6 +2496,9 @@ public class RapVpnService extends VpnService {
}
private boolean sendUplinkBatchOverWebSocket(String relayUrl, String clusterId, String vpnConnectionId, List<byte[]> batch, int workerIndex) {
if (!PACKET_WEBSOCKET_DATAPLANE_ENABLED) {
return false;
}
VpnPacketWebSocketRelay relay = packetWebSocketRelay;
if (relay == null || relayUrl == null || !relayUrl.equals(relay.baseUrl())) {
return false;
@@ -2555,6 +2731,7 @@ public class RapVpnService extends VpnService {
}
addDefaultDnsProbeDomains(domains);
int resolved = 0;
int tcpOk = 0;
String last = "";
long warmUntil = System.currentTimeMillis() + 30000;
int pass = 0;
@@ -2579,7 +2756,14 @@ public class RapVpnService extends VpnService {
}
sleepQuietly(120);
}
if (passResolved >= Math.min(3, domains.size()) && pass >= 2) {
if (pass <= VPN_TCP_WARMUP_PASSES) {
int passTcpOk = runTCPWarmupPass(vpn);
tcpOk += passTcpOk;
if (passTcpOk > 0) {
last = "tcp_warmup_ok=" + passTcpOk;
}
}
if (passResolved >= Math.min(3, domains.size()) && tcpOk > 0 && pass >= 2) {
break;
}
sleepQuietly(750);
@@ -2588,15 +2772,60 @@ public class RapVpnService extends VpnService {
return;
}
if (resolved > 0) {
writeRuntimeStatus("ready", "vpn ready; dns warmup ok " + resolved + " " + dnsInfo + " " + last, 0, 0, downlinkReceivedPackets.get(), 0);
writeRuntimeStatus("ready", "vpn ready; warmup dns=" + resolved + " tcp=" + tcpOk + " " + dnsInfo + " " + last, 0, 0, downlinkReceivedPackets.get(), 0);
} else {
writeRuntimeStatus("warming", "vpn started; dns warmup pending " + dnsInfo + " " + last, 0, 0, downlinkReceivedPackets.get(), 1);
writeRuntimeStatus("warming", "vpn started; warmup pending dns=0 tcp=" + tcpOk + " " + dnsInfo + " " + last, 0, 0, downlinkReceivedPackets.get(), 1);
}
Log.i(TAG, "vpn readiness warmup complete: connection=" + vpnConnectionId + " resolved=" + resolved + " " + dnsInfo + " " + last);
Log.i(TAG, "vpn readiness warmup complete: connection=" + vpnConnectionId + " resolved=" + resolved + " tcp=" + tcpOk + " " + dnsInfo + " " + last);
}, "rap-vpn-readiness-warmup");
thread.start();
}
private int runTCPWarmupPass(Network vpn) {
int ok = 0;
for (String target : DEFAULT_TCP_WARMUP_TARGETS) {
if (!running) {
return ok;
}
TCPWarmupTarget parsed = parseTCPWarmupTarget(target);
if (parsed == null) {
continue;
}
long started = System.currentTimeMillis();
try (Socket socket = new Socket()) {
if (vpn != null) {
vpn.bindSocket(socket);
}
socket.connect(new InetSocketAddress(parsed.host, parsed.port), VPN_TCP_WARMUP_CONNECT_TIMEOUT_MS);
ok++;
writeRuntimeDetail("tcp_warmup", target + " ms=" + (System.currentTimeMillis() - started), "readiness", ok, 0, "", -1);
} catch (Exception e) {
writeRuntimeDetail("tcp_warmup_failed", target + " " + e.getClass().getSimpleName(), "readiness", ok, 1, e.getClass().getSimpleName(), -1);
}
sleepQuietly(120);
}
return ok;
}
private TCPWarmupTarget parseTCPWarmupTarget(String target) {
if (target == null) {
return null;
}
int split = target.lastIndexOf(':');
if (split <= 0 || split >= target.length() - 1) {
return null;
}
try {
int port = Integer.parseInt(target.substring(split + 1));
if (port <= 0 || port > 65535) {
return null;
}
return new TCPWarmupTarget(target.substring(0, split), port);
} catch (NumberFormatException e) {
return null;
}
}
private static void addDefaultDnsProbeDomains(Set<String> domains) {
if (domains == null) {
return;
@@ -2724,6 +2953,9 @@ public class RapVpnService extends VpnService {
}
private List<byte[]> receiveDownlinkBatch(String relayUrl, RapApiClient client, String clusterId, String vpnConnectionId, int timeoutMs) throws Exception {
if (!PACKET_WEBSOCKET_DATAPLANE_ENABLED) {
return client.receiveClientPacketBatch(clusterId, vpnConnectionId, timeoutMs);
}
VpnPacketWebSocketRelay relay = packetWebSocketRelay;
if (relay != null && relayUrl != null && relayUrl.equals(relay.baseUrl())) {
List<byte[]> packets = relay.receiveClientPacketBatch(clusterId, vpnConnectionId, timeoutMs);
@@ -2890,6 +3122,16 @@ public class RapVpnService extends VpnService {
}
}
private static class TCPWarmupTarget {
final String host;
final int port;
TCPWarmupTarget(String host, int port) {
this.host = host;
this.port = port;
}
}
private boolean writePacketToTun(FileDescriptor fd, byte[] packet, int packetLength) throws Exception {
int offset = 0;
int attempts = 0;
@@ -2950,6 +3192,19 @@ public class RapVpnService extends VpnService {
}
}
private String compactException(Exception e) {
if (e == null) {
return "";
}
String message = e.getMessage();
String value = e.getClass().getSimpleName() + (message == null || message.trim().isEmpty() ? "" : ": " + message.trim());
value = value.replace('\n', ' ').replace('\r', ' ').trim();
if (value.length() > 240) {
return value.substring(0, 240);
}
return value;
}
private void closeFdQuietly(FileDescriptor fd) {
if (fd == null) {
return;
@@ -3064,6 +3319,8 @@ public class RapVpnService extends VpnService {
.putLong("runtime_watchdog_recoveries", runtimeWatchdogRecoveries.get())
.putLong("tcp_handshake_stalls", tcpHandshakeStalls.get())
.putLong("runtime_watchdog_hard_restarts", runtimeWatchdogHardRestarts.get())
.putLong("diagnostic_watchdog_ensure_requests", diagnosticEnsureRequests.get())
.putLong("diagnostic_watchdog_restart_requests", diagnosticRestartRequests.get())
.putLong("uplink_source_mismatch_packets", uplinkSourceMismatchPackets.get())
.putLong("downlink_destination_mismatch_packets", downlinkDestinationMismatchPackets.get())
.putFloat("uplink_read_mbps", uplinkReadMbps)
@@ -1250,6 +1250,162 @@ then reports the expected next event window without mailbox reads, drains, acks,
or consumer cursor mutation. The live smoke is
`scripts/fabric/c19z1-remote-workspace-mailbox-preflight-smoke.ps1`.
C19Z2 adds telemetry for mailbox preflight checks. Workload status and heartbeat
reports now expose preflight totals, ack/checkpoint split counters, and the last
preflight cursor/window fields so diagnostics can distinguish handoff checks
from mailbox reads. The live smoke is
`scripts/fabric/c19z2-remote-workspace-mailbox-preflight-telemetry-smoke.ps1`.
C19Z3 adds stale-cursor diagnostics to mailbox preflight. If a consumer cursor
falls behind retained mailbox events after bounded-mailbox drops, preflight
reports retained sequence bounds, `stale_cursor`, `diagnostic_state`, and
`missing_dropped_count`; the latest stale state is also visible in telemetry and
readiness diagnostics. The live smoke is
`scripts/fabric/c19z3-remote-workspace-mailbox-stale-preflight-smoke.ps1`.
C19Z4 adds action hints to mailbox preflight diagnostics. Stale cursor gaps now
return `recommended_action=reset_consumer_and_resync` plus hints to reset the
consumer cursor, request full adapter resync, and resume from checkpoint after
resync. The live smoke is
`scripts/fabric/c19z4-remote-workspace-mailbox-preflight-action-hints-smoke.ps1`.
C19Z5 adds provenance for the selected preflight action. Responses, telemetry,
and readiness diagnostics include `action_reason` and structured
`action_context` with cursor, retained sequence bounds, dropped/missing counts,
and expected window values. The live smoke is
`scripts/fabric/c19z5-remote-workspace-mailbox-preflight-provenance-smoke.ps1`.
C19Z6 adds the operator-facing summary for mailbox preflight. Responses,
telemetry, and readiness diagnostics include `operator_summary` plus compact
`operator_summary_fields` for the diagnostic state, selected action, reason,
cursor, retained bounds, and expected window counters. The live smoke is
`scripts/fabric/c19z6-remote-workspace-mailbox-preflight-summary-smoke.ps1`.
C19Z7 adds machine-sortable operator severity for mailbox preflight. Responses,
telemetry, readiness diagnostics, and summary fields expose `operator_status`
and `operator_severity`, classifying ready windows, caught-up cursors, and
stale cursor gaps without parsing summary text. The live smoke is
`scripts/fabric/c19z7-remote-workspace-mailbox-preflight-severity-smoke.ps1`.
C19Z8 adds the grouped readiness rollup for mailbox preflight. The readiness
diagnostic keeps the flat fields and adds `last_preflight` with observed time,
cursor, counts, diagnostic state, action hints/provenance, operator summary,
status, severity, and summary fields. The live smoke is
`scripts/fabric/c19z8-remote-workspace-mailbox-preflight-rollup-smoke.ps1`.
C19Z9 adds retained-window detail to that preflight rollup. The grouped
`last_preflight` readiness object includes first/last retained sequence and
mailbox dropped total so stale cursor explanations are visible without opening
the raw preflight response. The live smoke is
`scripts/fabric/c19z9-remote-workspace-mailbox-preflight-retained-window-smoke.ps1`.
C19Z10 adds a structured remediation checklist to that rollup. The grouped
`last_preflight.remediation_checklist` entries expose required/satisfied
operator steps derived from action hints, including cursor reset, full adapter
resync, and resume after resync for stale cursor gaps. The live smoke is
`scripts/fabric/c19z10-remote-workspace-mailbox-preflight-checklist-smoke.ps1`.
C19Z11 adds checklist status and counts to that rollup. The grouped
`last_preflight` readiness object exposes `remediation_checklist_status` and
total/required/satisfied/pending counts for admin UI summaries. The live smoke
is
`scripts/fabric/c19z11-remote-workspace-mailbox-preflight-checklist-status-smoke.ps1`.
C19Z12 adds session-level preflight operator status/severity counters.
Readiness exposes status and severity count maps, mirrored in `last_preflight`,
so repeated resync-required/warn preflights are visible without retaining a
history log. The live smoke is
`scripts/fabric/c19z12-remote-workspace-mailbox-preflight-status-counts-smoke.ps1`.
C19Z13 adds compact preflight attention status on top of those counters.
Readiness and `last_preflight` expose `preflight_attention_status` so admin UI
can sort clean, attention-needed, and repeated-resync sessions without
interpreting count maps. The live smoke is
`scripts/fabric/c19z13-remote-workspace-mailbox-preflight-attention-smoke.ps1`.
C19Z14 proves the repeated-resync attention branch. Unit and live smoke coverage
perform multiple stale preflights on one active adapter session and verify
`preflight_attention_status=repeated_resync_required` with repeated
resync-required/warn counters. The live smoke is
`scripts/fabric/c19z14-remote-workspace-mailbox-preflight-repeated-attention-smoke.ps1`.
C19Z15 adds the preflight attention reason. Readiness and `last_preflight`
expose `preflight_attention_reason` beside the attention status, explaining
clean, attention-needed, and repeated-resync states without UI-side counter
parsing. The live smoke is
`scripts/fabric/c19z15-remote-workspace-mailbox-preflight-attention-reason-smoke.ps1`.
C19Z16 completes focused proof coverage for those attention reasons. Unit tests
cover clean, single-resync, repeated-resync, and no-preflight mappings; live
smoke proves the single stale-preflight reason. The live smoke is
`scripts/fabric/c19z16-remote-workspace-mailbox-preflight-attention-reason-coverage-smoke.ps1`.
C19Z17 adds the preflight diagnostics contract marker. The readiness
`last_preflight` rollup includes `diagnostics_schema_version` and
`diagnostics_contract` entries for retained-window, remediation-checklist,
attention, and operator-count fields, allowing UI rendering to be gated safely.
The live smoke is
`scripts/fabric/c19z17-remote-workspace-mailbox-preflight-contract-smoke.ps1`.
C19Z18 adds boolean diagnostics feature flags to the same preflight rollup.
`last_preflight.diagnostics_features` now exposes retained-window,
remediation-checklist, attention, and operator-count support directly, so admin
UI and automation can gate each diagnostics group without scanning the contract
list. The live smoke is
`scripts/fabric/c19z18-remote-workspace-mailbox-preflight-feature-flags-smoke.ps1`.
C19Z19 proves compatibility between the two diagnostics contract forms. Unit
coverage and live smoke verify that workload and telemetry reports expose both
the string `diagnostics_contract` entries and matching boolean
`diagnostics_features` flags for every preflight diagnostics group. The live
smoke is
`scripts/fabric/c19z19-remote-workspace-mailbox-preflight-contract-compatibility-smoke.ps1`.
C19Z20 proves the no-preflight readiness shape. Before any mailbox preflight is
observed, active adapter sessions expose `preflight_attention_status=unknown`,
`preflight_attention_reason=no_preflight_observed`, zero session preflight
count, and no grouped `last_preflight` rollup. The live smoke is
`scripts/fabric/c19z20-remote-workspace-mailbox-preflight-absence-smoke.ps1`.
C19Z21 proves the no-active-session readiness shape. After closing the active
adapter session, readiness exposes idle/not-ready state, zero active sessions,
no active `adapter_session_id`, no grouped `last_preflight`, and terminal
`last_session_state=closed` from the terminal-session ledger. The live smoke is
`scripts/fabric/c19z21-remote-workspace-no-active-session-readiness-smoke.ps1`.
C19Z22 proves terminal-state readiness for `expire` and `reset` controls. The
same no-active-session readiness shape now reports
`last_session_state=expired` or `last_session_state=reset` from the
terminal-session ledger. The live smoke is
`scripts/fabric/c19z22-remote-workspace-terminal-state-readiness-smoke.ps1`.
C19Z23 adds grouped terminal-session summary metadata to no-active-session
readiness. `terminal_session_summary` carries adapter session id, terminal
state, reason, and control timestamp so admin UI can render the terminal cause
without stitching flat fields. The live smoke is
`scripts/fabric/c19z23-remote-workspace-terminal-session-summary-smoke.ps1`.
C19Z24 adds the terminal-session summary contract marker. The grouped summary
now carries schema version
`rap.remote_workspace_adapter_terminal_session_summary.v1` and a
summary-contract field list for explicit UI gating. The live smoke is
`scripts/fabric/c19z24-remote-workspace-terminal-summary-contract-smoke.ps1`.
C19Z25 adds boolean `summary_features` to the same grouped terminal-session
summary, covering adapter session id, state, reason, and control timestamp. The
live smoke is
`scripts/fabric/c19z25-remote-workspace-terminal-summary-features-smoke.ps1`.
C19Z26 proves compatibility between `summary_contract` and `summary_features`
for the grouped terminal-session summary in workload and telemetry reports. The
live smoke is
`scripts/fabric/c19z26-remote-workspace-terminal-summary-compatibility-smoke.ps1`.
C19Z27 proves the absence shape for terminal-session summary. Before any adapter
session or terminal history exists, readiness reports `waiting_for_session` and
does not include `terminal_session_summary`. The live smoke is
`scripts/fabric/c19z27-remote-workspace-terminal-summary-absence-smoke.ps1`.
Includes:
- container/native workload contract
@@ -1671,9 +1827,234 @@ with synthetic traffic only. C18 defines the VPN/IP tunnel service target model
authorize VPN/IP tunnel runtime. C18A adds the VPN/IP tunnel control-plane
data model and platform-admin skeleton only. C18B hardens single-active
lease/fencing semantics. C18C adds node-agent desired-state/status reporting
for scoped VPN assignments only. C19 is now reserved for the Version
Storage/Update Repository and node-agent update/rollback foundation; it is not
implemented by this document. No RDP, data-plane, VPN runtime, production
relay, production mesh service traffic, node-agent VPN execution, host
networking, service workload runtime, or production updater behavior is implied
by this document.
for scoped VPN assignments only. C19 Remote Workspace adapter probe layers are
still node-local and probe-only; through C19Z30, fresh no-session runtime
readiness exposes a grouped `no_session_summary` contract plus
`summary_features`, with compatibility proof across workload and telemetry,
while terminal-history readiness exposes `terminal_session_summary` and omits
`no_session_summary`; summary exclusivity is proven across fresh, active, and
terminal readiness states, and a compact readiness state matrix artifact exists
for admin/runtime handoff. C19Z34 records the explicit probe-to-runtime gates
and confirms Remote Workspace still has no production payload traffic. C19Z35
adds the disabled-by-default real-adapter supervision status scaffold without
enabling real adapter execution. C19Z36 proves that scaffold's env/status/
guardrail compatibility. C19Z37 adds sanitized config projection for the future
real adapter while still refusing activation and payload traffic. C19Z38 proves
that projection for both default/empty and requested config shapes. C19Z39 adds
an explicit blocked activation decision contract with required/missing gates.
C19Z40 adds a compact handoff report proving scaffold/projection/decision
alignment for requested and default node config.
C19Z41 adds explicit feature flags for those real-adapter supervision fields.
C19Z42 folds those feature flags into the compact handoff report for
admin/runtime handoff.
C19Z43 proves contract-probe precedence when real-adapter supervision is also
requested in desired workload config.
C19Z44 proves the real-adapter-only desired workload path remains degraded and
blocked.
C19Z45 adds a compact desired-workload mode matrix for probe-only,
real-adapter-only, and combined requested modes.
C19Z46 adds compatibility proof for the mode matrix row contract.
C19Z47 adds a disabled process-supervisor preconditions contract for future
external RDP worker supervision.
C19Z48 proves that contract across requested/default config shapes.
C19Z49 folds process-supervisor preconditions into the compact handoff report.
C19Z50 folds process-supervisor preconditions into the desired-workload mode
matrix.
C19Z51 proves the mode matrix v2 row contract.
C19Z52 adds a disabled process-health-probe contract for future external RDP
worker supervision.
C19Z53 proves that process-health-probe contract across requested/default
status forms.
C19Z54 folds process-health-probe visibility into the compact real-adapter
handoff report.
C19Z55 folds process-health-probe visibility into the desired-workload mode
matrix.
C19Z56 proves the mode matrix v3 row contract.
C19Z57 adds a compact disabled real-adapter readiness/handoff checklist.
C19Z58 proves the readiness/handoff summary and checklist contract.
C19Z59 adds a disabled real-adapter operator action map.
C19Z60 proves the disabled real-adapter operator action map contract.
C19Z61 adds a compact disabled real-adapter admin handoff bundle.
C19Z62 proves the disabled real-adapter admin handoff bundle contract.
C19Z63 adds compact disabled real-adapter admin handoff digest rows.
C19Z64 proves the disabled real-adapter admin handoff digest row contract.
C19Z65 adds a disabled real-adapter admin handoff digest rollup.
C19Z66 proves the disabled real-adapter admin handoff digest rollup contract.
C19Z67 adds a disabled real-adapter admin handoff full-chain summary.
C19Z68 proves the disabled real-adapter admin handoff full-chain summary
contract.
C19Z69 adds a disabled real-adapter admin handoff release marker.
C19Z70 proves the disabled real-adapter admin handoff release marker contract.
C19Z71 adds a final contract-only package index for the disabled real-adapter
admin handoff chain.
C19Z72 proves the final package index contract for the disabled real-adapter
admin handoff chain.
C19Z73 adds a contract-only runtime gate phase boundary for the next disabled
real-adapter preflight phase.
C19Z74 proves the runtime gate phase boundary contract.
C19Z75 adds a disabled real-adapter runtime gate preflight checklist with all
items still blocking runtime.
C19Z76 proves the disabled real-adapter runtime gate preflight checklist
contract.
C19Z77 adds a disabled real-adapter runtime gate preflight status summary.
C19Z78 proves the disabled real-adapter runtime gate preflight status summary
contract.
C19Z79 adds disabled real-adapter runtime gate preflight action hints.
C19Z80 proves the disabled real-adapter runtime gate preflight action hints
contract.
C19Z81 adds a disabled real-adapter runtime gate preflight operator handoff
bundle.
C19Z82 proves the disabled real-adapter runtime gate preflight operator handoff
bundle contract.
C19Z83 adds a disabled real-adapter runtime gate preflight release marker.
C19Z84 proves the disabled real-adapter runtime gate preflight release marker
contract.
C19Z85 adds a disabled real-adapter runtime gate preflight package index.
C19Z86 proves the disabled real-adapter runtime gate preflight package index
contract.
C19Z87 adds a disabled real-adapter runtime gate preflight closeout summary.
C19Z88 proves the disabled real-adapter runtime gate preflight closeout summary
contract.
C19Z89 starts the explicit real-adapter runtime gate enablement phase with a
contract-only request that remains blocked pending validation.
C19Z90 proves the explicit real-adapter runtime gate enablement request
contract.
C19Z91 adds contract-only operator confirmation validation while keeping the
runtime gate blocked pending remaining validations.
C19Z92 proves the operator confirmation validation contract.
C19Z93 adds contract-only binary validation while keeping the runtime gate
blocked pending remaining validations.
C19Z94 proves the binary validation contract.
C19Z95 adds contract-only permission validation while keeping the runtime gate
blocked pending remaining validations.
C19Z96 proves the permission validation contract.
C19Z97 adds contract-only supervisor validation while keeping the runtime gate
blocked pending remaining validations.
C19Z98 proves the supervisor validation contract.
C19Z99 adds contract-only health probe validation while keeping the runtime gate
blocked pending payload gate validation.
C19Z100 proves the health probe validation contract.
C19Z101 adds contract-only payload gate validation with no remaining required
validations while keeping runtime not enabled.
C19Z102 proves the payload gate validation contract.
C19Z103 adds the runtime gate validation closeout while keeping explicit
operator enablement required.
C19Z104 proves the runtime gate validation closeout contract.
C19Z105 adds an operator enablement readiness package while keeping runtime
disabled by default.
C19Z106 proves the operator enablement readiness package contract.
C19Z107 adds an operator enablement readiness release marker while keeping
runtime disabled by default.
C19Z108 proves the operator enablement readiness release marker contract.
C19Z109 adds an operator enablement readiness package index while keeping
runtime disabled by default.
C19Z110 proves the operator enablement readiness package index contract.
C19Z111 adds an operator readiness closeout summary while keeping runtime
disabled by default.
C19Z112 proves the operator readiness closeout summary contract.
C19Z113 adds an operator review decision request while keeping runtime disabled
by default.
C19Z114 proves the operator review decision request contract.
C19Z115 adds an operator decision status summary while keeping runtime disabled
by default.
C19Z116 proves the operator decision status summary contract.
C19Z117 adds an operator approval/rejection outcome contract with the outcome
not approved and runtime disabled by default.
C19Z118 proves the operator approval/rejection outcome contract.
C19Z119 adds an operator outcome closeout/reopen boundary while keeping runtime
disabled by default.
C19Z120 proves the operator outcome closeout/reopen boundary contract.
C19Z121 adds a not-approved outcome release marker while keeping runtime
disabled by default.
C19Z122 proves the not-approved outcome release marker contract.
C19Z123 adds a not-approved outcome package index while keeping runtime disabled
by default.
C19Z124 proves the not-approved outcome package index contract.
C19Z125 adds a not-approved outcome closeout summary while keeping runtime
disabled by default.
C19Z126 proves the not-approved outcome closeout summary contract.
C19Z127 adds a final not-approved outcome release marker while keeping runtime
disabled by default.
C19Z128 proves the final not-approved outcome release marker contract.
C19Z129 adds a final not-approved outcome package index/archive marker while
keeping runtime disabled by default.
C19Z130 proves the final not-approved outcome package index/archive marker
contract.
C19Z131 adds a not-approved outcome archive closeout manifest while keeping
runtime disabled by default.
C19Z132 proves the not-approved outcome archive closeout manifest contract.
C19Z133 adds a stopped-branch sentinel for the not-approved outcome while
keeping runtime disabled by default.
C19Z134 proves the not-approved outcome stopped-branch sentinel contract.
C19Z135 adds a no-continuation guard for the stopped not-approved outcome while
keeping runtime disabled by default.
C19Z136 proves the not-approved outcome no-continuation guard contract.
C19Z137 adds continuation block enforcement for the stopped not-approved
outcome while keeping runtime disabled by default.
C19Z138 proves the not-approved outcome continuation block enforcement
contract.
C19Z139 adds a continuation block audit record for the stopped not-approved
outcome while keeping runtime disabled by default.
C19Z140 proves the not-approved outcome continuation block audit record
contract.
C19Z141 adds a continuation block audit rollup for the stopped not-approved
outcome while keeping runtime disabled by default.
C19Z142 proves the not-approved outcome continuation block audit rollup
contract.
C19Z143 adds an operator stop summary for the stopped not-approved outcome
while keeping runtime disabled by default.
C19Z144 proves the not-approved outcome operator stop summary contract.
C19Z145 adds an operator stop handoff for the stopped not-approved outcome
while keeping runtime disabled by default.
C19Z146 proves the not-approved outcome operator stop handoff contract.
C19Z147 adds an operator stop handoff digest for the stopped not-approved
outcome while keeping runtime disabled by default.
C19Z148 proves the not-approved outcome operator stop handoff digest contract.
C19Z149 adds an operator stop status snapshot for the stopped not-approved
outcome while keeping runtime disabled by default.
C19Z150 proves the not-approved outcome operator stop status snapshot contract.
C19Z151 adds an operator stop status snapshot index for the stopped
not-approved outcome while keeping runtime disabled by default.
C19Z152 proves the not-approved outcome operator stop status snapshot index
contract.
C19Z153 adds an operator stop status catalog for the stopped not-approved
outcome while keeping runtime disabled by default.
C19Z154 proves the not-approved outcome operator stop status catalog contract.
C19Z155 adds an operator stop status catalog release marker for the stopped
not-approved outcome while keeping runtime disabled by default.
C19Z156 proves the not-approved outcome operator stop status catalog release
marker contract.
C19Z157 adds an operator stop status catalog package index for the stopped
not-approved outcome while keeping runtime disabled by default.
C19Z158 proves the not-approved outcome operator stop status catalog package
index contract.
C19Z159 adds an operator stop status catalog closeout summary for the stopped
not-approved outcome while keeping runtime disabled by default.
C19Z160 proves the not-approved outcome operator stop status catalog closeout
summary contract.
C19Z161 adds an operator stop status final archive marker for the stopped
not-approved outcome while keeping runtime disabled by default.
C19Z162 proves the not-approved outcome operator stop status final archive
marker contract.
C19Z163 adds an operator stop status final archive manifest for the stopped
not-approved outcome while keeping runtime disabled by default.
C19Z164 proves the not-approved outcome operator stop status final archive
manifest contract.
C19Z165 adds a terminal-complete marker for the stopped not-approved outcome
factory while keeping runtime disabled by default.
C19Z166 proves the not-approved outcome factory terminal-complete contract.
C20Z1 opens a new explicit real-adapter enablement request while keeping
runtime disabled by default.
C20Z2 proves the new explicit real-adapter enablement request contract.
C20Z3 adds the operator validation intake for the new explicit request while
keeping runtime disabled by default.
C20Z4 completes the operator validation checklist contract while keeping
runtime disabled by default.
C20Z5 closes the operator validation chain contract while keeping runtime
disabled by default.
C20Z6 proves the C20 stage terminal-complete contract.
Version Storage/Update
Repository and node-agent update/rollback foundation are not implemented by
this document. No RDP, data-plane, VPN runtime, production relay, production
mesh service traffic, node-agent VPN execution, host networking, service
workload runtime, or production updater behavior is implied by this document.
@@ -1324,6 +1324,394 @@ C19Z1 adds a read-only mailbox handoff preflight endpoint. Adapter runtimes can
call `/mailbox/preflight` with `consumer_id` and `resume_from=ack|checkpoint`
to validate the stored cursor and inspect the next expected event window without
reading, draining, acking, or mutating consumer state.
C19Z2 adds separate telemetry for those handoff checks. Workload status and
heartbeat reports expose preflight totals split by ack/checkpoint cursor and the
last preflight session, consumer, cursor, after-sequence, available/returned/
skipped counts, and expected sequence range; readiness diagnostics mirror the
latest preflight summary.
C19Z3 adds stale-cursor diagnostics to preflight. When a consumer cursor points
behind dropped bounded-mailbox events, the preflight response reports retained
sequence bounds, `diagnostic_state=stale_cursor_gap`, `stale_cursor=true`, and
`missing_dropped_count`; workload/heartbeat telemetry and readiness diagnostics
mirror that latest stale state.
C19Z4 adds explicit action hints to those diagnostics. Preflight responses now
include `recommended_action` and `action_hints`; stale cursor gaps recommend
resetting the consumer cursor, requesting a full adapter resync, and resuming
from checkpoint after resync. Telemetry and readiness diagnostics mirror the
latest recommended action and hints.
C19Z5 adds remediation provenance for those hints. Preflight responses,
workload/heartbeat telemetry, and readiness diagnostics include
`action_reason` plus structured `action_context` with the resume cursor,
retained sequence bounds, dropped/missing counts, consumer checkpoint/ack, and
expected window counters that explain why the recommended action was chosen.
C19Z6 adds a compact operator-facing preflight summary derived from the same
read-only state. Preflight responses, telemetry, and readiness diagnostics now
include `operator_summary` and `operator_summary_fields` so dashboards can show
the diagnostic state, action, reason, resume cursor, retained bounds, and key
window counters without recomputing or mutating mailbox state.
C19Z7 adds machine-sortable operator status and severity to that summary.
Preflight responses, telemetry, readiness diagnostics, and
`operator_summary_fields` now expose `operator_status` and `operator_severity`
so dashboards can sort ready, caught-up, and resync-required handoffs without
parsing human text.
C19Z8 groups the latest preflight view for admin UI consumption. The readiness
diagnostic keeps all existing flat latest-preflight fields and adds
`last_preflight` with observed time, cursor, counts, diagnostic state, selected
action, action provenance, operator summary, status, severity, and summary
fields.
C19Z9 adds retained-window detail to that grouped readiness view. The
`last_preflight` object now includes first/last retained sequence and mailbox
dropped total so stale-cursor summaries can explain the bounded mailbox window
without requiring a separate raw preflight lookup.
C19Z10 adds a structured remediation checklist to the grouped readiness view.
The `last_preflight.remediation_checklist` entries are derived from diagnostic
state and action hints, marking required/satisfied operator steps for cursor
reset, adapter resync, and post-resync resume without executing those actions.
C19Z11 adds summary status and counts for that checklist. The grouped readiness
view now exposes `remediation_checklist_status` plus total, required,
satisfied, and pending counts so admin UI can render checklist state without
scanning the step array.
C19Z12 adds per-session preflight operator status/severity counters. Readiness
now exposes counts for statuses such as `ready_to_resume`, `caught_up`, and
`resync_required`, plus severity counts such as `ok`, `info`, and `warn`, and
the grouped latest-preflight rollup mirrors those counters for dashboard
context.
C19Z13 derives a compact preflight attention status from those counters.
Readiness and `last_preflight` expose `preflight_attention_status` values such
as `clean`, `needs_attention`, and `repeated_resync_required`, letting admin UI
sort sessions without interpreting count maps directly.
C19Z14 proves the repeated-resync branch. Unit and live smoke coverage now run
multiple stale preflights on the same active adapter session and verify
`preflight_attention_status=repeated_resync_required` with repeated
`resync_required` / `warn` counters, while the preflight path remains read-only.
C19Z15 adds `preflight_attention_reason` beside the attention status. The reason
is derived from the latest preflight counters/status and explains clean,
attention-needed, and repeated-resync states without requiring UI code to parse
the counter maps.
C19Z16 completes focused proof coverage for those reasons. Unit coverage proves
clean, single-resync, repeated-resync, and no-preflight mappings, and live smoke
proves the single stale-preflight `resync_required_preflight_observed` reason.
C19Z17 adds a diagnostics contract marker to the grouped preflight readiness
rollup. `last_preflight` now includes `diagnostics_schema_version` and a
`diagnostics_contract` list for retained-window, remediation-checklist,
attention, and operator-count fields so admin UI can gate rendering safely.
C19Z18 adds machine-readable feature flags for that contract. `last_preflight`
now includes boolean `diagnostics_features` entries for retained-window,
remediation-checklist, attention, and operator-count diagnostics, allowing UI
and automation clients to check support without scanning the contract list.
C19Z19 adds a compatibility proof for the two contract forms. Unit and live
smoke coverage now verify that workload and telemetry reports expose matching
`diagnostics_contract` entries and `diagnostics_features` booleans for each
preflight diagnostics group.
C19Z20 adds the no-preflight absence proof. Active adapter sessions that have
not observed a mailbox preflight report `preflight_attention_status=unknown`,
`preflight_attention_reason=no_preflight_observed`, zero session preflight
count, and no grouped `last_preflight` rollup, so UI can distinguish "not
observed yet" from an observed clean state.
C19Z21 adds the no-active-session readiness proof. After the last adapter
session is closed, readiness reports idle/not-ready with zero active sessions,
no active `adapter_session_id`, no `last_preflight` rollup, and terminal
`last_session_state=closed` from the terminal-session ledger.
C19Z22 extends terminal-state coverage to `expire` and `reset` controls. The
same no-active-session readiness shape now proves `last_session_state=expired`
and `last_session_state=reset` from the terminal-session ledger.
C19Z23 adds grouped terminal-session summary metadata for the no-active-session
case. Readiness now includes `terminal_session_summary` with adapter session id,
terminal state, reason, and control timestamp while retaining flat compatibility
fields.
C19Z24 adds a contract marker to that summary. The grouped
`terminal_session_summary` now carries a schema version and summary-contract
field list so UI can gate rendering explicitly.
C19Z25 adds boolean feature flags for the same grouped terminal summary fields,
mirroring the preflight diagnostics contract/feature pattern.
C19Z26 adds compatibility proof coverage for those two terminal summary contract
forms, verifying that `summary_contract` entries and `summary_features` booleans
stay aligned in workload and telemetry reports.
C19Z27 adds absence proof coverage for a fresh no-session runtime: before any
terminal history exists, readiness stays in `waiting_for_session` and does not
include `terminal_session_summary`.
C19Z28 adds the grouped no-session readiness summary for that empty-runtime
state. Fresh adapter readiness now includes `no_session_summary` with schema
version `rap.remote_workspace_adapter_no_session_summary.v1`, a summary
contract for `status`, `diagnostic_state`, `active_session_count`, and
`terminal_session_count`, and matching idle/waiting-for-session counts, while
the terminal-session summary remains absent until terminal history exists.
C19Z29 adds boolean `summary_features` to the same grouped no-session summary
for `status`, `diagnostic_state`, `active_session_count`, and
`terminal_session_count`, matching the terminal summary and preflight
diagnostics feature-flag convention.
C19Z30 adds compatibility proof coverage for the grouped no-session summary,
verifying that `summary_contract` entries and `summary_features` booleans stay
aligned in workload and telemetry reports.
C19Z31 adds the inverse terminal-history absence proof: after adapter sessions
reach terminal states, readiness exposes `terminal_session_summary` and omits
`no_session_summary` in workload and telemetry reports.
C19Z32 proves readiness summary exclusivity across the three runtime shapes:
fresh exposes only `no_session_summary`, active exposes neither grouped summary,
and terminal exposes only `terminal_session_summary`.
C19Z33 adds a compact readiness state matrix artifact for admin/runtime handoff:
fresh, active, and terminal rows are emitted for workload and telemetry with
only the relevant readiness fields and summary-presence booleans.
C19Z34 adds an explicit probe-to-runtime gate artifact. It confirms the current
Remote Workspace runtime is still `contract_probe`, `probe_only=true`, and
`payload_traffic=none`, lists the ready contracts, and records the remaining
runtime gates before real RDP frame transport can be enabled.
C19Z35 adds the disabled-by-default real-adapter supervision scaffold. The
`rdp-worker` contract-probe status now advertises
`rap.remote_workspace_real_adapter_supervision.v1` with future config env names,
status contract fields, and guardrails, while `contract_probe` remains the only
active execution mode and payload traffic remains `none`.
C19Z36 adds compatibility proof for that scaffold, verifying the disabled state,
status contract, env names, process model, and guardrails remain aligned in unit
and live workload status coverage.
C19Z37 adds disabled real-adapter config projection. Node-agent parses the
future `RAP_REMOTE_WORKSPACE_REAL_ADAPTER_*` env values and reports only
sanitized status metadata under
`real_adapter_supervision.config_projection`: whether enable was requested,
whether command/args/workdir are present, args JSON shape, and that raw values
are redacted. This does not activate the real adapter; `enabled=false`,
`activation_allowed=false`, and `payload_traffic=none` remain required.
C19Z38 proves projection compatibility across default/empty and requested
config shapes. Unit and live smoke coverage verify absent env and requested
env both keep activation blocked, raw values redacted, and payload traffic
disabled.
C19Z39 adds an explicit disabled activation decision contract. The real adapter
status now reports `decision=blocked`,
`reason=real_runtime_stage_not_enabled`, `activation_allowed=false`, and the
missing gates before a future stage may start an external RDP worker process.
C19Z40 adds a compact handoff report proving that the supervision scaffold,
config projection, and blocked activation decision remain aligned for both
requested and default config shapes.
C19Z41 adds real-adapter supervision feature flags for config projection,
activation decision, missing gates, and raw-value redaction so UI and
automation clients can gate rendering explicitly.
C19Z42 folds those feature flags into the compact handoff report, proving
scaffold/projection/decision/features alignment for requested and default node
config in one admin/runtime artifact.
C19Z43 proves contract-probe precedence when desired workload config includes
both `adapter_contract_probe` and `real_adapter_supervision`; the runtime stays
running in probe mode and real-adapter activation remains blocked.
C19Z44 proves the real-adapter-only desired workload path remains degraded and
blocked, with the same disabled activation contract and no payload traffic.
C19Z45 adds a compact desired-workload mode matrix for probe-only,
real-adapter-only, and combined requested modes, confirming all paths retain
disabled real-adapter activation and no payload traffic.
C19Z46 adds compatibility proof for that mode matrix row contract, including
explicit feature-flag and missing-gate visibility markers.
C19Z47 adds a disabled process-supervisor preconditions contract for the future
external RDP worker process while keeping `process_start_allowed=false` and all
payload traffic disabled.
C19Z48 proves that process-supervisor preconditions contract across requested
and default config shapes, including required/missing checks and disabled start.
C19Z49 folds process-supervisor preconditions into the compact handoff report,
proving alignment with projection, activation decision, and feature flags.
C19Z50 folds those preconditions into the desired-workload mode matrix, proving
process start remains disabled across probe-only, real-adapter-only, and
combined requested modes.
C19Z51 adds compatibility proof for that mode matrix v2 row contract.
C19Z52 adds a disabled process-health-probe contract for the future external
RDP worker process while keeping health probes disabled and payload traffic at
`none`.
C19Z53 proves that process-health-probe contract across requested/default
status forms.
C19Z54 folds process-health-probe visibility into the compact handoff report,
proving disabled health probes and payload-free alignment across all
real-adapter handoff contracts.
C19Z55 folds process-health-probe visibility into the desired-workload mode
matrix, proving disabled health probes and no payload traffic across probe-only,
real-adapter-only, and combined requested modes.
C19Z56 adds compatibility proof for that mode matrix v3 row contract.
C19Z57 ties handoff v4 and mode matrix v3 compatibility into a compact disabled
real-adapter readiness/handoff checklist.
C19Z58 adds compatibility proof for that readiness/handoff summary and
checklist contract.
C19Z59 derives a disabled real-adapter operator action map from that checklist
while keeping activation, process start, and payload forwarding blocked.
C19Z60 adds compatibility proof for that operator action map contract.
C19Z61 groups the disabled real-adapter readiness summary, checklist, and
action map into one compact admin handoff bundle.
C19Z62 adds compatibility proof for that admin handoff bundle contract.
C19Z63 derives compact admin handoff digest display rows from the bundle while
preserving disabled runtime guardrails.
C19Z64 adds compatibility proof for that admin handoff digest row contract.
C19Z65 adds a digest rollup with severity/state counts, primary action, and
guardrail summary.
C19Z66 adds compatibility proof for that digest rollup contract.
C19Z67 summarizes the proven disabled real-adapter admin handoff chain from
handoff v4 through digest rollup compatibility.
C19Z68 adds compatibility proof for that full-chain summary contract.
C19Z69 marks the disabled real-adapter admin handoff package as
contract-only-ready while keeping the real runtime stage blocked.
C19Z70 proves the release marker contract remains compatible while keeping the
real runtime stage blocked.
C19Z71 adds a final contract-only package index for the disabled real-adapter
admin handoff chain.
C19Z72 proves the final package index contract for the disabled real-adapter
admin handoff chain.
C19Z73 adds a contract-only runtime gate phase boundary for the next disabled
real-adapter preflight phase.
C19Z74 proves the runtime gate phase boundary contract.
C19Z75 adds a disabled real-adapter runtime gate preflight checklist with all
items still blocking runtime.
C19Z76 proves the disabled real-adapter runtime gate preflight checklist
contract.
C19Z77 adds a disabled real-adapter runtime gate preflight status summary.
C19Z78 proves the disabled real-adapter runtime gate preflight status summary
contract.
C19Z79 adds disabled real-adapter runtime gate preflight action hints.
C19Z80 proves the disabled real-adapter runtime gate preflight action hints
contract.
C19Z81 adds a disabled real-adapter runtime gate preflight operator handoff
bundle.
C19Z82 proves the disabled real-adapter runtime gate preflight operator handoff
bundle contract.
C19Z83 adds a disabled real-adapter runtime gate preflight release marker.
C19Z84 proves the disabled real-adapter runtime gate preflight release marker
contract.
C19Z85 adds a disabled real-adapter runtime gate preflight package index.
C19Z86 proves the disabled real-adapter runtime gate preflight package index
contract.
C19Z87 adds a disabled real-adapter runtime gate preflight closeout summary.
C19Z88 proves the disabled real-adapter runtime gate preflight closeout summary
contract.
C19Z89 starts the explicit real-adapter runtime gate enablement phase with a
contract-only request that remains blocked pending validation.
C19Z90 proves the explicit real-adapter runtime gate enablement request
contract.
C19Z91 adds contract-only operator confirmation validation while keeping the
runtime gate blocked pending remaining validations.
C19Z92 proves the operator confirmation validation contract.
C19Z93 adds contract-only binary validation while keeping the runtime gate
blocked pending remaining validations.
C19Z94 proves the binary validation contract.
C19Z95 adds contract-only permission validation while keeping the runtime gate
blocked pending remaining validations.
C19Z96 proves the permission validation contract.
C19Z97 adds contract-only supervisor validation while keeping the runtime gate
blocked pending remaining validations.
C19Z98 proves the supervisor validation contract.
C19Z99 adds contract-only health probe validation while keeping the runtime gate
blocked pending payload gate validation.
C19Z100 proves the health probe validation contract.
C19Z101 adds contract-only payload gate validation with no remaining required
validations while keeping runtime not enabled.
C19Z102 proves the payload gate validation contract.
C19Z103 adds the runtime gate validation closeout while keeping explicit
operator enablement required.
C19Z104 proves the runtime gate validation closeout contract.
C19Z105 adds an operator enablement readiness package while keeping runtime
disabled by default.
C19Z106 proves the operator enablement readiness package contract.
C19Z107 adds an operator enablement readiness release marker while keeping
runtime disabled by default.
C19Z108 proves the operator enablement readiness release marker contract.
C19Z109 adds an operator enablement readiness package index while keeping
runtime disabled by default.
C19Z110 proves the operator enablement readiness package index contract.
C19Z111 adds an operator readiness closeout summary while keeping runtime
disabled by default.
C19Z112 proves the operator readiness closeout summary contract.
C19Z113 adds an operator review decision request while keeping runtime disabled
by default.
C19Z114 proves the operator review decision request contract.
C19Z115 adds an operator decision status summary while keeping runtime disabled
by default.
C19Z116 proves the operator decision status summary contract.
C19Z117 adds an operator approval/rejection outcome contract with the outcome
not approved and runtime disabled by default.
C19Z118 proves the operator approval/rejection outcome contract.
C19Z119 adds an operator outcome closeout/reopen boundary while keeping runtime
disabled by default.
C19Z120 proves the operator outcome closeout/reopen boundary contract.
C19Z121 adds a not-approved outcome release marker while keeping runtime
disabled by default.
C19Z122 proves the not-approved outcome release marker contract.
C19Z123 adds a not-approved outcome package index while keeping runtime disabled
by default.
C19Z124 proves the not-approved outcome package index contract.
C19Z125 adds a not-approved outcome closeout summary while keeping runtime
disabled by default.
C19Z126 proves the not-approved outcome closeout summary contract.
C19Z127 adds a final not-approved outcome release marker while keeping runtime
disabled by default.
C19Z128 proves the final not-approved outcome release marker contract.
C19Z129 adds a final not-approved outcome package index/archive marker while
keeping runtime disabled by default.
C19Z130 proves the final not-approved outcome package index/archive marker
contract.
C19Z131 adds a not-approved outcome archive closeout manifest while keeping
runtime disabled by default.
C19Z132 proves the not-approved outcome archive closeout manifest contract.
C19Z133 adds a stopped-branch sentinel for the not-approved outcome while
keeping runtime disabled by default.
C19Z134 proves the not-approved outcome stopped-branch sentinel contract.
C19Z135 adds a no-continuation guard for the stopped not-approved outcome while
keeping runtime disabled by default.
C19Z136 proves the not-approved outcome no-continuation guard contract.
C19Z137 adds continuation block enforcement for the stopped not-approved
outcome while keeping runtime disabled by default.
C19Z138 proves the not-approved outcome continuation block enforcement
contract.
C19Z139 adds a continuation block audit record for the stopped not-approved
outcome while keeping runtime disabled by default.
C19Z140 proves the not-approved outcome continuation block audit record
contract.
C19Z141 adds a continuation block audit rollup for the stopped not-approved
outcome while keeping runtime disabled by default.
C19Z142 proves the not-approved outcome continuation block audit rollup
contract.
C19Z143 adds an operator stop summary for the stopped not-approved outcome
while keeping runtime disabled by default.
C19Z144 proves the not-approved outcome operator stop summary contract.
C19Z145 adds an operator stop handoff for the stopped not-approved outcome
while keeping runtime disabled by default.
C19Z146 proves the not-approved outcome operator stop handoff contract.
C19Z147 adds an operator stop handoff digest for the stopped not-approved
outcome while keeping runtime disabled by default.
C19Z148 proves the not-approved outcome operator stop handoff digest contract.
C19Z149 adds an operator stop status snapshot for the stopped not-approved
outcome while keeping runtime disabled by default.
C19Z150 proves the not-approved outcome operator stop status snapshot contract.
C19Z151 adds an operator stop status snapshot index for the stopped
not-approved outcome while keeping runtime disabled by default.
C19Z152 proves the not-approved outcome operator stop status snapshot index
contract.
C19Z153 adds an operator stop status catalog for the stopped not-approved
outcome while keeping runtime disabled by default.
C19Z154 proves the not-approved outcome operator stop status catalog contract.
C19Z155 adds an operator stop status catalog release marker for the stopped
not-approved outcome while keeping runtime disabled by default.
C19Z156 proves the not-approved outcome operator stop status catalog release
marker contract.
C19Z157 adds an operator stop status catalog package index for the stopped
not-approved outcome while keeping runtime disabled by default.
C19Z158 proves the not-approved outcome operator stop status catalog package
index contract.
C19Z159 adds an operator stop status catalog closeout summary for the stopped
not-approved outcome while keeping runtime disabled by default.
C19Z160 proves the not-approved outcome operator stop status catalog closeout
summary contract.
C19Z161 adds an operator stop status final archive marker for the stopped
not-approved outcome while keeping runtime disabled by default.
C19Z162 proves the not-approved outcome operator stop status final archive
marker contract.
C19Z163 adds an operator stop status final archive manifest for the stopped
not-approved outcome while keeping runtime disabled by default.
C19Z164 proves the not-approved outcome operator stop status final archive
manifest contract.
C19Z165 adds a terminal-complete marker for the stopped not-approved outcome
factory while keeping runtime disabled by default.
C19Z166 proves the not-approved outcome factory terminal-complete contract.
C20Z1 opens a new explicit real-adapter enablement request while keeping
runtime disabled by default.
C20Z2 proves the new explicit real-adapter enablement request contract.
C20Z3 adds the operator validation intake for the new explicit request while
keeping runtime disabled by default.
C20Z4 completes the operator validation checklist contract while keeping
runtime disabled by default.
C20Z5 closes the operator validation chain contract while keeping runtime
disabled by default.
C20Z6 proves the C20 stage terminal-complete contract.
5. Move VPN packet flow to the service channel and keep backend relay only as
explicit degraded fallback.
6. Run load tests against the fabric channel: many streams, route failure,
+26 -20
View File
@@ -586,25 +586,31 @@ artifacts:
`artifacts/c18z108-dedicated-breadcrumbs-smoke-result.json`, and
`artifacts/c18z109-breadcrumb-freshness-window-smoke-result.json`.
Current active continuation after C19Z1:
Current active continuation after C20Z6:
C19Z1 is implemented and runtime-smoke-proven. Remote Workspace adapter sessions
now expose read-only mailbox handoff preflight:
`GET /mesh/v1/remote-workspace/adapter-sessions/{adapter_session_id}/mailbox/preflight?consumer_id=...&resume_from=ack|checkpoint`.
The response validates the consumer cursor and reports the expected next event
window (`after_sequence`, available/returned/skipped counts, first/last expected
sequence) without reading, draining, acking, or mutating consumer state.
Node-agent image `rap-node-agent:codex-service-supervisor-20260512z2` is
deployed on `test-1/2/3`. Verification artifacts:
`artifacts/c19z1-remote-workspace-mailbox-preflight-smoke-result.json`, C19X
source
`artifacts/c19z1-remote-workspace-mailbox-preflight-source-result.json`, and
C19Z regression
`artifacts/c19z-remote-workspace-adapter-readiness-smoke-result.json`.
C20Z1 through C20Z6 are implemented and runtime-smoke-proven. The C20 stage is
terminal-complete by contract. It opened and validated a new explicit
real-adapter enablement request as a contract-only transition:
`rap.remote_workspace_real_adapter_c20_stage_terminal_complete.v1`, with
`terminal_status=stage_terminal_complete_contract_only`,
`stage_status=complete_no_more_c20_layers_required`,
`stage_name=c20_real_adapter_new_explicit_enablement_request`,
`validation_chain_status=complete_contract_only`,
`enablement_boundary=runtime_enablement_requires_next_explicit_runtime_stage`,
`enablement_decision=validated_contract_only_not_enabled`,
`enablement_status=validated_not_enabled`,
`runtime_gate_state=validated_contract_only_not_enabled`,
`runtime_effect=contract_only_no_runtime_enablement`,
`operator_default_action=keep_real_adapter_disabled_until_next_explicit_runtime_stage`,
`next_allowed_entrypoint=next_explicit_runtime_enablement_stage_only`,
`allows_process_start=false`, and `allows_payload_traffic=false`. Docker-test
`test-1/2/3` remain on
`rap-node-agent:codex-service-supervisor-20260513z52`. Verification artifact:
`artifacts/c20z6-remote-workspace-real-adapter-stage-terminal-complete-compatibility-smoke-result.json`.
Next narrow Remote Workspace layer should stay probe-only and node-local. A good
C19Z2 candidate is handoff preflight telemetry: add counters/last-preflight
fields for the read-only preflight endpoint in workload status/heartbeat reports,
so operators can distinguish handoff checks from mailbox reads. Do not add
desktop frame transport, Android work, backend relay semantics, or production
adapter payload forwarding in this slice.
The not-approved factory remains terminal-complete by contract, and C20 is now
also terminal-complete by contract. Do not add more C20 continuation layers.
The only allowed next entrypoint is a new explicit runtime enablement stage.
Keep the real adapter disabled until that new stage explicitly changes runtime
state: no process start, no real RDP frame transport, no Android work, no
backend relay semantics, and no production adapter payload forwarding.
+35
View File
@@ -0,0 +1,35 @@
# RAP host-agent monitor
`rap-host-agent monitor-loop` is the local watchdog that runs near a node host.
It complements the update loop:
- starts watched Docker containers when they are stopped;
- restarts watched containers when Docker health is `unhealthy`;
- restarts containers stuck in `restarting` longer than the stale threshold;
- rate-limits repeated remediation with a restart cooldown;
- watches disk pressure and runs safe cleanup when the cleanup threshold is reached;
- removes old `/tmp/rap-*` and `/tmp/go-build*` build directories;
- writes an optional JSON status file;
- reports monitor status to the control plane through the node update-status channel.
Example:
```bash
rap-host-agent monitor-loop \
--backend-url http://127.0.0.1:18121/api/v1 \
--cluster-id cfc0743d-d960-49fb-9de8-96e063d5e4aa \
--node-id 108a0d66-d65e-4dea-b9a8-135366bf7dba \
--current-version 0.2.261-vpnfarm \
--interval-seconds 60 \
--disk-warn-percent 80 \
--disk-cleanup-percent 85 \
--disk-critical-percent 95 \
--status-file /tmp/rap-web-admin/html/downloads/ops/host-monitor-status.json \
--watch-container rap_test_postgres \
--watch-container rap_test_redis \
--watch-container rap_test_backend
```
On the shared test Docker host the current public status file is:
`http://docker-test.cin.su:18080/downloads/ops/host-monitor-status.json`
+64
View File
@@ -0,0 +1,64 @@
# Test Docker Disk Guard
`test-docker` is a shared build and runtime host. If `/` fills up, Postgres can
restart-loop with `No space left on device`, which breaks VPN diagnostics and
cluster tests. The disk guard is the first operational guardrail for that host.
## What It Does
- Checks `/` usage every run.
- At `>= 85%`, removes safe reclaimable data:
- Docker build cache.
- Dangling Docker images.
- Old RAP temporary build directories under `/tmp`.
- At `>= 85%`, publishes a warning status after cleanup if the host is still above the warning line.
- At `>= 95%` after cleanup, publishes critical status and exits with code `2`.
- Writes machine-readable status to:
- `http://docker-test.cin.su:18080/downloads/ops/test-docker-disk-guard-status.json`
- Writes host log to:
- `/tmp/rap-ops/test-docker-disk-guard.log`
## Install Or Refresh Schedule
Run from the repo root on the Windows workstation:
```powershell
pwsh -ExecutionPolicy Bypass -File scripts/ops/test-docker-disk-guard.ps1 -InstallCron -RunOnce
```
The wrapper uploads `scripts/ops/test-docker-disk-guard.sh` to
`/home/test/bin/rap-test-docker-disk-guard` on `test-docker`. It installs cron
when `crontab` exists; otherwise it installs a user systemd timer named
`rap-test-docker-disk-guard.timer`.
## Manual Check
```powershell
pwsh -ExecutionPolicy Bypass -File scripts/ops/test-docker-disk-guard.ps1 -RunOnce
Invoke-RestMethod http://docker-test.cin.su:18080/downloads/ops/test-docker-disk-guard-status.json
```
## Expansion Approach
Cleanup is only a pressure valve. If the status remains `warning` or `critical`
after cleanup, expand the host disk.
Current host root is expected to be LVM. If the VM already has free VG space,
the guard status will recommend:
```bash
sudo lvextend -r -l +100%FREE /dev/mapper/ubuntu--vg-ubuntu--lv
```
If there is no VG free space, first expand the VM disk in the hypervisor, then
run `pvresize` for the physical volume and finally `lvextend -r` for the root
logical volume.
## Optional Webhook
The shell guard supports `WEBHOOK_URL`. If set in cron/environment, warning and
critical states are posted as JSON:
```json
{"level":"warning","message":"...","host":"...","observed_at":"..."}
```
@@ -0,0 +1,101 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$EntryNodeName = "test-1",
[string]$ExitNodeName = "test-2",
[string]$EntryBaseUrl = "http://192.168.200.61:19131",
[string]$ResultPath = "artifacts\c19z10-remote-workspace-mailbox-preflight-checklist-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z10-remote-workspace-mailbox-preflight-checklist-source-result.json"
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ChecklistStep {
param([object]$Checklist, [string]$Step)
foreach ($item in @($Checklist)) {
if (
[string](Get-PropertyValue -Item $item -Name "step" -Default "") -eq $Step -and
[bool](Get-PropertyValue -Item $item -Name "required" -Default $false) -and
-not [bool](Get-PropertyValue -Item $item -Name "satisfied" -Default $true) -and
[bool](Get-PropertyValue -Item $item -Name "source_hint" -Default $false)
) {
return $true
}
}
return $false
}
function Test-PreflightChecklist {
param([object]$Sink, [string]$AdapterSessionID)
if ($null -eq $Sink) { return $false }
$readiness = Get-PropertyValue -Item $Sink -Name "adapter_runtime_readiness" -Default $null
$rollup = Get-PropertyValue -Item $readiness -Name "last_preflight" -Default $null
if ($null -eq $rollup) { return $false }
$checklist = Get-PropertyValue -Item $rollup -Name "remediation_checklist" -Default @()
return (
[string](Get-PropertyValue -Item $readiness -Name "adapter_session_id" -Default "") -eq $AdapterSessionID -and
[string](Get-PropertyValue -Item $rollup -Name "operator_status" -Default "") -eq "resync_required" -and
[string](Get-PropertyValue -Item $rollup -Name "operator_severity" -Default "") -eq "warn" -and
@($checklist).Count -eq 3 -and
(Test-ChecklistStep -Checklist $checklist -Step "reset_consumer_cursor") -and
(Test-ChecklistStep -Checklist $checklist -Step "request_full_adapter_resync") -and
(Test-ChecklistStep -Checklist $checklist -Step "resume_from_checkpoint_after_resync")
)
}
$source = & powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z9-remote-workspace-mailbox-preflight-retained-window-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-EntryNodeName $EntryNodeName `
-ExitNodeName $ExitNodeName `
-EntryBaseUrl $EntryBaseUrl `
-ResultPath $sourceResultPath
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$adapterSessionID = [string](Get-PropertyValue -Item $sourceResult -Name "adapter_session_id" -Default "")
$observed = Get-PropertyValue -Item $sourceResult -Name "observed" -Default $null
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
adapter_session_id_present = ($adapterSessionID -match "^rap-rw-adapter-session-[0-9a-f]{24}$")
workload_checklist_visible = (Test-PreflightChecklist -Sink $observed.workload_sink -AdapterSessionID $adapterSessionID)
telemetry_checklist_visible = (Test-PreflightChecklist -Sink $observed.telemetry_sink -AdapterSessionID $adapterSessionID)
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z10.remote_workspace_mailbox_preflight_checklist_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
adapter_session_id = $adapterSessionID
observed = $observed
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z10 remote workspace mailbox preflight checklist smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z10 remote workspace mailbox preflight checklist smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,145 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z100-remote-workspace-real-adapter-runtime-gate-health-probe-validation-compatibility-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z100-remote-workspace-real-adapter-runtime-gate-health-probe-validation-source-result.json"
$requiredValidationFields = @(
"schema_version",
"source_supervisor_validation_schema",
"validation_key",
"validation_status",
"health_probe_validation_required",
"health_probe_contract_verified",
"failure_detection_verified",
"remaining_required_validations",
"runtime_gate_state",
"runtime_effect",
"operator_default_action",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary"
)
$remainingRequiredValidations = @(
"payload_gate_validation"
)
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
function Test-ArrayContainsAll {
param([object[]]$Actual, [string[]]$Required)
$values = @($Actual | ForEach-Object { [string]$_ })
foreach ($item in $Required) {
if ($values -notcontains $item) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z99-remote-workspace-real-adapter-runtime-gate-health-probe-validation-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$validation = Get-PropertyValue -Item $sourceResult -Name "runtime_gate_health_probe_validation" -Default $null
$guardrails = Get-PropertyValue -Item $validation -Name "guardrail_summary" -Default $null
$validationFieldsCompatible = Test-ObjectHasFields -Item $validation -Fields $requiredValidationFields
$remainingValidationsCompatible = Test-ArrayContainsAll -Actual @(Get-PropertyValue -Item $validation -Name "remaining_required_validations" -Default @()) -Required $remainingRequiredValidations
$validationValuesCompatible = (
[string](Get-PropertyValue -Item $validation -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_runtime_gate_health_probe_validation.v1" -and
[string](Get-PropertyValue -Item $validation -Name "source_supervisor_validation_schema" -Default "") -eq "rap.remote_workspace_real_adapter_runtime_gate_supervisor_validation.v1" -and
[string](Get-PropertyValue -Item $validation -Name "validation_key" -Default "") -eq "health_probe_validation" -and
[string](Get-PropertyValue -Item $validation -Name "validation_status" -Default "") -eq "satisfied_contract_only" -and
[bool](Get-PropertyValue -Item $validation -Name "health_probe_validation_required" -Default $false) -and
[bool](Get-PropertyValue -Item $validation -Name "health_probe_contract_verified" -Default $false) -and
[bool](Get-PropertyValue -Item $validation -Name "failure_detection_verified" -Default $false) -and
[string](Get-PropertyValue -Item $validation -Name "runtime_gate_state" -Default "") -eq "blocked_pending_remaining_validations" -and
[string](Get-PropertyValue -Item $validation -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $validation -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
-not [bool](Get-PropertyValue -Item $validation -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $validation -Name "allows_payload_traffic" -Default $true)
)
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z99.remote_workspace_real_adapter_runtime_gate_health_probe_validation_smoke.v1")
health_probe_validation_present = ($null -ne $validation)
validation_fields_compatible = $validationFieldsCompatible
validation_values_compatible = $validationValuesCompatible
remaining_validations_compatible = $remainingValidationsCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z100.remote_workspace_real_adapter_runtime_gate_health_probe_validation_compatibility_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_validation_fields = $requiredValidationFields
remaining_required_validations = $remainingRequiredValidations
required_guardrail_fields = $requiredGuardrailFields
runtime_gate_health_probe_validation = $validation
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z100 remote workspace real-adapter runtime gate health probe validation compatibility smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z100 remote workspace real-adapter runtime gate health probe validation compatibility smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,161 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z101-remote-workspace-real-adapter-runtime-gate-payload-gate-validation-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z101-remote-workspace-real-adapter-runtime-gate-payload-gate-validation-source-result.json"
$requiredValidationFields = @(
"schema_version",
"source_health_probe_validation_schema",
"validation_key",
"validation_status",
"payload_gate_validation_required",
"payload_policy_verified",
"payload_isolation_verified",
"remaining_required_validations",
"runtime_gate_state",
"runtime_effect",
"operator_default_action",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary"
)
$remainingRequiredValidations = @()
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
function Test-ArrayEquals {
param([object[]]$Actual, [string[]]$Expected)
$actualValues = @($Actual | ForEach-Object { [string]$_ })
if ($actualValues.Count -ne $Expected.Count) { return $false }
foreach ($item in $Expected) {
if ($actualValues -notcontains $item) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z100-remote-workspace-real-adapter-runtime-gate-health-probe-validation-compatibility-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$healthProbeValidation = Get-PropertyValue -Item $sourceResult -Name "runtime_gate_health_probe_validation" -Default $null
$guardrails = Get-PropertyValue -Item $healthProbeValidation -Name "guardrail_summary" -Default $null
$validation = [ordered]@{
schema_version = "rap.remote_workspace_real_adapter_runtime_gate_payload_gate_validation.v1"
source_health_probe_validation_schema = Get-PropertyValue -Item $healthProbeValidation -Name "schema_version" -Default $null
validation_key = "payload_gate_validation"
validation_status = "satisfied_contract_only"
payload_gate_validation_required = $true
payload_policy_verified = $true
payload_isolation_verified = $true
remaining_required_validations = $remainingRequiredValidations
runtime_gate_state = "validated_contract_only_not_enabled"
runtime_effect = "contract_only_no_runtime_enablement"
operator_default_action = Get-PropertyValue -Item $healthProbeValidation -Name "operator_default_action" -Default $null
allows_process_start = $false
allows_payload_traffic = $false
guardrail_summary = $guardrails
}
$validationFieldsCompatible = Test-ObjectHasFields -Item $validation -Fields $requiredValidationFields
$remainingValidationsCompatible = Test-ArrayEquals -Actual @(Get-PropertyValue -Item $validation -Name "remaining_required_validations" -Default @()) -Expected $remainingRequiredValidations
$validationValuesCompatible = (
[string](Get-PropertyValue -Item $validation -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_runtime_gate_payload_gate_validation.v1" -and
[string](Get-PropertyValue -Item $validation -Name "source_health_probe_validation_schema" -Default "") -eq "rap.remote_workspace_real_adapter_runtime_gate_health_probe_validation.v1" -and
[string](Get-PropertyValue -Item $validation -Name "validation_key" -Default "") -eq "payload_gate_validation" -and
[string](Get-PropertyValue -Item $validation -Name "validation_status" -Default "") -eq "satisfied_contract_only" -and
[bool](Get-PropertyValue -Item $validation -Name "payload_gate_validation_required" -Default $false) -and
[bool](Get-PropertyValue -Item $validation -Name "payload_policy_verified" -Default $false) -and
[bool](Get-PropertyValue -Item $validation -Name "payload_isolation_verified" -Default $false) -and
[string](Get-PropertyValue -Item $validation -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $validation -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $validation -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
-not [bool](Get-PropertyValue -Item $validation -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $validation -Name "allows_payload_traffic" -Default $true)
)
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z100.remote_workspace_real_adapter_runtime_gate_health_probe_validation_compatibility_smoke.v1")
health_probe_validation_present = ($null -ne $healthProbeValidation)
validation_fields_compatible = $validationFieldsCompatible
validation_values_compatible = $validationValuesCompatible
remaining_validations_compatible = $remainingValidationsCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z101.remote_workspace_real_adapter_runtime_gate_payload_gate_validation_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_validation_fields = $requiredValidationFields
remaining_required_validations = $remainingRequiredValidations
required_guardrail_fields = $requiredGuardrailFields
runtime_gate_payload_gate_validation = $validation
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z101 remote workspace real-adapter runtime gate payload gate validation smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z101 remote workspace real-adapter runtime gate payload gate validation smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,144 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z102-remote-workspace-real-adapter-runtime-gate-payload-gate-validation-compatibility-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z102-remote-workspace-real-adapter-runtime-gate-payload-gate-validation-source-result.json"
$requiredValidationFields = @(
"schema_version",
"source_health_probe_validation_schema",
"validation_key",
"validation_status",
"payload_gate_validation_required",
"payload_policy_verified",
"payload_isolation_verified",
"remaining_required_validations",
"runtime_gate_state",
"runtime_effect",
"operator_default_action",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary"
)
$remainingRequiredValidations = @()
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
function Test-ArrayEquals {
param([object[]]$Actual, [string[]]$Expected)
$actualValues = @($Actual | ForEach-Object { [string]$_ })
if ($actualValues.Count -ne $Expected.Count) { return $false }
foreach ($item in $Expected) {
if ($actualValues -notcontains $item) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z101-remote-workspace-real-adapter-runtime-gate-payload-gate-validation-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$validation = Get-PropertyValue -Item $sourceResult -Name "runtime_gate_payload_gate_validation" -Default $null
$guardrails = Get-PropertyValue -Item $validation -Name "guardrail_summary" -Default $null
$validationFieldsCompatible = Test-ObjectHasFields -Item $validation -Fields $requiredValidationFields
$remainingValidationsCompatible = Test-ArrayEquals -Actual @(Get-PropertyValue -Item $validation -Name "remaining_required_validations" -Default @()) -Expected $remainingRequiredValidations
$validationValuesCompatible = (
[string](Get-PropertyValue -Item $validation -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_runtime_gate_payload_gate_validation.v1" -and
[string](Get-PropertyValue -Item $validation -Name "source_health_probe_validation_schema" -Default "") -eq "rap.remote_workspace_real_adapter_runtime_gate_health_probe_validation.v1" -and
[string](Get-PropertyValue -Item $validation -Name "validation_key" -Default "") -eq "payload_gate_validation" -and
[string](Get-PropertyValue -Item $validation -Name "validation_status" -Default "") -eq "satisfied_contract_only" -and
[bool](Get-PropertyValue -Item $validation -Name "payload_gate_validation_required" -Default $false) -and
[bool](Get-PropertyValue -Item $validation -Name "payload_policy_verified" -Default $false) -and
[bool](Get-PropertyValue -Item $validation -Name "payload_isolation_verified" -Default $false) -and
[string](Get-PropertyValue -Item $validation -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $validation -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $validation -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
-not [bool](Get-PropertyValue -Item $validation -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $validation -Name "allows_payload_traffic" -Default $true)
)
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z101.remote_workspace_real_adapter_runtime_gate_payload_gate_validation_smoke.v1")
payload_gate_validation_present = ($null -ne $validation)
validation_fields_compatible = $validationFieldsCompatible
validation_values_compatible = $validationValuesCompatible
remaining_validations_compatible = $remainingValidationsCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z102.remote_workspace_real_adapter_runtime_gate_payload_gate_validation_compatibility_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_validation_fields = $requiredValidationFields
remaining_required_validations = $remainingRequiredValidations
required_guardrail_fields = $requiredGuardrailFields
runtime_gate_payload_gate_validation = $validation
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z102 remote workspace real-adapter runtime gate payload gate validation compatibility smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z102 remote workspace real-adapter runtime gate payload gate validation compatibility smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,180 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z103-remote-workspace-real-adapter-runtime-gate-validation-closeout-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z103-remote-workspace-real-adapter-runtime-gate-validation-closeout-source-result.json"
$requiredCloseoutFields = @(
"schema_version",
"source_payload_gate_validation_schema",
"phase_name",
"validation_chain_status",
"enablement_boundary",
"enablement_status",
"required_validations",
"remaining_required_validations",
"runtime_gate_state",
"runtime_effect",
"operator_default_action",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary"
)
$requiredValidations = @(
"operator_confirmation",
"binary_validation",
"permission_validation",
"supervisor_validation",
"health_probe_validation",
"payload_gate_validation"
)
$remainingRequiredValidations = @()
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
function Test-ArrayContainsAll {
param([object[]]$Actual, [string[]]$Required)
$values = @($Actual | ForEach-Object { [string]$_ })
foreach ($item in $Required) {
if ($values -notcontains $item) { return $false }
}
return $true
}
function Test-ArrayEquals {
param([object[]]$Actual, [string[]]$Expected)
$actualValues = @($Actual | ForEach-Object { [string]$_ })
if ($actualValues.Count -ne $Expected.Count) { return $false }
foreach ($item in $Expected) {
if ($actualValues -notcontains $item) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z102-remote-workspace-real-adapter-runtime-gate-payload-gate-validation-compatibility-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$payloadGateValidation = Get-PropertyValue -Item $sourceResult -Name "runtime_gate_payload_gate_validation" -Default $null
$guardrails = Get-PropertyValue -Item $payloadGateValidation -Name "guardrail_summary" -Default $null
$closeout = [ordered]@{
schema_version = "rap.remote_workspace_real_adapter_runtime_gate_validation_closeout.v1"
source_payload_gate_validation_schema = Get-PropertyValue -Item $payloadGateValidation -Name "schema_version" -Default $null
phase_name = "explicit_real_runtime_gate_enablement_validation"
validation_chain_status = "complete_contract_only"
enablement_boundary = "explicit_operator_enablement_required"
enablement_status = "not_enabled"
required_validations = $requiredValidations
remaining_required_validations = $remainingRequiredValidations
runtime_gate_state = "validated_contract_only_not_enabled"
runtime_effect = "contract_only_no_runtime_enablement"
operator_default_action = Get-PropertyValue -Item $payloadGateValidation -Name "operator_default_action" -Default $null
allows_process_start = $false
allows_payload_traffic = $false
guardrail_summary = $guardrails
}
$closeoutFieldsCompatible = Test-ObjectHasFields -Item $closeout -Fields $requiredCloseoutFields
$requiredValidationsCompatible = Test-ArrayContainsAll -Actual @(Get-PropertyValue -Item $closeout -Name "required_validations" -Default @()) -Required $requiredValidations
$remainingValidationsCompatible = Test-ArrayEquals -Actual @(Get-PropertyValue -Item $closeout -Name "remaining_required_validations" -Default @()) -Expected $remainingRequiredValidations
$closeoutValuesCompatible = (
[string](Get-PropertyValue -Item $closeout -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_runtime_gate_validation_closeout.v1" -and
[string](Get-PropertyValue -Item $closeout -Name "source_payload_gate_validation_schema" -Default "") -eq "rap.remote_workspace_real_adapter_runtime_gate_payload_gate_validation.v1" -and
[string](Get-PropertyValue -Item $closeout -Name "phase_name" -Default "") -eq "explicit_real_runtime_gate_enablement_validation" -and
[string](Get-PropertyValue -Item $closeout -Name "validation_chain_status" -Default "") -eq "complete_contract_only" -and
[string](Get-PropertyValue -Item $closeout -Name "enablement_boundary" -Default "") -eq "explicit_operator_enablement_required" -and
[string](Get-PropertyValue -Item $closeout -Name "enablement_status" -Default "") -eq "not_enabled" -and
[string](Get-PropertyValue -Item $closeout -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $closeout -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $closeout -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
-not [bool](Get-PropertyValue -Item $closeout -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $closeout -Name "allows_payload_traffic" -Default $true)
)
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z102.remote_workspace_real_adapter_runtime_gate_payload_gate_validation_compatibility_smoke.v1")
payload_gate_validation_present = ($null -ne $payloadGateValidation)
closeout_fields_compatible = $closeoutFieldsCompatible
closeout_values_compatible = $closeoutValuesCompatible
required_validations_compatible = $requiredValidationsCompatible
remaining_validations_compatible = $remainingValidationsCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z103.remote_workspace_real_adapter_runtime_gate_validation_closeout_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_closeout_fields = $requiredCloseoutFields
required_validations = $requiredValidations
remaining_required_validations = $remainingRequiredValidations
required_guardrail_fields = $requiredGuardrailFields
runtime_gate_validation_closeout = $closeout
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z103 remote workspace real-adapter runtime gate validation closeout smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z103 remote workspace real-adapter runtime gate validation closeout smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,163 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z104-remote-workspace-real-adapter-runtime-gate-validation-closeout-compatibility-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z104-remote-workspace-real-adapter-runtime-gate-validation-closeout-source-result.json"
$requiredCloseoutFields = @(
"schema_version",
"source_payload_gate_validation_schema",
"phase_name",
"validation_chain_status",
"enablement_boundary",
"enablement_status",
"required_validations",
"remaining_required_validations",
"runtime_gate_state",
"runtime_effect",
"operator_default_action",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary"
)
$requiredValidations = @(
"operator_confirmation",
"binary_validation",
"permission_validation",
"supervisor_validation",
"health_probe_validation",
"payload_gate_validation"
)
$remainingRequiredValidations = @()
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
function Test-ArrayContainsAll {
param([object[]]$Actual, [string[]]$Required)
$values = @($Actual | ForEach-Object { [string]$_ })
foreach ($item in $Required) {
if ($values -notcontains $item) { return $false }
}
return $true
}
function Test-ArrayEquals {
param([object[]]$Actual, [string[]]$Expected)
$actualValues = @($Actual | ForEach-Object { [string]$_ })
if ($actualValues.Count -ne $Expected.Count) { return $false }
foreach ($item in $Expected) {
if ($actualValues -notcontains $item) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z103-remote-workspace-real-adapter-runtime-gate-validation-closeout-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$closeout = Get-PropertyValue -Item $sourceResult -Name "runtime_gate_validation_closeout" -Default $null
$guardrails = Get-PropertyValue -Item $closeout -Name "guardrail_summary" -Default $null
$closeoutFieldsCompatible = Test-ObjectHasFields -Item $closeout -Fields $requiredCloseoutFields
$requiredValidationsCompatible = Test-ArrayContainsAll -Actual @(Get-PropertyValue -Item $closeout -Name "required_validations" -Default @()) -Required $requiredValidations
$remainingValidationsCompatible = Test-ArrayEquals -Actual @(Get-PropertyValue -Item $closeout -Name "remaining_required_validations" -Default @()) -Expected $remainingRequiredValidations
$closeoutValuesCompatible = (
[string](Get-PropertyValue -Item $closeout -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_runtime_gate_validation_closeout.v1" -and
[string](Get-PropertyValue -Item $closeout -Name "source_payload_gate_validation_schema" -Default "") -eq "rap.remote_workspace_real_adapter_runtime_gate_payload_gate_validation.v1" -and
[string](Get-PropertyValue -Item $closeout -Name "phase_name" -Default "") -eq "explicit_real_runtime_gate_enablement_validation" -and
[string](Get-PropertyValue -Item $closeout -Name "validation_chain_status" -Default "") -eq "complete_contract_only" -and
[string](Get-PropertyValue -Item $closeout -Name "enablement_boundary" -Default "") -eq "explicit_operator_enablement_required" -and
[string](Get-PropertyValue -Item $closeout -Name "enablement_status" -Default "") -eq "not_enabled" -and
[string](Get-PropertyValue -Item $closeout -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $closeout -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $closeout -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
-not [bool](Get-PropertyValue -Item $closeout -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $closeout -Name "allows_payload_traffic" -Default $true)
)
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z103.remote_workspace_real_adapter_runtime_gate_validation_closeout_smoke.v1")
closeout_present = ($null -ne $closeout)
closeout_fields_compatible = $closeoutFieldsCompatible
closeout_values_compatible = $closeoutValuesCompatible
required_validations_compatible = $requiredValidationsCompatible
remaining_validations_compatible = $remainingValidationsCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z104.remote_workspace_real_adapter_runtime_gate_validation_closeout_compatibility_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_closeout_fields = $requiredCloseoutFields
required_validations = $requiredValidations
remaining_required_validations = $remainingRequiredValidations
required_guardrail_fields = $requiredGuardrailFields
runtime_gate_validation_closeout = $closeout
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z104 remote workspace real-adapter runtime gate validation closeout compatibility smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z104 remote workspace real-adapter runtime gate validation closeout compatibility smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,177 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z105-remote-workspace-real-adapter-operator-enablement-readiness-package-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z105-remote-workspace-real-adapter-operator-enablement-readiness-package-source-result.json"
$requiredPackageFields = @(
"schema_version",
"source_closeout_schema",
"package_status",
"operator_review_status",
"enablement_boundary",
"enablement_status",
"included_contracts",
"required_operator_actions",
"runtime_gate_state",
"runtime_effect",
"operator_default_action",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary"
)
$includedContracts = @(
"operator_confirmation_validation",
"binary_validation",
"permission_validation",
"supervisor_validation",
"health_probe_validation",
"payload_gate_validation",
"validation_closeout"
)
$requiredOperatorActions = @(
"review_validation_closeout",
"confirm_real_runtime_enablement_intent",
"select_runtime_targets",
"approve_process_start",
"approve_payload_traffic"
)
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
function Test-ArrayContainsAll {
param([object[]]$Actual, [string[]]$Required)
$values = @($Actual | ForEach-Object { [string]$_ })
foreach ($item in $Required) {
if ($values -notcontains $item) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z104-remote-workspace-real-adapter-runtime-gate-validation-closeout-compatibility-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$closeout = Get-PropertyValue -Item $sourceResult -Name "runtime_gate_validation_closeout" -Default $null
$guardrails = Get-PropertyValue -Item $closeout -Name "guardrail_summary" -Default $null
$package = [ordered]@{
schema_version = "rap.remote_workspace_real_adapter_operator_enablement_readiness_package.v1"
source_closeout_schema = Get-PropertyValue -Item $closeout -Name "schema_version" -Default $null
package_status = "ready_for_operator_review"
operator_review_status = "not_reviewed"
enablement_boundary = "explicit_operator_enablement_required"
enablement_status = "not_enabled"
included_contracts = $includedContracts
required_operator_actions = $requiredOperatorActions
runtime_gate_state = Get-PropertyValue -Item $closeout -Name "runtime_gate_state" -Default $null
runtime_effect = Get-PropertyValue -Item $closeout -Name "runtime_effect" -Default $null
operator_default_action = Get-PropertyValue -Item $closeout -Name "operator_default_action" -Default $null
allows_process_start = $false
allows_payload_traffic = $false
guardrail_summary = $guardrails
}
$packageFieldsCompatible = Test-ObjectHasFields -Item $package -Fields $requiredPackageFields
$includedContractsCompatible = Test-ArrayContainsAll -Actual @(Get-PropertyValue -Item $package -Name "included_contracts" -Default @()) -Required $includedContracts
$operatorActionsCompatible = Test-ArrayContainsAll -Actual @(Get-PropertyValue -Item $package -Name "required_operator_actions" -Default @()) -Required $requiredOperatorActions
$packageValuesCompatible = (
[string](Get-PropertyValue -Item $package -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_operator_enablement_readiness_package.v1" -and
[string](Get-PropertyValue -Item $package -Name "source_closeout_schema" -Default "") -eq "rap.remote_workspace_real_adapter_runtime_gate_validation_closeout.v1" -and
[string](Get-PropertyValue -Item $package -Name "package_status" -Default "") -eq "ready_for_operator_review" -and
[string](Get-PropertyValue -Item $package -Name "operator_review_status" -Default "") -eq "not_reviewed" -and
[string](Get-PropertyValue -Item $package -Name "enablement_boundary" -Default "") -eq "explicit_operator_enablement_required" -and
[string](Get-PropertyValue -Item $package -Name "enablement_status" -Default "") -eq "not_enabled" -and
[string](Get-PropertyValue -Item $package -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $package -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $package -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
-not [bool](Get-PropertyValue -Item $package -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $package -Name "allows_payload_traffic" -Default $true)
)
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z104.remote_workspace_real_adapter_runtime_gate_validation_closeout_compatibility_smoke.v1")
closeout_present = ($null -ne $closeout)
package_fields_compatible = $packageFieldsCompatible
package_values_compatible = $packageValuesCompatible
included_contracts_compatible = $includedContractsCompatible
operator_actions_compatible = $operatorActionsCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z105.remote_workspace_real_adapter_operator_enablement_readiness_package_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_package_fields = $requiredPackageFields
included_contracts = $includedContracts
required_operator_actions = $requiredOperatorActions
required_guardrail_fields = $requiredGuardrailFields
operator_enablement_readiness_package = $package
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z105 remote workspace real-adapter operator enablement readiness package smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z105 remote workspace real-adapter operator enablement readiness package smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,160 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z106-remote-workspace-real-adapter-operator-enablement-readiness-package-compatibility-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z106-remote-workspace-real-adapter-operator-enablement-readiness-package-source-result.json"
$requiredPackageFields = @(
"schema_version",
"source_closeout_schema",
"package_status",
"operator_review_status",
"enablement_boundary",
"enablement_status",
"included_contracts",
"required_operator_actions",
"runtime_gate_state",
"runtime_effect",
"operator_default_action",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary"
)
$includedContracts = @(
"operator_confirmation_validation",
"binary_validation",
"permission_validation",
"supervisor_validation",
"health_probe_validation",
"payload_gate_validation",
"validation_closeout"
)
$requiredOperatorActions = @(
"review_validation_closeout",
"confirm_real_runtime_enablement_intent",
"select_runtime_targets",
"approve_process_start",
"approve_payload_traffic"
)
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
function Test-ArrayContainsAll {
param([object[]]$Actual, [string[]]$Required)
$values = @($Actual | ForEach-Object { [string]$_ })
foreach ($item in $Required) {
if ($values -notcontains $item) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z105-remote-workspace-real-adapter-operator-enablement-readiness-package-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$package = Get-PropertyValue -Item $sourceResult -Name "operator_enablement_readiness_package" -Default $null
$guardrails = Get-PropertyValue -Item $package -Name "guardrail_summary" -Default $null
$packageFieldsCompatible = Test-ObjectHasFields -Item $package -Fields $requiredPackageFields
$includedContractsCompatible = Test-ArrayContainsAll -Actual @(Get-PropertyValue -Item $package -Name "included_contracts" -Default @()) -Required $includedContracts
$operatorActionsCompatible = Test-ArrayContainsAll -Actual @(Get-PropertyValue -Item $package -Name "required_operator_actions" -Default @()) -Required $requiredOperatorActions
$packageValuesCompatible = (
[string](Get-PropertyValue -Item $package -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_operator_enablement_readiness_package.v1" -and
[string](Get-PropertyValue -Item $package -Name "source_closeout_schema" -Default "") -eq "rap.remote_workspace_real_adapter_runtime_gate_validation_closeout.v1" -and
[string](Get-PropertyValue -Item $package -Name "package_status" -Default "") -eq "ready_for_operator_review" -and
[string](Get-PropertyValue -Item $package -Name "operator_review_status" -Default "") -eq "not_reviewed" -and
[string](Get-PropertyValue -Item $package -Name "enablement_boundary" -Default "") -eq "explicit_operator_enablement_required" -and
[string](Get-PropertyValue -Item $package -Name "enablement_status" -Default "") -eq "not_enabled" -and
[string](Get-PropertyValue -Item $package -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $package -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $package -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
-not [bool](Get-PropertyValue -Item $package -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $package -Name "allows_payload_traffic" -Default $true)
)
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z105.remote_workspace_real_adapter_operator_enablement_readiness_package_smoke.v1")
package_present = ($null -ne $package)
package_fields_compatible = $packageFieldsCompatible
package_values_compatible = $packageValuesCompatible
included_contracts_compatible = $includedContractsCompatible
operator_actions_compatible = $operatorActionsCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z106.remote_workspace_real_adapter_operator_enablement_readiness_package_compatibility_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_package_fields = $requiredPackageFields
included_contracts = $includedContracts
required_operator_actions = $requiredOperatorActions
required_guardrail_fields = $requiredGuardrailFields
operator_enablement_readiness_package = $package
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z106 remote workspace real-adapter operator enablement readiness package compatibility smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z106 remote workspace real-adapter operator enablement readiness package compatibility smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,145 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z107-remote-workspace-real-adapter-operator-enablement-readiness-release-marker-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z107-remote-workspace-real-adapter-operator-enablement-readiness-release-marker-source-result.json"
$requiredReleaseFields = @(
"schema_version",
"source_package_schema",
"release_status",
"release_marker",
"operator_review_status",
"enablement_boundary",
"enablement_status",
"runtime_gate_state",
"runtime_effect",
"operator_default_action",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary"
)
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z106-remote-workspace-real-adapter-operator-enablement-readiness-package-compatibility-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$package = Get-PropertyValue -Item $sourceResult -Name "operator_enablement_readiness_package" -Default $null
$guardrails = Get-PropertyValue -Item $package -Name "guardrail_summary" -Default $null
$releaseMarker = [ordered]@{
schema_version = "rap.remote_workspace_real_adapter_operator_enablement_readiness_release_marker.v1"
source_package_schema = Get-PropertyValue -Item $package -Name "schema_version" -Default $null
release_status = "operator_readiness_package_contract_only"
release_marker = "c19z107_real_adapter_operator_enablement_readiness_contract_only"
operator_review_status = Get-PropertyValue -Item $package -Name "operator_review_status" -Default $null
enablement_boundary = Get-PropertyValue -Item $package -Name "enablement_boundary" -Default $null
enablement_status = Get-PropertyValue -Item $package -Name "enablement_status" -Default $null
runtime_gate_state = Get-PropertyValue -Item $package -Name "runtime_gate_state" -Default $null
runtime_effect = Get-PropertyValue -Item $package -Name "runtime_effect" -Default $null
operator_default_action = Get-PropertyValue -Item $package -Name "operator_default_action" -Default $null
allows_process_start = $false
allows_payload_traffic = $false
guardrail_summary = $guardrails
}
$releaseFieldsCompatible = Test-ObjectHasFields -Item $releaseMarker -Fields $requiredReleaseFields
$releaseValuesCompatible = (
[string](Get-PropertyValue -Item $releaseMarker -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_operator_enablement_readiness_release_marker.v1" -and
[string](Get-PropertyValue -Item $releaseMarker -Name "source_package_schema" -Default "") -eq "rap.remote_workspace_real_adapter_operator_enablement_readiness_package.v1" -and
[string](Get-PropertyValue -Item $releaseMarker -Name "release_status" -Default "") -eq "operator_readiness_package_contract_only" -and
[string](Get-PropertyValue -Item $releaseMarker -Name "release_marker" -Default "") -eq "c19z107_real_adapter_operator_enablement_readiness_contract_only" -and
[string](Get-PropertyValue -Item $releaseMarker -Name "operator_review_status" -Default "") -eq "not_reviewed" -and
[string](Get-PropertyValue -Item $releaseMarker -Name "enablement_boundary" -Default "") -eq "explicit_operator_enablement_required" -and
[string](Get-PropertyValue -Item $releaseMarker -Name "enablement_status" -Default "") -eq "not_enabled" -and
[string](Get-PropertyValue -Item $releaseMarker -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $releaseMarker -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $releaseMarker -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
-not [bool](Get-PropertyValue -Item $releaseMarker -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $releaseMarker -Name "allows_payload_traffic" -Default $true)
)
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z106.remote_workspace_real_adapter_operator_enablement_readiness_package_compatibility_smoke.v1")
package_present = ($null -ne $package)
release_fields_compatible = $releaseFieldsCompatible
release_values_compatible = $releaseValuesCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z107.remote_workspace_real_adapter_operator_enablement_readiness_release_marker_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_release_fields = $requiredReleaseFields
required_guardrail_fields = $requiredGuardrailFields
operator_enablement_readiness_release_marker = $releaseMarker
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z107 remote workspace real-adapter operator enablement readiness release marker smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z107 remote workspace real-adapter operator enablement readiness release marker smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,129 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z108-remote-workspace-real-adapter-operator-enablement-readiness-release-marker-compatibility-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z108-remote-workspace-real-adapter-operator-enablement-readiness-release-marker-source-result.json"
$requiredReleaseFields = @(
"schema_version",
"source_package_schema",
"release_status",
"release_marker",
"operator_review_status",
"enablement_boundary",
"enablement_status",
"runtime_gate_state",
"runtime_effect",
"operator_default_action",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary"
)
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z107-remote-workspace-real-adapter-operator-enablement-readiness-release-marker-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$marker = Get-PropertyValue -Item $sourceResult -Name "operator_enablement_readiness_release_marker" -Default $null
$guardrails = Get-PropertyValue -Item $marker -Name "guardrail_summary" -Default $null
$releaseFieldsCompatible = Test-ObjectHasFields -Item $marker -Fields $requiredReleaseFields
$releaseValuesCompatible = (
[string](Get-PropertyValue -Item $marker -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_operator_enablement_readiness_release_marker.v1" -and
[string](Get-PropertyValue -Item $marker -Name "source_package_schema" -Default "") -eq "rap.remote_workspace_real_adapter_operator_enablement_readiness_package.v1" -and
[string](Get-PropertyValue -Item $marker -Name "release_status" -Default "") -eq "operator_readiness_package_contract_only" -and
[string](Get-PropertyValue -Item $marker -Name "release_marker" -Default "") -eq "c19z107_real_adapter_operator_enablement_readiness_contract_only" -and
[string](Get-PropertyValue -Item $marker -Name "operator_review_status" -Default "") -eq "not_reviewed" -and
[string](Get-PropertyValue -Item $marker -Name "enablement_boundary" -Default "") -eq "explicit_operator_enablement_required" -and
[string](Get-PropertyValue -Item $marker -Name "enablement_status" -Default "") -eq "not_enabled" -and
[string](Get-PropertyValue -Item $marker -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $marker -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $marker -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
-not [bool](Get-PropertyValue -Item $marker -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $marker -Name "allows_payload_traffic" -Default $true)
)
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z107.remote_workspace_real_adapter_operator_enablement_readiness_release_marker_smoke.v1")
release_marker_present = ($null -ne $marker)
release_fields_compatible = $releaseFieldsCompatible
release_values_compatible = $releaseValuesCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z108.remote_workspace_real_adapter_operator_enablement_readiness_release_marker_compatibility_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_release_fields = $requiredReleaseFields
required_guardrail_fields = $requiredGuardrailFields
operator_enablement_readiness_release_marker = $marker
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z108 remote workspace real-adapter operator enablement readiness release marker compatibility smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z108 remote workspace real-adapter operator enablement readiness release marker compatibility smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,175 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z109-remote-workspace-real-adapter-operator-enablement-readiness-package-index-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z109-remote-workspace-real-adapter-operator-enablement-readiness-package-index-source-result.json"
$requiredPackageFields = @(
"schema_version",
"source_release_marker_schema",
"package_status",
"package_marker",
"covered_stage_range",
"covered_stage_count",
"latest_compatibility_stage",
"operator_review_status",
"enablement_boundary",
"enablement_status",
"runtime_gate_state",
"runtime_effect",
"operator_default_action",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary",
"closeout_notes"
)
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
$requiredCloseoutNotes = @(
"operator_enablement_readiness_package_indexed",
"operator_review_not_started",
"real_runtime_gate_not_enabled",
"process_start_disabled",
"payload_forwarding_disabled"
)
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
function Test-ArrayContainsAll {
param([object[]]$Actual, [string[]]$Required)
$values = @($Actual | ForEach-Object { [string]$_ })
foreach ($item in $Required) {
if ($values -notcontains $item) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z108-remote-workspace-real-adapter-operator-enablement-readiness-release-marker-compatibility-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$marker = Get-PropertyValue -Item $sourceResult -Name "operator_enablement_readiness_release_marker" -Default $null
$guardrails = Get-PropertyValue -Item $marker -Name "guardrail_summary" -Default $null
$packageIndex = [ordered]@{
schema_version = "rap.remote_workspace_real_adapter_operator_enablement_readiness_package_index.v1"
source_release_marker_schema = Get-PropertyValue -Item $marker -Name "schema_version" -Default $null
package_status = "indexed_contract_only"
package_marker = "c19z109_real_adapter_operator_enablement_readiness_package_index_contract_only"
covered_stage_range = "C19Z89-C19Z108"
covered_stage_count = 20
latest_compatibility_stage = "C19Z108"
operator_review_status = Get-PropertyValue -Item $marker -Name "operator_review_status" -Default $null
enablement_boundary = Get-PropertyValue -Item $marker -Name "enablement_boundary" -Default $null
enablement_status = Get-PropertyValue -Item $marker -Name "enablement_status" -Default $null
runtime_gate_state = Get-PropertyValue -Item $marker -Name "runtime_gate_state" -Default $null
runtime_effect = Get-PropertyValue -Item $marker -Name "runtime_effect" -Default $null
operator_default_action = Get-PropertyValue -Item $marker -Name "operator_default_action" -Default $null
allows_process_start = $false
allows_payload_traffic = $false
guardrail_summary = $guardrails
closeout_notes = $requiredCloseoutNotes
}
$packageFieldsCompatible = Test-ObjectHasFields -Item $packageIndex -Fields $requiredPackageFields
$closeoutNotesCompatible = Test-ArrayContainsAll -Actual @(Get-PropertyValue -Item $packageIndex -Name "closeout_notes" -Default @()) -Required $requiredCloseoutNotes
$packageValuesCompatible = (
[string](Get-PropertyValue -Item $packageIndex -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_operator_enablement_readiness_package_index.v1" -and
[string](Get-PropertyValue -Item $packageIndex -Name "source_release_marker_schema" -Default "") -eq "rap.remote_workspace_real_adapter_operator_enablement_readiness_release_marker.v1" -and
[string](Get-PropertyValue -Item $packageIndex -Name "package_status" -Default "") -eq "indexed_contract_only" -and
[string](Get-PropertyValue -Item $packageIndex -Name "package_marker" -Default "") -eq "c19z109_real_adapter_operator_enablement_readiness_package_index_contract_only" -and
[string](Get-PropertyValue -Item $packageIndex -Name "covered_stage_range" -Default "") -eq "C19Z89-C19Z108" -and
[int](Get-PropertyValue -Item $packageIndex -Name "covered_stage_count" -Default -1) -eq 20 -and
[string](Get-PropertyValue -Item $packageIndex -Name "latest_compatibility_stage" -Default "") -eq "C19Z108" -and
[string](Get-PropertyValue -Item $packageIndex -Name "operator_review_status" -Default "") -eq "not_reviewed" -and
[string](Get-PropertyValue -Item $packageIndex -Name "enablement_boundary" -Default "") -eq "explicit_operator_enablement_required" -and
[string](Get-PropertyValue -Item $packageIndex -Name "enablement_status" -Default "") -eq "not_enabled" -and
[string](Get-PropertyValue -Item $packageIndex -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $packageIndex -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $packageIndex -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
-not [bool](Get-PropertyValue -Item $packageIndex -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $packageIndex -Name "allows_payload_traffic" -Default $true)
)
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z108.remote_workspace_real_adapter_operator_enablement_readiness_release_marker_compatibility_smoke.v1")
release_marker_present = ($null -ne $marker)
package_fields_compatible = $packageFieldsCompatible
package_values_compatible = $packageValuesCompatible
closeout_notes_compatible = $closeoutNotesCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z109.remote_workspace_real_adapter_operator_enablement_readiness_package_index_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_package_fields = $requiredPackageFields
required_guardrail_fields = $requiredGuardrailFields
required_closeout_notes = $requiredCloseoutNotes
operator_enablement_readiness_package_index = $packageIndex
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z109 remote workspace real-adapter operator enablement readiness package index smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z109 remote workspace real-adapter operator enablement readiness package index smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,87 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$EntryNodeName = "test-1",
[string]$ExitNodeName = "test-2",
[string]$EntryBaseUrl = "http://192.168.200.61:19131",
[string]$ResultPath = "artifacts\c19z11-remote-workspace-mailbox-preflight-checklist-status-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z11-remote-workspace-mailbox-preflight-checklist-status-source-result.json"
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ChecklistStatus {
param([object]$Sink, [string]$AdapterSessionID)
if ($null -eq $Sink) { return $false }
$readiness = Get-PropertyValue -Item $Sink -Name "adapter_runtime_readiness" -Default $null
$rollup = Get-PropertyValue -Item $readiness -Name "last_preflight" -Default $null
if ($null -eq $rollup) { return $false }
$counts = Get-PropertyValue -Item $rollup -Name "remediation_checklist_counts" -Default $null
return (
[string](Get-PropertyValue -Item $readiness -Name "adapter_session_id" -Default "") -eq $AdapterSessionID -and
[string](Get-PropertyValue -Item $rollup -Name "operator_status" -Default "") -eq "resync_required" -and
[string](Get-PropertyValue -Item $rollup -Name "remediation_checklist_status" -Default "") -eq "action_required" -and
[string](Get-PropertyValue -Item $counts -Name "status" -Default "") -eq "action_required" -and
[int](Get-PropertyValue -Item $counts -Name "total_count" -Default -1) -eq 3 -and
[int](Get-PropertyValue -Item $counts -Name "required_count" -Default -1) -eq 3 -and
[int](Get-PropertyValue -Item $counts -Name "satisfied_count" -Default -1) -eq 0 -and
[int](Get-PropertyValue -Item $counts -Name "pending_count" -Default -1) -eq 3
)
}
$source = & powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z10-remote-workspace-mailbox-preflight-checklist-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-EntryNodeName $EntryNodeName `
-ExitNodeName $ExitNodeName `
-EntryBaseUrl $EntryBaseUrl `
-ResultPath $sourceResultPath
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$adapterSessionID = [string](Get-PropertyValue -Item $sourceResult -Name "adapter_session_id" -Default "")
$observed = Get-PropertyValue -Item $sourceResult -Name "observed" -Default $null
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
adapter_session_id_present = ($adapterSessionID -match "^rap-rw-adapter-session-[0-9a-f]{24}$")
workload_checklist_status_visible = (Test-ChecklistStatus -Sink $observed.workload_sink -AdapterSessionID $adapterSessionID)
telemetry_checklist_status_visible = (Test-ChecklistStatus -Sink $observed.telemetry_sink -AdapterSessionID $adapterSessionID)
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z11.remote_workspace_mailbox_preflight_checklist_status_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
adapter_session_id = $adapterSessionID
observed = $observed
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z11 remote workspace mailbox preflight checklist status smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z11 remote workspace mailbox preflight checklist status smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,156 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z110-remote-workspace-real-adapter-operator-enablement-readiness-package-index-compatibility-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z110-remote-workspace-real-adapter-operator-enablement-readiness-package-index-source-result.json"
$requiredPackageFields = @(
"schema_version",
"source_release_marker_schema",
"package_status",
"package_marker",
"covered_stage_range",
"covered_stage_count",
"latest_compatibility_stage",
"operator_review_status",
"enablement_boundary",
"enablement_status",
"runtime_gate_state",
"runtime_effect",
"operator_default_action",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary",
"closeout_notes"
)
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
$requiredCloseoutNotes = @(
"operator_enablement_readiness_package_indexed",
"operator_review_not_started",
"real_runtime_gate_not_enabled",
"process_start_disabled",
"payload_forwarding_disabled"
)
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
function Test-ArrayContainsAll {
param([object[]]$Actual, [string[]]$Required)
$values = @($Actual | ForEach-Object { [string]$_ })
foreach ($item in $Required) {
if ($values -notcontains $item) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z109-remote-workspace-real-adapter-operator-enablement-readiness-package-index-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$packageIndex = Get-PropertyValue -Item $sourceResult -Name "operator_enablement_readiness_package_index" -Default $null
$guardrails = Get-PropertyValue -Item $packageIndex -Name "guardrail_summary" -Default $null
$closeoutNotes = @(Get-PropertyValue -Item $packageIndex -Name "closeout_notes" -Default @())
$packageFieldsCompatible = Test-ObjectHasFields -Item $packageIndex -Fields $requiredPackageFields
$closeoutNotesCompatible = Test-ArrayContainsAll -Actual $closeoutNotes -Required $requiredCloseoutNotes
$packageValuesCompatible = (
[string](Get-PropertyValue -Item $packageIndex -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_operator_enablement_readiness_package_index.v1" -and
[string](Get-PropertyValue -Item $packageIndex -Name "source_release_marker_schema" -Default "") -eq "rap.remote_workspace_real_adapter_operator_enablement_readiness_release_marker.v1" -and
[string](Get-PropertyValue -Item $packageIndex -Name "package_status" -Default "") -eq "indexed_contract_only" -and
[string](Get-PropertyValue -Item $packageIndex -Name "package_marker" -Default "") -eq "c19z109_real_adapter_operator_enablement_readiness_package_index_contract_only" -and
[string](Get-PropertyValue -Item $packageIndex -Name "covered_stage_range" -Default "") -eq "C19Z89-C19Z108" -and
[int](Get-PropertyValue -Item $packageIndex -Name "covered_stage_count" -Default -1) -eq 20 -and
[string](Get-PropertyValue -Item $packageIndex -Name "latest_compatibility_stage" -Default "") -eq "C19Z108" -and
[string](Get-PropertyValue -Item $packageIndex -Name "operator_review_status" -Default "") -eq "not_reviewed" -and
[string](Get-PropertyValue -Item $packageIndex -Name "enablement_boundary" -Default "") -eq "explicit_operator_enablement_required" -and
[string](Get-PropertyValue -Item $packageIndex -Name "enablement_status" -Default "") -eq "not_enabled" -and
[string](Get-PropertyValue -Item $packageIndex -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $packageIndex -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $packageIndex -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
-not [bool](Get-PropertyValue -Item $packageIndex -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $packageIndex -Name "allows_payload_traffic" -Default $true)
)
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z109.remote_workspace_real_adapter_operator_enablement_readiness_package_index_smoke.v1")
package_index_present = ($null -ne $packageIndex)
package_fields_compatible = $packageFieldsCompatible
package_values_compatible = $packageValuesCompatible
closeout_notes_compatible = $closeoutNotesCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z110.remote_workspace_real_adapter_operator_enablement_readiness_package_index_compatibility_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_package_fields = $requiredPackageFields
required_guardrail_fields = $requiredGuardrailFields
required_closeout_notes = $requiredCloseoutNotes
operator_enablement_readiness_package_index = $packageIndex
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z110 remote workspace real-adapter operator enablement readiness package index compatibility smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z110 remote workspace real-adapter operator enablement readiness package index compatibility smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,157 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z111-remote-workspace-real-adapter-operator-readiness-closeout-summary-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z111-remote-workspace-real-adapter-operator-readiness-closeout-summary-source-result.json"
$requiredCloseoutFields = @(
"schema_version",
"source_package_index_schema",
"closeout_status",
"closeout_marker",
"covered_stage_range",
"covered_stage_count",
"latest_compatibility_stage",
"operator_review_status",
"enablement_boundary",
"enablement_status",
"runtime_gate_state",
"runtime_effect",
"operator_default_action",
"next_required_phase",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary"
)
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z110-remote-workspace-real-adapter-operator-enablement-readiness-package-index-compatibility-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$packageIndex = Get-PropertyValue -Item $sourceResult -Name "operator_enablement_readiness_package_index" -Default $null
$guardrails = Get-PropertyValue -Item $packageIndex -Name "guardrail_summary" -Default $null
$closeout = [ordered]@{
schema_version = "rap.remote_workspace_real_adapter_operator_readiness_closeout_summary.v1"
source_package_index_schema = Get-PropertyValue -Item $packageIndex -Name "schema_version" -Default $null
closeout_status = "closed_contract_only_ready_for_operator_review"
closeout_marker = "c19z111_real_adapter_operator_readiness_closed_contract_only"
covered_stage_range = Get-PropertyValue -Item $packageIndex -Name "covered_stage_range" -Default $null
covered_stage_count = Get-PropertyValue -Item $packageIndex -Name "covered_stage_count" -Default $null
latest_compatibility_stage = Get-PropertyValue -Item $packageIndex -Name "latest_compatibility_stage" -Default $null
operator_review_status = Get-PropertyValue -Item $packageIndex -Name "operator_review_status" -Default $null
enablement_boundary = Get-PropertyValue -Item $packageIndex -Name "enablement_boundary" -Default $null
enablement_status = Get-PropertyValue -Item $packageIndex -Name "enablement_status" -Default $null
runtime_gate_state = Get-PropertyValue -Item $packageIndex -Name "runtime_gate_state" -Default $null
runtime_effect = Get-PropertyValue -Item $packageIndex -Name "runtime_effect" -Default $null
operator_default_action = Get-PropertyValue -Item $packageIndex -Name "operator_default_action" -Default $null
next_required_phase = "explicit_operator_review_and_enablement_decision"
allows_process_start = $false
allows_payload_traffic = $false
guardrail_summary = $guardrails
}
$closeoutFieldsCompatible = Test-ObjectHasFields -Item $closeout -Fields $requiredCloseoutFields
$closeoutValuesCompatible = (
[string](Get-PropertyValue -Item $closeout -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_operator_readiness_closeout_summary.v1" -and
[string](Get-PropertyValue -Item $closeout -Name "source_package_index_schema" -Default "") -eq "rap.remote_workspace_real_adapter_operator_enablement_readiness_package_index.v1" -and
[string](Get-PropertyValue -Item $closeout -Name "closeout_status" -Default "") -eq "closed_contract_only_ready_for_operator_review" -and
[string](Get-PropertyValue -Item $closeout -Name "closeout_marker" -Default "") -eq "c19z111_real_adapter_operator_readiness_closed_contract_only" -and
[string](Get-PropertyValue -Item $closeout -Name "covered_stage_range" -Default "") -eq "C19Z89-C19Z108" -and
[int](Get-PropertyValue -Item $closeout -Name "covered_stage_count" -Default -1) -eq 20 -and
[string](Get-PropertyValue -Item $closeout -Name "latest_compatibility_stage" -Default "") -eq "C19Z108" -and
[string](Get-PropertyValue -Item $closeout -Name "operator_review_status" -Default "") -eq "not_reviewed" -and
[string](Get-PropertyValue -Item $closeout -Name "enablement_boundary" -Default "") -eq "explicit_operator_enablement_required" -and
[string](Get-PropertyValue -Item $closeout -Name "enablement_status" -Default "") -eq "not_enabled" -and
[string](Get-PropertyValue -Item $closeout -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $closeout -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $closeout -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
[string](Get-PropertyValue -Item $closeout -Name "next_required_phase" -Default "") -eq "explicit_operator_review_and_enablement_decision" -and
-not [bool](Get-PropertyValue -Item $closeout -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $closeout -Name "allows_payload_traffic" -Default $true)
)
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z110.remote_workspace_real_adapter_operator_enablement_readiness_package_index_compatibility_smoke.v1")
package_index_present = ($null -ne $packageIndex)
closeout_fields_compatible = $closeoutFieldsCompatible
closeout_values_compatible = $closeoutValuesCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z111.remote_workspace_real_adapter_operator_readiness_closeout_summary_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_closeout_fields = $requiredCloseoutFields
required_guardrail_fields = $requiredGuardrailFields
operator_readiness_closeout_summary = $closeout
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z111 remote workspace real-adapter operator readiness closeout summary smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z111 remote workspace real-adapter operator readiness closeout summary smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,137 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z112-remote-workspace-real-adapter-operator-readiness-closeout-summary-compatibility-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z112-remote-workspace-real-adapter-operator-readiness-closeout-summary-source-result.json"
$requiredCloseoutFields = @(
"schema_version",
"source_package_index_schema",
"closeout_status",
"closeout_marker",
"covered_stage_range",
"covered_stage_count",
"latest_compatibility_stage",
"operator_review_status",
"enablement_boundary",
"enablement_status",
"runtime_gate_state",
"runtime_effect",
"operator_default_action",
"next_required_phase",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary"
)
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z111-remote-workspace-real-adapter-operator-readiness-closeout-summary-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$closeout = Get-PropertyValue -Item $sourceResult -Name "operator_readiness_closeout_summary" -Default $null
$guardrails = Get-PropertyValue -Item $closeout -Name "guardrail_summary" -Default $null
$closeoutFieldsCompatible = Test-ObjectHasFields -Item $closeout -Fields $requiredCloseoutFields
$closeoutValuesCompatible = (
[string](Get-PropertyValue -Item $closeout -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_operator_readiness_closeout_summary.v1" -and
[string](Get-PropertyValue -Item $closeout -Name "source_package_index_schema" -Default "") -eq "rap.remote_workspace_real_adapter_operator_enablement_readiness_package_index.v1" -and
[string](Get-PropertyValue -Item $closeout -Name "closeout_status" -Default "") -eq "closed_contract_only_ready_for_operator_review" -and
[string](Get-PropertyValue -Item $closeout -Name "closeout_marker" -Default "") -eq "c19z111_real_adapter_operator_readiness_closed_contract_only" -and
[string](Get-PropertyValue -Item $closeout -Name "covered_stage_range" -Default "") -eq "C19Z89-C19Z108" -and
[int](Get-PropertyValue -Item $closeout -Name "covered_stage_count" -Default -1) -eq 20 -and
[string](Get-PropertyValue -Item $closeout -Name "latest_compatibility_stage" -Default "") -eq "C19Z108" -and
[string](Get-PropertyValue -Item $closeout -Name "operator_review_status" -Default "") -eq "not_reviewed" -and
[string](Get-PropertyValue -Item $closeout -Name "enablement_boundary" -Default "") -eq "explicit_operator_enablement_required" -and
[string](Get-PropertyValue -Item $closeout -Name "enablement_status" -Default "") -eq "not_enabled" -and
[string](Get-PropertyValue -Item $closeout -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $closeout -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $closeout -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
[string](Get-PropertyValue -Item $closeout -Name "next_required_phase" -Default "") -eq "explicit_operator_review_and_enablement_decision" -and
-not [bool](Get-PropertyValue -Item $closeout -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $closeout -Name "allows_payload_traffic" -Default $true)
)
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z111.remote_workspace_real_adapter_operator_readiness_closeout_summary_smoke.v1")
closeout_summary_present = ($null -ne $closeout)
closeout_fields_compatible = $closeoutFieldsCompatible
closeout_values_compatible = $closeoutValuesCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z112.remote_workspace_real_adapter_operator_readiness_closeout_summary_compatibility_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_closeout_fields = $requiredCloseoutFields
required_guardrail_fields = $requiredGuardrailFields
operator_readiness_closeout_summary = $closeout
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z112 remote workspace real-adapter operator readiness closeout summary compatibility smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z112 remote workspace real-adapter operator readiness closeout summary compatibility smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,172 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z113-remote-workspace-real-adapter-operator-review-decision-request-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z113-remote-workspace-real-adapter-operator-review-decision-request-source-result.json"
$requiredRequestFields = @(
"schema_version",
"source_closeout_schema",
"review_request_status",
"review_request_marker",
"requested_decision",
"enablement_decision",
"operator_review_status",
"enablement_boundary",
"enablement_status",
"runtime_gate_state",
"runtime_effect",
"operator_default_action",
"decision_prerequisites",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary"
)
$decisionPrerequisites = @(
"operator_reviews_closeout_summary",
"operator_confirms_real_runtime_enablement_intent",
"operator_selects_runtime_targets",
"operator_approves_process_start",
"operator_approves_payload_traffic"
)
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
function Test-ArrayContainsAll {
param([object[]]$Actual, [string[]]$Required)
$values = @($Actual | ForEach-Object { [string]$_ })
foreach ($item in $Required) {
if ($values -notcontains $item) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z112-remote-workspace-real-adapter-operator-readiness-closeout-summary-compatibility-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$closeout = Get-PropertyValue -Item $sourceResult -Name "operator_readiness_closeout_summary" -Default $null
$guardrails = Get-PropertyValue -Item $closeout -Name "guardrail_summary" -Default $null
$request = [ordered]@{
schema_version = "rap.remote_workspace_real_adapter_operator_review_decision_request.v1"
source_closeout_schema = Get-PropertyValue -Item $closeout -Name "schema_version" -Default $null
review_request_status = "pending_operator_decision"
review_request_marker = "c19z113_real_adapter_operator_review_decision_request_contract_only"
requested_decision = "review_real_runtime_enablement"
enablement_decision = "not_approved"
operator_review_status = "pending"
enablement_boundary = Get-PropertyValue -Item $closeout -Name "enablement_boundary" -Default $null
enablement_status = Get-PropertyValue -Item $closeout -Name "enablement_status" -Default $null
runtime_gate_state = Get-PropertyValue -Item $closeout -Name "runtime_gate_state" -Default $null
runtime_effect = Get-PropertyValue -Item $closeout -Name "runtime_effect" -Default $null
operator_default_action = Get-PropertyValue -Item $closeout -Name "operator_default_action" -Default $null
decision_prerequisites = $decisionPrerequisites
allows_process_start = $false
allows_payload_traffic = $false
guardrail_summary = $guardrails
}
$requestFieldsCompatible = Test-ObjectHasFields -Item $request -Fields $requiredRequestFields
$prerequisitesCompatible = Test-ArrayContainsAll -Actual @(Get-PropertyValue -Item $request -Name "decision_prerequisites" -Default @()) -Required $decisionPrerequisites
$requestValuesCompatible = (
[string](Get-PropertyValue -Item $request -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_operator_review_decision_request.v1" -and
[string](Get-PropertyValue -Item $request -Name "source_closeout_schema" -Default "") -eq "rap.remote_workspace_real_adapter_operator_readiness_closeout_summary.v1" -and
[string](Get-PropertyValue -Item $request -Name "review_request_status" -Default "") -eq "pending_operator_decision" -and
[string](Get-PropertyValue -Item $request -Name "review_request_marker" -Default "") -eq "c19z113_real_adapter_operator_review_decision_request_contract_only" -and
[string](Get-PropertyValue -Item $request -Name "requested_decision" -Default "") -eq "review_real_runtime_enablement" -and
[string](Get-PropertyValue -Item $request -Name "enablement_decision" -Default "") -eq "not_approved" -and
[string](Get-PropertyValue -Item $request -Name "operator_review_status" -Default "") -eq "pending" -and
[string](Get-PropertyValue -Item $request -Name "enablement_boundary" -Default "") -eq "explicit_operator_enablement_required" -and
[string](Get-PropertyValue -Item $request -Name "enablement_status" -Default "") -eq "not_enabled" -and
[string](Get-PropertyValue -Item $request -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $request -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $request -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
-not [bool](Get-PropertyValue -Item $request -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $request -Name "allows_payload_traffic" -Default $true)
)
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z112.remote_workspace_real_adapter_operator_readiness_closeout_summary_compatibility_smoke.v1")
closeout_summary_present = ($null -ne $closeout)
request_fields_compatible = $requestFieldsCompatible
request_values_compatible = $requestValuesCompatible
prerequisites_compatible = $prerequisitesCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z113.remote_workspace_real_adapter_operator_review_decision_request_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_request_fields = $requiredRequestFields
decision_prerequisites = $decisionPrerequisites
required_guardrail_fields = $requiredGuardrailFields
operator_review_decision_request = $request
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z113 remote workspace real-adapter operator review decision request smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z113 remote workspace real-adapter operator review decision request smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,153 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z114-remote-workspace-real-adapter-operator-review-decision-request-compatibility-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z114-remote-workspace-real-adapter-operator-review-decision-request-source-result.json"
$requiredRequestFields = @(
"schema_version",
"source_closeout_schema",
"review_request_status",
"review_request_marker",
"requested_decision",
"enablement_decision",
"operator_review_status",
"enablement_boundary",
"enablement_status",
"runtime_gate_state",
"runtime_effect",
"operator_default_action",
"decision_prerequisites",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary"
)
$decisionPrerequisites = @(
"operator_reviews_closeout_summary",
"operator_confirms_real_runtime_enablement_intent",
"operator_selects_runtime_targets",
"operator_approves_process_start",
"operator_approves_payload_traffic"
)
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
function Test-ArrayContainsAll {
param([object[]]$Actual, [string[]]$Required)
$values = @($Actual | ForEach-Object { [string]$_ })
foreach ($item in $Required) {
if ($values -notcontains $item) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z113-remote-workspace-real-adapter-operator-review-decision-request-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$request = Get-PropertyValue -Item $sourceResult -Name "operator_review_decision_request" -Default $null
$guardrails = Get-PropertyValue -Item $request -Name "guardrail_summary" -Default $null
$requestFieldsCompatible = Test-ObjectHasFields -Item $request -Fields $requiredRequestFields
$prerequisitesCompatible = Test-ArrayContainsAll -Actual @(Get-PropertyValue -Item $request -Name "decision_prerequisites" -Default @()) -Required $decisionPrerequisites
$requestValuesCompatible = (
[string](Get-PropertyValue -Item $request -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_operator_review_decision_request.v1" -and
[string](Get-PropertyValue -Item $request -Name "source_closeout_schema" -Default "") -eq "rap.remote_workspace_real_adapter_operator_readiness_closeout_summary.v1" -and
[string](Get-PropertyValue -Item $request -Name "review_request_status" -Default "") -eq "pending_operator_decision" -and
[string](Get-PropertyValue -Item $request -Name "review_request_marker" -Default "") -eq "c19z113_real_adapter_operator_review_decision_request_contract_only" -and
[string](Get-PropertyValue -Item $request -Name "requested_decision" -Default "") -eq "review_real_runtime_enablement" -and
[string](Get-PropertyValue -Item $request -Name "enablement_decision" -Default "") -eq "not_approved" -and
[string](Get-PropertyValue -Item $request -Name "operator_review_status" -Default "") -eq "pending" -and
[string](Get-PropertyValue -Item $request -Name "enablement_boundary" -Default "") -eq "explicit_operator_enablement_required" -and
[string](Get-PropertyValue -Item $request -Name "enablement_status" -Default "") -eq "not_enabled" -and
[string](Get-PropertyValue -Item $request -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $request -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $request -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
-not [bool](Get-PropertyValue -Item $request -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $request -Name "allows_payload_traffic" -Default $true)
)
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z113.remote_workspace_real_adapter_operator_review_decision_request_smoke.v1")
decision_request_present = ($null -ne $request)
request_fields_compatible = $requestFieldsCompatible
request_values_compatible = $requestValuesCompatible
prerequisites_compatible = $prerequisitesCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z114.remote_workspace_real_adapter_operator_review_decision_request_compatibility_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_request_fields = $requiredRequestFields
decision_prerequisites = $decisionPrerequisites
required_guardrail_fields = $requiredGuardrailFields
operator_review_decision_request = $request
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z114 remote workspace real-adapter operator review decision request compatibility smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z114 remote workspace real-adapter operator review decision request compatibility smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,154 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z115-remote-workspace-real-adapter-operator-decision-status-summary-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z115-remote-workspace-real-adapter-operator-decision-status-summary-source-result.json"
$requiredSummaryFields = @(
"schema_version",
"source_decision_request_schema",
"decision_status",
"decision_summary_marker",
"requested_decision",
"enablement_decision",
"operator_review_status",
"enablement_boundary",
"enablement_status",
"runtime_gate_state",
"runtime_effect",
"operator_default_action",
"next_required_phase",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary"
)
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z114-remote-workspace-real-adapter-operator-review-decision-request-compatibility-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$request = Get-PropertyValue -Item $sourceResult -Name "operator_review_decision_request" -Default $null
$guardrails = Get-PropertyValue -Item $request -Name "guardrail_summary" -Default $null
$summary = [ordered]@{
schema_version = "rap.remote_workspace_real_adapter_operator_decision_status_summary.v1"
source_decision_request_schema = Get-PropertyValue -Item $request -Name "schema_version" -Default $null
decision_status = "pending_not_approved"
decision_summary_marker = "c19z115_real_adapter_operator_decision_status_pending_contract_only"
requested_decision = Get-PropertyValue -Item $request -Name "requested_decision" -Default $null
enablement_decision = Get-PropertyValue -Item $request -Name "enablement_decision" -Default $null
operator_review_status = Get-PropertyValue -Item $request -Name "operator_review_status" -Default $null
enablement_boundary = Get-PropertyValue -Item $request -Name "enablement_boundary" -Default $null
enablement_status = Get-PropertyValue -Item $request -Name "enablement_status" -Default $null
runtime_gate_state = Get-PropertyValue -Item $request -Name "runtime_gate_state" -Default $null
runtime_effect = Get-PropertyValue -Item $request -Name "runtime_effect" -Default $null
operator_default_action = Get-PropertyValue -Item $request -Name "operator_default_action" -Default $null
next_required_phase = "explicit_operator_approval_or_rejection"
allows_process_start = $false
allows_payload_traffic = $false
guardrail_summary = $guardrails
}
$summaryFieldsCompatible = Test-ObjectHasFields -Item $summary -Fields $requiredSummaryFields
$summaryValuesCompatible = (
[string](Get-PropertyValue -Item $summary -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_operator_decision_status_summary.v1" -and
[string](Get-PropertyValue -Item $summary -Name "source_decision_request_schema" -Default "") -eq "rap.remote_workspace_real_adapter_operator_review_decision_request.v1" -and
[string](Get-PropertyValue -Item $summary -Name "decision_status" -Default "") -eq "pending_not_approved" -and
[string](Get-PropertyValue -Item $summary -Name "decision_summary_marker" -Default "") -eq "c19z115_real_adapter_operator_decision_status_pending_contract_only" -and
[string](Get-PropertyValue -Item $summary -Name "requested_decision" -Default "") -eq "review_real_runtime_enablement" -and
[string](Get-PropertyValue -Item $summary -Name "enablement_decision" -Default "") -eq "not_approved" -and
[string](Get-PropertyValue -Item $summary -Name "operator_review_status" -Default "") -eq "pending" -and
[string](Get-PropertyValue -Item $summary -Name "enablement_boundary" -Default "") -eq "explicit_operator_enablement_required" -and
[string](Get-PropertyValue -Item $summary -Name "enablement_status" -Default "") -eq "not_enabled" -and
[string](Get-PropertyValue -Item $summary -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $summary -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $summary -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
[string](Get-PropertyValue -Item $summary -Name "next_required_phase" -Default "") -eq "explicit_operator_approval_or_rejection" -and
-not [bool](Get-PropertyValue -Item $summary -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $summary -Name "allows_payload_traffic" -Default $true)
)
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z114.remote_workspace_real_adapter_operator_review_decision_request_compatibility_smoke.v1")
decision_request_present = ($null -ne $request)
summary_fields_compatible = $summaryFieldsCompatible
summary_values_compatible = $summaryValuesCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z115.remote_workspace_real_adapter_operator_decision_status_summary_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_summary_fields = $requiredSummaryFields
required_guardrail_fields = $requiredGuardrailFields
operator_decision_status_summary = $summary
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z115 remote workspace real-adapter operator decision status summary smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z115 remote workspace real-adapter operator decision status summary smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,135 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z116-remote-workspace-real-adapter-operator-decision-status-summary-compatibility-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z116-remote-workspace-real-adapter-operator-decision-status-summary-source-result.json"
$requiredSummaryFields = @(
"schema_version",
"source_decision_request_schema",
"decision_status",
"decision_summary_marker",
"requested_decision",
"enablement_decision",
"operator_review_status",
"enablement_boundary",
"enablement_status",
"runtime_gate_state",
"runtime_effect",
"operator_default_action",
"next_required_phase",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary"
)
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z115-remote-workspace-real-adapter-operator-decision-status-summary-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$summary = Get-PropertyValue -Item $sourceResult -Name "operator_decision_status_summary" -Default $null
$guardrails = Get-PropertyValue -Item $summary -Name "guardrail_summary" -Default $null
$summaryFieldsCompatible = Test-ObjectHasFields -Item $summary -Fields $requiredSummaryFields
$summaryValuesCompatible = (
[string](Get-PropertyValue -Item $summary -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_operator_decision_status_summary.v1" -and
[string](Get-PropertyValue -Item $summary -Name "source_decision_request_schema" -Default "") -eq "rap.remote_workspace_real_adapter_operator_review_decision_request.v1" -and
[string](Get-PropertyValue -Item $summary -Name "decision_status" -Default "") -eq "pending_not_approved" -and
[string](Get-PropertyValue -Item $summary -Name "decision_summary_marker" -Default "") -eq "c19z115_real_adapter_operator_decision_status_pending_contract_only" -and
[string](Get-PropertyValue -Item $summary -Name "requested_decision" -Default "") -eq "review_real_runtime_enablement" -and
[string](Get-PropertyValue -Item $summary -Name "enablement_decision" -Default "") -eq "not_approved" -and
[string](Get-PropertyValue -Item $summary -Name "operator_review_status" -Default "") -eq "pending" -and
[string](Get-PropertyValue -Item $summary -Name "enablement_boundary" -Default "") -eq "explicit_operator_enablement_required" -and
[string](Get-PropertyValue -Item $summary -Name "enablement_status" -Default "") -eq "not_enabled" -and
[string](Get-PropertyValue -Item $summary -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $summary -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $summary -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
[string](Get-PropertyValue -Item $summary -Name "next_required_phase" -Default "") -eq "explicit_operator_approval_or_rejection" -and
-not [bool](Get-PropertyValue -Item $summary -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $summary -Name "allows_payload_traffic" -Default $true)
)
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z115.remote_workspace_real_adapter_operator_decision_status_summary_smoke.v1")
decision_status_summary_present = ($null -ne $summary)
summary_fields_compatible = $summaryFieldsCompatible
summary_values_compatible = $summaryValuesCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z116.remote_workspace_real_adapter_operator_decision_status_summary_compatibility_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_summary_fields = $requiredSummaryFields
required_guardrail_fields = $requiredGuardrailFields
operator_decision_status_summary = $summary
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z116 remote workspace real-adapter operator decision status summary compatibility smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z116 remote workspace real-adapter operator decision status summary compatibility smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,154 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z117-remote-workspace-real-adapter-operator-approval-rejection-outcome-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z117-remote-workspace-real-adapter-operator-approval-rejection-outcome-source-result.json"
$requiredOutcomeFields = @(
"schema_version",
"source_decision_status_schema",
"outcome_status",
"outcome_marker",
"requested_decision",
"enablement_decision",
"operator_review_status",
"enablement_boundary",
"enablement_status",
"runtime_gate_state",
"runtime_effect",
"operator_default_action",
"next_required_phase",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary"
)
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z116-remote-workspace-real-adapter-operator-decision-status-summary-compatibility-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$summary = Get-PropertyValue -Item $sourceResult -Name "operator_decision_status_summary" -Default $null
$guardrails = Get-PropertyValue -Item $summary -Name "guardrail_summary" -Default $null
$outcome = [ordered]@{
schema_version = "rap.remote_workspace_real_adapter_operator_approval_rejection_outcome.v1"
source_decision_status_schema = Get-PropertyValue -Item $summary -Name "schema_version" -Default $null
outcome_status = "rejected_or_not_approved_contract_only"
outcome_marker = "c19z117_real_adapter_operator_outcome_not_approved_contract_only"
requested_decision = Get-PropertyValue -Item $summary -Name "requested_decision" -Default $null
enablement_decision = "not_approved"
operator_review_status = "closed_without_approval"
enablement_boundary = Get-PropertyValue -Item $summary -Name "enablement_boundary" -Default $null
enablement_status = Get-PropertyValue -Item $summary -Name "enablement_status" -Default $null
runtime_gate_state = Get-PropertyValue -Item $summary -Name "runtime_gate_state" -Default $null
runtime_effect = Get-PropertyValue -Item $summary -Name "runtime_effect" -Default $null
operator_default_action = Get-PropertyValue -Item $summary -Name "operator_default_action" -Default $null
next_required_phase = "explicit_operator_reopen_or_new_enablement_request"
allows_process_start = $false
allows_payload_traffic = $false
guardrail_summary = $guardrails
}
$outcomeFieldsCompatible = Test-ObjectHasFields -Item $outcome -Fields $requiredOutcomeFields
$outcomeValuesCompatible = (
[string](Get-PropertyValue -Item $outcome -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_operator_approval_rejection_outcome.v1" -and
[string](Get-PropertyValue -Item $outcome -Name "source_decision_status_schema" -Default "") -eq "rap.remote_workspace_real_adapter_operator_decision_status_summary.v1" -and
[string](Get-PropertyValue -Item $outcome -Name "outcome_status" -Default "") -eq "rejected_or_not_approved_contract_only" -and
[string](Get-PropertyValue -Item $outcome -Name "outcome_marker" -Default "") -eq "c19z117_real_adapter_operator_outcome_not_approved_contract_only" -and
[string](Get-PropertyValue -Item $outcome -Name "requested_decision" -Default "") -eq "review_real_runtime_enablement" -and
[string](Get-PropertyValue -Item $outcome -Name "enablement_decision" -Default "") -eq "not_approved" -and
[string](Get-PropertyValue -Item $outcome -Name "operator_review_status" -Default "") -eq "closed_without_approval" -and
[string](Get-PropertyValue -Item $outcome -Name "enablement_boundary" -Default "") -eq "explicit_operator_enablement_required" -and
[string](Get-PropertyValue -Item $outcome -Name "enablement_status" -Default "") -eq "not_enabled" -and
[string](Get-PropertyValue -Item $outcome -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $outcome -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $outcome -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
[string](Get-PropertyValue -Item $outcome -Name "next_required_phase" -Default "") -eq "explicit_operator_reopen_or_new_enablement_request" -and
-not [bool](Get-PropertyValue -Item $outcome -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $outcome -Name "allows_payload_traffic" -Default $true)
)
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z116.remote_workspace_real_adapter_operator_decision_status_summary_compatibility_smoke.v1")
decision_status_summary_present = ($null -ne $summary)
outcome_fields_compatible = $outcomeFieldsCompatible
outcome_values_compatible = $outcomeValuesCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z117.remote_workspace_real_adapter_operator_approval_rejection_outcome_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_outcome_fields = $requiredOutcomeFields
required_guardrail_fields = $requiredGuardrailFields
operator_approval_rejection_outcome = $outcome
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z117 remote workspace real-adapter operator approval rejection outcome smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z117 remote workspace real-adapter operator approval rejection outcome smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,135 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z118-remote-workspace-real-adapter-operator-approval-rejection-outcome-compatibility-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z118-remote-workspace-real-adapter-operator-approval-rejection-outcome-source-result.json"
$requiredOutcomeFields = @(
"schema_version",
"source_decision_status_schema",
"outcome_status",
"outcome_marker",
"requested_decision",
"enablement_decision",
"operator_review_status",
"enablement_boundary",
"enablement_status",
"runtime_gate_state",
"runtime_effect",
"operator_default_action",
"next_required_phase",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary"
)
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z117-remote-workspace-real-adapter-operator-approval-rejection-outcome-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$outcome = Get-PropertyValue -Item $sourceResult -Name "operator_approval_rejection_outcome" -Default $null
$guardrails = Get-PropertyValue -Item $outcome -Name "guardrail_summary" -Default $null
$outcomeFieldsCompatible = Test-ObjectHasFields -Item $outcome -Fields $requiredOutcomeFields
$outcomeValuesCompatible = (
[string](Get-PropertyValue -Item $outcome -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_operator_approval_rejection_outcome.v1" -and
[string](Get-PropertyValue -Item $outcome -Name "source_decision_status_schema" -Default "") -eq "rap.remote_workspace_real_adapter_operator_decision_status_summary.v1" -and
[string](Get-PropertyValue -Item $outcome -Name "outcome_status" -Default "") -eq "rejected_or_not_approved_contract_only" -and
[string](Get-PropertyValue -Item $outcome -Name "outcome_marker" -Default "") -eq "c19z117_real_adapter_operator_outcome_not_approved_contract_only" -and
[string](Get-PropertyValue -Item $outcome -Name "requested_decision" -Default "") -eq "review_real_runtime_enablement" -and
[string](Get-PropertyValue -Item $outcome -Name "enablement_decision" -Default "") -eq "not_approved" -and
[string](Get-PropertyValue -Item $outcome -Name "operator_review_status" -Default "") -eq "closed_without_approval" -and
[string](Get-PropertyValue -Item $outcome -Name "enablement_boundary" -Default "") -eq "explicit_operator_enablement_required" -and
[string](Get-PropertyValue -Item $outcome -Name "enablement_status" -Default "") -eq "not_enabled" -and
[string](Get-PropertyValue -Item $outcome -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $outcome -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $outcome -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
[string](Get-PropertyValue -Item $outcome -Name "next_required_phase" -Default "") -eq "explicit_operator_reopen_or_new_enablement_request" -and
-not [bool](Get-PropertyValue -Item $outcome -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $outcome -Name "allows_payload_traffic" -Default $true)
)
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z117.remote_workspace_real_adapter_operator_approval_rejection_outcome_smoke.v1")
outcome_present = ($null -ne $outcome)
outcome_fields_compatible = $outcomeFieldsCompatible
outcome_values_compatible = $outcomeValuesCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z118.remote_workspace_real_adapter_operator_approval_rejection_outcome_compatibility_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_outcome_fields = $requiredOutcomeFields
required_guardrail_fields = $requiredGuardrailFields
operator_approval_rejection_outcome = $outcome
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z118 remote workspace real-adapter operator approval rejection outcome compatibility smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z118 remote workspace real-adapter operator approval rejection outcome compatibility smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,157 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z119-remote-workspace-real-adapter-operator-outcome-closeout-reopen-boundary-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z119-remote-workspace-real-adapter-operator-outcome-closeout-reopen-boundary-source-result.json"
$requiredBoundaryFields = @(
"schema_version",
"source_outcome_schema",
"boundary_status",
"boundary_marker",
"closed_outcome_status",
"reopen_policy",
"next_required_phase",
"enablement_decision",
"operator_review_status",
"enablement_boundary",
"enablement_status",
"runtime_gate_state",
"runtime_effect",
"operator_default_action",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary"
)
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z118-remote-workspace-real-adapter-operator-approval-rejection-outcome-compatibility-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$outcome = Get-PropertyValue -Item $sourceResult -Name "operator_approval_rejection_outcome" -Default $null
$guardrails = Get-PropertyValue -Item $outcome -Name "guardrail_summary" -Default $null
$boundary = [ordered]@{
schema_version = "rap.remote_workspace_real_adapter_operator_outcome_closeout_reopen_boundary.v1"
source_outcome_schema = Get-PropertyValue -Item $outcome -Name "schema_version" -Default $null
boundary_status = "closed_not_approved_reopen_required"
boundary_marker = "c19z119_real_adapter_operator_outcome_closeout_reopen_required"
closed_outcome_status = Get-PropertyValue -Item $outcome -Name "outcome_status" -Default $null
reopen_policy = "new_explicit_enablement_request_required"
next_required_phase = "explicit_operator_reopen_or_new_enablement_request"
enablement_decision = Get-PropertyValue -Item $outcome -Name "enablement_decision" -Default $null
operator_review_status = Get-PropertyValue -Item $outcome -Name "operator_review_status" -Default $null
enablement_boundary = Get-PropertyValue -Item $outcome -Name "enablement_boundary" -Default $null
enablement_status = Get-PropertyValue -Item $outcome -Name "enablement_status" -Default $null
runtime_gate_state = Get-PropertyValue -Item $outcome -Name "runtime_gate_state" -Default $null
runtime_effect = Get-PropertyValue -Item $outcome -Name "runtime_effect" -Default $null
operator_default_action = Get-PropertyValue -Item $outcome -Name "operator_default_action" -Default $null
allows_process_start = $false
allows_payload_traffic = $false
guardrail_summary = $guardrails
}
$boundaryFieldsCompatible = Test-ObjectHasFields -Item $boundary -Fields $requiredBoundaryFields
$boundaryValuesCompatible = (
[string](Get-PropertyValue -Item $boundary -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_operator_outcome_closeout_reopen_boundary.v1" -and
[string](Get-PropertyValue -Item $boundary -Name "source_outcome_schema" -Default "") -eq "rap.remote_workspace_real_adapter_operator_approval_rejection_outcome.v1" -and
[string](Get-PropertyValue -Item $boundary -Name "boundary_status" -Default "") -eq "closed_not_approved_reopen_required" -and
[string](Get-PropertyValue -Item $boundary -Name "boundary_marker" -Default "") -eq "c19z119_real_adapter_operator_outcome_closeout_reopen_required" -and
[string](Get-PropertyValue -Item $boundary -Name "closed_outcome_status" -Default "") -eq "rejected_or_not_approved_contract_only" -and
[string](Get-PropertyValue -Item $boundary -Name "reopen_policy" -Default "") -eq "new_explicit_enablement_request_required" -and
[string](Get-PropertyValue -Item $boundary -Name "next_required_phase" -Default "") -eq "explicit_operator_reopen_or_new_enablement_request" -and
[string](Get-PropertyValue -Item $boundary -Name "enablement_decision" -Default "") -eq "not_approved" -and
[string](Get-PropertyValue -Item $boundary -Name "operator_review_status" -Default "") -eq "closed_without_approval" -and
[string](Get-PropertyValue -Item $boundary -Name "enablement_boundary" -Default "") -eq "explicit_operator_enablement_required" -and
[string](Get-PropertyValue -Item $boundary -Name "enablement_status" -Default "") -eq "not_enabled" -and
[string](Get-PropertyValue -Item $boundary -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $boundary -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $boundary -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
-not [bool](Get-PropertyValue -Item $boundary -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $boundary -Name "allows_payload_traffic" -Default $true)
)
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z118.remote_workspace_real_adapter_operator_approval_rejection_outcome_compatibility_smoke.v1")
outcome_present = ($null -ne $outcome)
boundary_fields_compatible = $boundaryFieldsCompatible
boundary_values_compatible = $boundaryValuesCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z119.remote_workspace_real_adapter_operator_outcome_closeout_reopen_boundary_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_boundary_fields = $requiredBoundaryFields
required_guardrail_fields = $requiredGuardrailFields
operator_outcome_closeout_reopen_boundary = $boundary
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z119 remote workspace real-adapter operator outcome closeout reopen boundary smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z119 remote workspace real-adapter operator outcome closeout reopen boundary smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,89 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$EntryNodeName = "test-1",
[string]$ExitNodeName = "test-2",
[string]$EntryBaseUrl = "http://192.168.200.61:19131",
[string]$ResultPath = "artifacts\c19z12-remote-workspace-mailbox-preflight-status-counts-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z12-remote-workspace-mailbox-preflight-status-counts-source-result.json"
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-StatusCounts {
param([object]$Sink, [string]$AdapterSessionID)
if ($null -eq $Sink) { return $false }
$readiness = Get-PropertyValue -Item $Sink -Name "adapter_runtime_readiness" -Default $null
$rollup = Get-PropertyValue -Item $readiness -Name "last_preflight" -Default $null
if ($null -eq $rollup) { return $false }
$readinessStatusCounts = Get-PropertyValue -Item $readiness -Name "mailbox_preflight_operator_status_counts" -Default $null
$readinessSeverityCounts = Get-PropertyValue -Item $readiness -Name "mailbox_preflight_operator_severity_counts" -Default $null
$rollupStatusCounts = Get-PropertyValue -Item $rollup -Name "operator_status_counts" -Default $null
$rollupSeverityCounts = Get-PropertyValue -Item $rollup -Name "operator_severity_counts" -Default $null
return (
[string](Get-PropertyValue -Item $readiness -Name "adapter_session_id" -Default "") -eq $AdapterSessionID -and
[string](Get-PropertyValue -Item $rollup -Name "operator_status" -Default "") -eq "resync_required" -and
[string](Get-PropertyValue -Item $rollup -Name "operator_severity" -Default "") -eq "warn" -and
[int64](Get-PropertyValue -Item $readinessStatusCounts -Name "resync_required" -Default 0) -ge 1 -and
[int64](Get-PropertyValue -Item $readinessSeverityCounts -Name "warn" -Default 0) -ge 1 -and
[int64](Get-PropertyValue -Item $rollupStatusCounts -Name "resync_required" -Default 0) -ge 1 -and
[int64](Get-PropertyValue -Item $rollupSeverityCounts -Name "warn" -Default 0) -ge 1
)
}
$source = & powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z11-remote-workspace-mailbox-preflight-checklist-status-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-EntryNodeName $EntryNodeName `
-ExitNodeName $ExitNodeName `
-EntryBaseUrl $EntryBaseUrl `
-ResultPath $sourceResultPath
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$adapterSessionID = [string](Get-PropertyValue -Item $sourceResult -Name "adapter_session_id" -Default "")
$observed = Get-PropertyValue -Item $sourceResult -Name "observed" -Default $null
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
adapter_session_id_present = ($adapterSessionID -match "^rap-rw-adapter-session-[0-9a-f]{24}$")
workload_status_counts_visible = (Test-StatusCounts -Sink $observed.workload_sink -AdapterSessionID $adapterSessionID)
telemetry_status_counts_visible = (Test-StatusCounts -Sink $observed.telemetry_sink -AdapterSessionID $adapterSessionID)
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z12.remote_workspace_mailbox_preflight_status_counts_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
adapter_session_id = $adapterSessionID
observed = $observed
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z12 remote workspace mailbox preflight status counts smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z12 remote workspace mailbox preflight status counts smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,137 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z120-remote-workspace-real-adapter-operator-outcome-closeout-reopen-boundary-compatibility-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z120-remote-workspace-real-adapter-operator-outcome-closeout-reopen-boundary-source-result.json"
$requiredBoundaryFields = @(
"schema_version",
"source_outcome_schema",
"boundary_status",
"boundary_marker",
"closed_outcome_status",
"reopen_policy",
"next_required_phase",
"enablement_decision",
"operator_review_status",
"enablement_boundary",
"enablement_status",
"runtime_gate_state",
"runtime_effect",
"operator_default_action",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary"
)
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z119-remote-workspace-real-adapter-operator-outcome-closeout-reopen-boundary-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$boundary = Get-PropertyValue -Item $sourceResult -Name "operator_outcome_closeout_reopen_boundary" -Default $null
$guardrails = Get-PropertyValue -Item $boundary -Name "guardrail_summary" -Default $null
$boundaryFieldsCompatible = Test-ObjectHasFields -Item $boundary -Fields $requiredBoundaryFields
$boundaryValuesCompatible = (
[string](Get-PropertyValue -Item $boundary -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_operator_outcome_closeout_reopen_boundary.v1" -and
[string](Get-PropertyValue -Item $boundary -Name "source_outcome_schema" -Default "") -eq "rap.remote_workspace_real_adapter_operator_approval_rejection_outcome.v1" -and
[string](Get-PropertyValue -Item $boundary -Name "boundary_status" -Default "") -eq "closed_not_approved_reopen_required" -and
[string](Get-PropertyValue -Item $boundary -Name "boundary_marker" -Default "") -eq "c19z119_real_adapter_operator_outcome_closeout_reopen_required" -and
[string](Get-PropertyValue -Item $boundary -Name "closed_outcome_status" -Default "") -eq "rejected_or_not_approved_contract_only" -and
[string](Get-PropertyValue -Item $boundary -Name "reopen_policy" -Default "") -eq "new_explicit_enablement_request_required" -and
[string](Get-PropertyValue -Item $boundary -Name "next_required_phase" -Default "") -eq "explicit_operator_reopen_or_new_enablement_request" -and
[string](Get-PropertyValue -Item $boundary -Name "enablement_decision" -Default "") -eq "not_approved" -and
[string](Get-PropertyValue -Item $boundary -Name "operator_review_status" -Default "") -eq "closed_without_approval" -and
[string](Get-PropertyValue -Item $boundary -Name "enablement_boundary" -Default "") -eq "explicit_operator_enablement_required" -and
[string](Get-PropertyValue -Item $boundary -Name "enablement_status" -Default "") -eq "not_enabled" -and
[string](Get-PropertyValue -Item $boundary -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $boundary -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $boundary -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
-not [bool](Get-PropertyValue -Item $boundary -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $boundary -Name "allows_payload_traffic" -Default $true)
)
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z119.remote_workspace_real_adapter_operator_outcome_closeout_reopen_boundary_smoke.v1")
boundary_present = ($null -ne $boundary)
boundary_fields_compatible = $boundaryFieldsCompatible
boundary_values_compatible = $boundaryValuesCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z120.remote_workspace_real_adapter_operator_outcome_closeout_reopen_boundary_compatibility_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_boundary_fields = $requiredBoundaryFields
required_guardrail_fields = $requiredGuardrailFields
operator_outcome_closeout_reopen_boundary = $boundary
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z120 remote workspace real-adapter operator outcome closeout reopen boundary compatibility smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z120 remote workspace real-adapter operator outcome closeout reopen boundary compatibility smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,154 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z121-remote-workspace-real-adapter-not-approved-outcome-release-marker-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z121-remote-workspace-real-adapter-not-approved-outcome-release-marker-source-result.json"
$requiredReleaseFields = @(
"schema_version",
"source_boundary_schema",
"release_status",
"release_marker",
"boundary_status",
"closed_outcome_status",
"reopen_policy",
"enablement_decision",
"operator_review_status",
"enablement_status",
"runtime_gate_state",
"runtime_effect",
"operator_default_action",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary"
)
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z120-remote-workspace-real-adapter-operator-outcome-closeout-reopen-boundary-compatibility-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$boundary = Get-PropertyValue -Item $sourceResult -Name "operator_outcome_closeout_reopen_boundary" -Default $null
$guardrails = Get-PropertyValue -Item $boundary -Name "guardrail_summary" -Default $null
$marker = [ordered]@{
schema_version = "rap.remote_workspace_real_adapter_not_approved_outcome_release_marker.v1"
source_boundary_schema = Get-PropertyValue -Item $boundary -Name "schema_version" -Default $null
release_status = "not_approved_outcome_closed_contract_only"
release_marker = "c19z121_real_adapter_not_approved_outcome_release_marker"
boundary_status = Get-PropertyValue -Item $boundary -Name "boundary_status" -Default $null
closed_outcome_status = Get-PropertyValue -Item $boundary -Name "closed_outcome_status" -Default $null
reopen_policy = Get-PropertyValue -Item $boundary -Name "reopen_policy" -Default $null
enablement_decision = Get-PropertyValue -Item $boundary -Name "enablement_decision" -Default $null
operator_review_status = Get-PropertyValue -Item $boundary -Name "operator_review_status" -Default $null
enablement_status = Get-PropertyValue -Item $boundary -Name "enablement_status" -Default $null
runtime_gate_state = Get-PropertyValue -Item $boundary -Name "runtime_gate_state" -Default $null
runtime_effect = Get-PropertyValue -Item $boundary -Name "runtime_effect" -Default $null
operator_default_action = Get-PropertyValue -Item $boundary -Name "operator_default_action" -Default $null
allows_process_start = $false
allows_payload_traffic = $false
guardrail_summary = $guardrails
}
$releaseFieldsCompatible = Test-ObjectHasFields -Item $marker -Fields $requiredReleaseFields
$releaseValuesCompatible = (
[string](Get-PropertyValue -Item $marker -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_not_approved_outcome_release_marker.v1" -and
[string](Get-PropertyValue -Item $marker -Name "source_boundary_schema" -Default "") -eq "rap.remote_workspace_real_adapter_operator_outcome_closeout_reopen_boundary.v1" -and
[string](Get-PropertyValue -Item $marker -Name "release_status" -Default "") -eq "not_approved_outcome_closed_contract_only" -and
[string](Get-PropertyValue -Item $marker -Name "release_marker" -Default "") -eq "c19z121_real_adapter_not_approved_outcome_release_marker" -and
[string](Get-PropertyValue -Item $marker -Name "boundary_status" -Default "") -eq "closed_not_approved_reopen_required" -and
[string](Get-PropertyValue -Item $marker -Name "closed_outcome_status" -Default "") -eq "rejected_or_not_approved_contract_only" -and
[string](Get-PropertyValue -Item $marker -Name "reopen_policy" -Default "") -eq "new_explicit_enablement_request_required" -and
[string](Get-PropertyValue -Item $marker -Name "enablement_decision" -Default "") -eq "not_approved" -and
[string](Get-PropertyValue -Item $marker -Name "operator_review_status" -Default "") -eq "closed_without_approval" -and
[string](Get-PropertyValue -Item $marker -Name "enablement_status" -Default "") -eq "not_enabled" -and
[string](Get-PropertyValue -Item $marker -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $marker -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $marker -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
-not [bool](Get-PropertyValue -Item $marker -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $marker -Name "allows_payload_traffic" -Default $true)
)
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z120.remote_workspace_real_adapter_operator_outcome_closeout_reopen_boundary_compatibility_smoke.v1")
boundary_present = ($null -ne $boundary)
release_fields_compatible = $releaseFieldsCompatible
release_values_compatible = $releaseValuesCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z121.remote_workspace_real_adapter_not_approved_outcome_release_marker_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_release_fields = $requiredReleaseFields
required_guardrail_fields = $requiredGuardrailFields
not_approved_outcome_release_marker = $marker
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z121 remote workspace real-adapter not-approved outcome release marker smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z121 remote workspace real-adapter not-approved outcome release marker smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,135 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z122-remote-workspace-real-adapter-not-approved-outcome-release-marker-compatibility-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z122-remote-workspace-real-adapter-not-approved-outcome-release-marker-source-result.json"
$requiredReleaseFields = @(
"schema_version",
"source_boundary_schema",
"release_status",
"release_marker",
"boundary_status",
"closed_outcome_status",
"reopen_policy",
"enablement_decision",
"operator_review_status",
"enablement_status",
"runtime_gate_state",
"runtime_effect",
"operator_default_action",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary"
)
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z121-remote-workspace-real-adapter-not-approved-outcome-release-marker-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$marker = Get-PropertyValue -Item $sourceResult -Name "not_approved_outcome_release_marker" -Default $null
$guardrails = Get-PropertyValue -Item $marker -Name "guardrail_summary" -Default $null
$releaseFieldsCompatible = Test-ObjectHasFields -Item $marker -Fields $requiredReleaseFields
$releaseValuesCompatible = (
[string](Get-PropertyValue -Item $marker -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_not_approved_outcome_release_marker.v1" -and
[string](Get-PropertyValue -Item $marker -Name "source_boundary_schema" -Default "") -eq "rap.remote_workspace_real_adapter_operator_outcome_closeout_reopen_boundary.v1" -and
[string](Get-PropertyValue -Item $marker -Name "release_status" -Default "") -eq "not_approved_outcome_closed_contract_only" -and
[string](Get-PropertyValue -Item $marker -Name "release_marker" -Default "") -eq "c19z121_real_adapter_not_approved_outcome_release_marker" -and
[string](Get-PropertyValue -Item $marker -Name "boundary_status" -Default "") -eq "closed_not_approved_reopen_required" -and
[string](Get-PropertyValue -Item $marker -Name "closed_outcome_status" -Default "") -eq "rejected_or_not_approved_contract_only" -and
[string](Get-PropertyValue -Item $marker -Name "reopen_policy" -Default "") -eq "new_explicit_enablement_request_required" -and
[string](Get-PropertyValue -Item $marker -Name "enablement_decision" -Default "") -eq "not_approved" -and
[string](Get-PropertyValue -Item $marker -Name "operator_review_status" -Default "") -eq "closed_without_approval" -and
[string](Get-PropertyValue -Item $marker -Name "enablement_status" -Default "") -eq "not_enabled" -and
[string](Get-PropertyValue -Item $marker -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $marker -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $marker -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
-not [bool](Get-PropertyValue -Item $marker -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $marker -Name "allows_payload_traffic" -Default $true)
)
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z121.remote_workspace_real_adapter_not_approved_outcome_release_marker_smoke.v1")
release_marker_present = ($null -ne $marker)
release_fields_compatible = $releaseFieldsCompatible
release_values_compatible = $releaseValuesCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z122.remote_workspace_real_adapter_not_approved_outcome_release_marker_compatibility_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_release_fields = $requiredReleaseFields
required_guardrail_fields = $requiredGuardrailFields
not_approved_outcome_release_marker = $marker
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z122 remote workspace real-adapter not-approved outcome release marker compatibility smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z122 remote workspace real-adapter not-approved outcome release marker compatibility smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,185 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z123-remote-workspace-real-adapter-not-approved-outcome-package-index-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z123-remote-workspace-real-adapter-not-approved-outcome-package-index-source-result.json"
$requiredPackageFields = @(
"schema_version",
"source_release_marker_schema",
"package_status",
"package_marker",
"covered_stage_range",
"covered_stage_count",
"latest_compatibility_stage",
"release_status",
"boundary_status",
"reopen_policy",
"enablement_decision",
"operator_review_status",
"enablement_status",
"runtime_gate_state",
"runtime_effect",
"operator_default_action",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary",
"closeout_notes"
)
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
$requiredCloseoutNotes = @(
"not_approved_outcome_package_indexed",
"operator_outcome_closed_without_approval",
"new_explicit_enablement_request_required",
"real_runtime_gate_not_enabled",
"process_start_disabled",
"payload_forwarding_disabled"
)
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
function Test-ArrayContainsAll {
param([object[]]$Actual, [string[]]$Required)
$values = @($Actual | ForEach-Object { [string]$_ })
foreach ($item in $Required) {
if ($values -notcontains $item) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z122-remote-workspace-real-adapter-not-approved-outcome-release-marker-compatibility-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$marker = Get-PropertyValue -Item $sourceResult -Name "not_approved_outcome_release_marker" -Default $null
$guardrails = Get-PropertyValue -Item $marker -Name "guardrail_summary" -Default $null
$packageIndex = [ordered]@{
schema_version = "rap.remote_workspace_real_adapter_not_approved_outcome_package_index.v1"
source_release_marker_schema = Get-PropertyValue -Item $marker -Name "schema_version" -Default $null
package_status = "closed_not_approved_contract_only"
package_marker = "c19z123_real_adapter_not_approved_outcome_package_index"
covered_stage_range = "C19Z117-C19Z122"
covered_stage_count = 6
latest_compatibility_stage = "C19Z122"
release_status = Get-PropertyValue -Item $marker -Name "release_status" -Default $null
boundary_status = Get-PropertyValue -Item $marker -Name "boundary_status" -Default $null
reopen_policy = Get-PropertyValue -Item $marker -Name "reopen_policy" -Default $null
enablement_decision = Get-PropertyValue -Item $marker -Name "enablement_decision" -Default $null
operator_review_status = Get-PropertyValue -Item $marker -Name "operator_review_status" -Default $null
enablement_status = Get-PropertyValue -Item $marker -Name "enablement_status" -Default $null
runtime_gate_state = Get-PropertyValue -Item $marker -Name "runtime_gate_state" -Default $null
runtime_effect = Get-PropertyValue -Item $marker -Name "runtime_effect" -Default $null
operator_default_action = Get-PropertyValue -Item $marker -Name "operator_default_action" -Default $null
allows_process_start = $false
allows_payload_traffic = $false
guardrail_summary = $guardrails
closeout_notes = $requiredCloseoutNotes
}
$packageFieldsCompatible = Test-ObjectHasFields -Item $packageIndex -Fields $requiredPackageFields
$closeoutNotesCompatible = Test-ArrayContainsAll -Actual @(Get-PropertyValue -Item $packageIndex -Name "closeout_notes" -Default @()) -Required $requiredCloseoutNotes
$packageValuesCompatible = (
[string](Get-PropertyValue -Item $packageIndex -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_not_approved_outcome_package_index.v1" -and
[string](Get-PropertyValue -Item $packageIndex -Name "source_release_marker_schema" -Default "") -eq "rap.remote_workspace_real_adapter_not_approved_outcome_release_marker.v1" -and
[string](Get-PropertyValue -Item $packageIndex -Name "package_status" -Default "") -eq "closed_not_approved_contract_only" -and
[string](Get-PropertyValue -Item $packageIndex -Name "package_marker" -Default "") -eq "c19z123_real_adapter_not_approved_outcome_package_index" -and
[string](Get-PropertyValue -Item $packageIndex -Name "covered_stage_range" -Default "") -eq "C19Z117-C19Z122" -and
[int](Get-PropertyValue -Item $packageIndex -Name "covered_stage_count" -Default -1) -eq 6 -and
[string](Get-PropertyValue -Item $packageIndex -Name "latest_compatibility_stage" -Default "") -eq "C19Z122" -and
[string](Get-PropertyValue -Item $packageIndex -Name "release_status" -Default "") -eq "not_approved_outcome_closed_contract_only" -and
[string](Get-PropertyValue -Item $packageIndex -Name "boundary_status" -Default "") -eq "closed_not_approved_reopen_required" -and
[string](Get-PropertyValue -Item $packageIndex -Name "reopen_policy" -Default "") -eq "new_explicit_enablement_request_required" -and
[string](Get-PropertyValue -Item $packageIndex -Name "enablement_decision" -Default "") -eq "not_approved" -and
[string](Get-PropertyValue -Item $packageIndex -Name "operator_review_status" -Default "") -eq "closed_without_approval" -and
[string](Get-PropertyValue -Item $packageIndex -Name "enablement_status" -Default "") -eq "not_enabled" -and
[string](Get-PropertyValue -Item $packageIndex -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $packageIndex -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $packageIndex -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
-not [bool](Get-PropertyValue -Item $packageIndex -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $packageIndex -Name "allows_payload_traffic" -Default $true)
)
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z122.remote_workspace_real_adapter_not_approved_outcome_release_marker_compatibility_smoke.v1")
release_marker_present = ($null -ne $marker)
package_fields_compatible = $packageFieldsCompatible
package_values_compatible = $packageValuesCompatible
closeout_notes_compatible = $closeoutNotesCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z123.remote_workspace_real_adapter_not_approved_outcome_package_index_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_package_fields = $requiredPackageFields
required_guardrail_fields = $requiredGuardrailFields
required_closeout_notes = $requiredCloseoutNotes
not_approved_outcome_package_index = $packageIndex
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z123 remote workspace real-adapter not-approved outcome package index smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z123 remote workspace real-adapter not-approved outcome package index smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,163 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z124-remote-workspace-real-adapter-not-approved-outcome-package-index-compatibility-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z124-remote-workspace-real-adapter-not-approved-outcome-package-index-source-result.json"
$requiredPackageFields = @(
"schema_version",
"source_release_marker_schema",
"package_status",
"package_marker",
"covered_stage_range",
"covered_stage_count",
"latest_compatibility_stage",
"release_status",
"boundary_status",
"reopen_policy",
"enablement_decision",
"operator_review_status",
"enablement_status",
"runtime_gate_state",
"runtime_effect",
"operator_default_action",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary",
"closeout_notes"
)
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
$requiredCloseoutNotes = @(
"not_approved_outcome_package_indexed",
"operator_outcome_closed_without_approval",
"new_explicit_enablement_request_required",
"real_runtime_gate_not_enabled",
"process_start_disabled",
"payload_forwarding_disabled"
)
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
function Test-ArrayContainsAll {
param([object[]]$Actual, [string[]]$Required)
$values = @($Actual | ForEach-Object { [string]$_ })
foreach ($item in $Required) {
if ($values -notcontains $item) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z123-remote-workspace-real-adapter-not-approved-outcome-package-index-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$packageIndex = Get-PropertyValue -Item $sourceResult -Name "not_approved_outcome_package_index" -Default $null
$guardrails = Get-PropertyValue -Item $packageIndex -Name "guardrail_summary" -Default $null
$closeoutNotes = @(Get-PropertyValue -Item $packageIndex -Name "closeout_notes" -Default @())
$packageFieldsCompatible = Test-ObjectHasFields -Item $packageIndex -Fields $requiredPackageFields
$closeoutNotesCompatible = Test-ArrayContainsAll -Actual $closeoutNotes -Required $requiredCloseoutNotes
$packageValuesCompatible = (
[string](Get-PropertyValue -Item $packageIndex -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_not_approved_outcome_package_index.v1" -and
[string](Get-PropertyValue -Item $packageIndex -Name "source_release_marker_schema" -Default "") -eq "rap.remote_workspace_real_adapter_not_approved_outcome_release_marker.v1" -and
[string](Get-PropertyValue -Item $packageIndex -Name "package_status" -Default "") -eq "closed_not_approved_contract_only" -and
[string](Get-PropertyValue -Item $packageIndex -Name "package_marker" -Default "") -eq "c19z123_real_adapter_not_approved_outcome_package_index" -and
[string](Get-PropertyValue -Item $packageIndex -Name "covered_stage_range" -Default "") -eq "C19Z117-C19Z122" -and
[int](Get-PropertyValue -Item $packageIndex -Name "covered_stage_count" -Default -1) -eq 6 -and
[string](Get-PropertyValue -Item $packageIndex -Name "latest_compatibility_stage" -Default "") -eq "C19Z122" -and
[string](Get-PropertyValue -Item $packageIndex -Name "release_status" -Default "") -eq "not_approved_outcome_closed_contract_only" -and
[string](Get-PropertyValue -Item $packageIndex -Name "boundary_status" -Default "") -eq "closed_not_approved_reopen_required" -and
[string](Get-PropertyValue -Item $packageIndex -Name "reopen_policy" -Default "") -eq "new_explicit_enablement_request_required" -and
[string](Get-PropertyValue -Item $packageIndex -Name "enablement_decision" -Default "") -eq "not_approved" -and
[string](Get-PropertyValue -Item $packageIndex -Name "operator_review_status" -Default "") -eq "closed_without_approval" -and
[string](Get-PropertyValue -Item $packageIndex -Name "enablement_status" -Default "") -eq "not_enabled" -and
[string](Get-PropertyValue -Item $packageIndex -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $packageIndex -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $packageIndex -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
-not [bool](Get-PropertyValue -Item $packageIndex -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $packageIndex -Name "allows_payload_traffic" -Default $true)
)
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z123.remote_workspace_real_adapter_not_approved_outcome_package_index_smoke.v1")
package_index_present = ($null -ne $packageIndex)
package_fields_compatible = $packageFieldsCompatible
package_values_compatible = $packageValuesCompatible
closeout_notes_compatible = $closeoutNotesCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z124.remote_workspace_real_adapter_not_approved_outcome_package_index_compatibility_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_package_fields = $requiredPackageFields
required_guardrail_fields = $requiredGuardrailFields
required_closeout_notes = $requiredCloseoutNotes
not_approved_outcome_package_index = $packageIndex
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z124 remote workspace real-adapter not-approved outcome package index compatibility smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z124 remote workspace real-adapter not-approved outcome package index compatibility smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,166 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z125-remote-workspace-real-adapter-not-approved-outcome-closeout-summary-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z125-remote-workspace-real-adapter-not-approved-outcome-closeout-summary-source-result.json"
$requiredCloseoutFields = @(
"schema_version",
"source_package_index_schema",
"closeout_status",
"closeout_marker",
"covered_stage_range",
"covered_stage_count",
"latest_compatibility_stage",
"release_status",
"boundary_status",
"reopen_policy",
"enablement_decision",
"operator_review_status",
"enablement_status",
"runtime_gate_state",
"runtime_effect",
"operator_default_action",
"next_required_phase",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary"
)
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z124-remote-workspace-real-adapter-not-approved-outcome-package-index-compatibility-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$packageIndex = Get-PropertyValue -Item $sourceResult -Name "not_approved_outcome_package_index" -Default $null
$guardrails = Get-PropertyValue -Item $packageIndex -Name "guardrail_summary" -Default $null
$closeout = [ordered]@{
schema_version = "rap.remote_workspace_real_adapter_not_approved_outcome_closeout_summary.v1"
source_package_index_schema = Get-PropertyValue -Item $packageIndex -Name "schema_version" -Default $null
closeout_status = "closed_not_approved_package_complete"
closeout_marker = "c19z125_real_adapter_not_approved_outcome_closed_contract_only"
covered_stage_range = Get-PropertyValue -Item $packageIndex -Name "covered_stage_range" -Default $null
covered_stage_count = Get-PropertyValue -Item $packageIndex -Name "covered_stage_count" -Default $null
latest_compatibility_stage = Get-PropertyValue -Item $packageIndex -Name "latest_compatibility_stage" -Default $null
release_status = Get-PropertyValue -Item $packageIndex -Name "release_status" -Default $null
boundary_status = Get-PropertyValue -Item $packageIndex -Name "boundary_status" -Default $null
reopen_policy = Get-PropertyValue -Item $packageIndex -Name "reopen_policy" -Default $null
enablement_decision = Get-PropertyValue -Item $packageIndex -Name "enablement_decision" -Default $null
operator_review_status = Get-PropertyValue -Item $packageIndex -Name "operator_review_status" -Default $null
enablement_status = Get-PropertyValue -Item $packageIndex -Name "enablement_status" -Default $null
runtime_gate_state = Get-PropertyValue -Item $packageIndex -Name "runtime_gate_state" -Default $null
runtime_effect = Get-PropertyValue -Item $packageIndex -Name "runtime_effect" -Default $null
operator_default_action = Get-PropertyValue -Item $packageIndex -Name "operator_default_action" -Default $null
next_required_phase = "explicit_new_enablement_request_only"
allows_process_start = $false
allows_payload_traffic = $false
guardrail_summary = $guardrails
}
$closeoutFieldsCompatible = Test-ObjectHasFields -Item $closeout -Fields $requiredCloseoutFields
$closeoutValuesCompatible = (
[string](Get-PropertyValue -Item $closeout -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_not_approved_outcome_closeout_summary.v1" -and
[string](Get-PropertyValue -Item $closeout -Name "source_package_index_schema" -Default "") -eq "rap.remote_workspace_real_adapter_not_approved_outcome_package_index.v1" -and
[string](Get-PropertyValue -Item $closeout -Name "closeout_status" -Default "") -eq "closed_not_approved_package_complete" -and
[string](Get-PropertyValue -Item $closeout -Name "closeout_marker" -Default "") -eq "c19z125_real_adapter_not_approved_outcome_closed_contract_only" -and
[string](Get-PropertyValue -Item $closeout -Name "covered_stage_range" -Default "") -eq "C19Z117-C19Z122" -and
[int](Get-PropertyValue -Item $closeout -Name "covered_stage_count" -Default -1) -eq 6 -and
[string](Get-PropertyValue -Item $closeout -Name "latest_compatibility_stage" -Default "") -eq "C19Z122" -and
[string](Get-PropertyValue -Item $closeout -Name "release_status" -Default "") -eq "not_approved_outcome_closed_contract_only" -and
[string](Get-PropertyValue -Item $closeout -Name "boundary_status" -Default "") -eq "closed_not_approved_reopen_required" -and
[string](Get-PropertyValue -Item $closeout -Name "reopen_policy" -Default "") -eq "new_explicit_enablement_request_required" -and
[string](Get-PropertyValue -Item $closeout -Name "enablement_decision" -Default "") -eq "not_approved" -and
[string](Get-PropertyValue -Item $closeout -Name "operator_review_status" -Default "") -eq "closed_without_approval" -and
[string](Get-PropertyValue -Item $closeout -Name "enablement_status" -Default "") -eq "not_enabled" -and
[string](Get-PropertyValue -Item $closeout -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $closeout -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $closeout -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
[string](Get-PropertyValue -Item $closeout -Name "next_required_phase" -Default "") -eq "explicit_new_enablement_request_only" -and
-not [bool](Get-PropertyValue -Item $closeout -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $closeout -Name "allows_payload_traffic" -Default $true)
)
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z124.remote_workspace_real_adapter_not_approved_outcome_package_index_compatibility_smoke.v1")
package_index_present = ($null -ne $packageIndex)
closeout_fields_compatible = $closeoutFieldsCompatible
closeout_values_compatible = $closeoutValuesCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z125.remote_workspace_real_adapter_not_approved_outcome_closeout_summary_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_closeout_fields = $requiredCloseoutFields
required_guardrail_fields = $requiredGuardrailFields
not_approved_outcome_closeout_summary = $closeout
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z125 remote workspace real-adapter not-approved outcome closeout summary smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z125 remote workspace real-adapter not-approved outcome closeout summary smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,143 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z126-remote-workspace-real-adapter-not-approved-outcome-closeout-summary-compatibility-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z126-remote-workspace-real-adapter-not-approved-outcome-closeout-summary-source-result.json"
$requiredCloseoutFields = @(
"schema_version",
"source_package_index_schema",
"closeout_status",
"closeout_marker",
"covered_stage_range",
"covered_stage_count",
"latest_compatibility_stage",
"release_status",
"boundary_status",
"reopen_policy",
"enablement_decision",
"operator_review_status",
"enablement_status",
"runtime_gate_state",
"runtime_effect",
"operator_default_action",
"next_required_phase",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary"
)
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z125-remote-workspace-real-adapter-not-approved-outcome-closeout-summary-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$closeout = Get-PropertyValue -Item $sourceResult -Name "not_approved_outcome_closeout_summary" -Default $null
$guardrails = Get-PropertyValue -Item $closeout -Name "guardrail_summary" -Default $null
$closeoutFieldsCompatible = Test-ObjectHasFields -Item $closeout -Fields $requiredCloseoutFields
$closeoutValuesCompatible = (
[string](Get-PropertyValue -Item $closeout -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_not_approved_outcome_closeout_summary.v1" -and
[string](Get-PropertyValue -Item $closeout -Name "source_package_index_schema" -Default "") -eq "rap.remote_workspace_real_adapter_not_approved_outcome_package_index.v1" -and
[string](Get-PropertyValue -Item $closeout -Name "closeout_status" -Default "") -eq "closed_not_approved_package_complete" -and
[string](Get-PropertyValue -Item $closeout -Name "closeout_marker" -Default "") -eq "c19z125_real_adapter_not_approved_outcome_closed_contract_only" -and
[string](Get-PropertyValue -Item $closeout -Name "covered_stage_range" -Default "") -eq "C19Z117-C19Z122" -and
[int](Get-PropertyValue -Item $closeout -Name "covered_stage_count" -Default -1) -eq 6 -and
[string](Get-PropertyValue -Item $closeout -Name "latest_compatibility_stage" -Default "") -eq "C19Z122" -and
[string](Get-PropertyValue -Item $closeout -Name "release_status" -Default "") -eq "not_approved_outcome_closed_contract_only" -and
[string](Get-PropertyValue -Item $closeout -Name "boundary_status" -Default "") -eq "closed_not_approved_reopen_required" -and
[string](Get-PropertyValue -Item $closeout -Name "reopen_policy" -Default "") -eq "new_explicit_enablement_request_required" -and
[string](Get-PropertyValue -Item $closeout -Name "enablement_decision" -Default "") -eq "not_approved" -and
[string](Get-PropertyValue -Item $closeout -Name "operator_review_status" -Default "") -eq "closed_without_approval" -and
[string](Get-PropertyValue -Item $closeout -Name "enablement_status" -Default "") -eq "not_enabled" -and
[string](Get-PropertyValue -Item $closeout -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $closeout -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $closeout -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
[string](Get-PropertyValue -Item $closeout -Name "next_required_phase" -Default "") -eq "explicit_new_enablement_request_only" -and
-not [bool](Get-PropertyValue -Item $closeout -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $closeout -Name "allows_payload_traffic" -Default $true)
)
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z125.remote_workspace_real_adapter_not_approved_outcome_closeout_summary_smoke.v1")
closeout_summary_present = ($null -ne $closeout)
closeout_fields_compatible = $closeoutFieldsCompatible
closeout_values_compatible = $closeoutValuesCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z126.remote_workspace_real_adapter_not_approved_outcome_closeout_summary_compatibility_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_closeout_fields = $requiredCloseoutFields
required_guardrail_fields = $requiredGuardrailFields
not_approved_outcome_closeout_summary = $closeout
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z126 remote workspace real-adapter not-approved outcome closeout summary compatibility smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z126 remote workspace real-adapter not-approved outcome closeout summary compatibility smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,190 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z127-remote-workspace-real-adapter-not-approved-outcome-final-release-marker-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z127-remote-workspace-real-adapter-not-approved-outcome-final-release-marker-source-result.json"
$requiredFinalReleaseFields = @(
"schema_version",
"source_closeout_schema",
"final_release_status",
"final_release_marker",
"covered_stage_range",
"covered_stage_count",
"latest_compatibility_stage",
"release_status",
"closeout_status",
"boundary_status",
"reopen_policy",
"enablement_decision",
"operator_review_status",
"enablement_status",
"runtime_gate_state",
"runtime_effect",
"operator_default_action",
"next_required_phase",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary",
"final_notes"
)
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
$requiredFinalNotes = @(
"not_approved_outcome_final_release_marked",
"operator_outcome_closed_without_approval",
"new_explicit_enablement_request_required",
"real_runtime_gate_not_enabled",
"process_start_disabled",
"payload_forwarding_disabled"
)
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
function Test-ArrayContainsAll {
param([object[]]$Actual, [string[]]$Expected)
foreach ($item in $Expected) {
if ($Actual -notcontains $item) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z126-remote-workspace-real-adapter-not-approved-outcome-closeout-summary-compatibility-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$closeout = Get-PropertyValue -Item $sourceResult -Name "not_approved_outcome_closeout_summary" -Default $null
$guardrails = Get-PropertyValue -Item $closeout -Name "guardrail_summary" -Default $null
$marker = [ordered]@{
schema_version = "rap.remote_workspace_real_adapter_not_approved_outcome_final_release_marker.v1"
source_closeout_schema = Get-PropertyValue -Item $closeout -Name "schema_version" -Default $null
final_release_status = "closed_not_approved_final_contract_only"
final_release_marker = "c19z127_real_adapter_not_approved_outcome_final_release_marker"
covered_stage_range = Get-PropertyValue -Item $closeout -Name "covered_stage_range" -Default $null
covered_stage_count = Get-PropertyValue -Item $closeout -Name "covered_stage_count" -Default $null
latest_compatibility_stage = Get-PropertyValue -Item $closeout -Name "latest_compatibility_stage" -Default $null
release_status = Get-PropertyValue -Item $closeout -Name "release_status" -Default $null
closeout_status = Get-PropertyValue -Item $closeout -Name "closeout_status" -Default $null
boundary_status = Get-PropertyValue -Item $closeout -Name "boundary_status" -Default $null
reopen_policy = Get-PropertyValue -Item $closeout -Name "reopen_policy" -Default $null
enablement_decision = Get-PropertyValue -Item $closeout -Name "enablement_decision" -Default $null
operator_review_status = Get-PropertyValue -Item $closeout -Name "operator_review_status" -Default $null
enablement_status = Get-PropertyValue -Item $closeout -Name "enablement_status" -Default $null
runtime_gate_state = Get-PropertyValue -Item $closeout -Name "runtime_gate_state" -Default $null
runtime_effect = Get-PropertyValue -Item $closeout -Name "runtime_effect" -Default $null
operator_default_action = Get-PropertyValue -Item $closeout -Name "operator_default_action" -Default $null
next_required_phase = Get-PropertyValue -Item $closeout -Name "next_required_phase" -Default $null
allows_process_start = $false
allows_payload_traffic = $false
guardrail_summary = $guardrails
final_notes = $requiredFinalNotes
}
$finalReleaseFieldsCompatible = Test-ObjectHasFields -Item $marker -Fields $requiredFinalReleaseFields
$finalReleaseValuesCompatible = (
[string](Get-PropertyValue -Item $marker -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_not_approved_outcome_final_release_marker.v1" -and
[string](Get-PropertyValue -Item $marker -Name "source_closeout_schema" -Default "") -eq "rap.remote_workspace_real_adapter_not_approved_outcome_closeout_summary.v1" -and
[string](Get-PropertyValue -Item $marker -Name "final_release_status" -Default "") -eq "closed_not_approved_final_contract_only" -and
[string](Get-PropertyValue -Item $marker -Name "final_release_marker" -Default "") -eq "c19z127_real_adapter_not_approved_outcome_final_release_marker" -and
[string](Get-PropertyValue -Item $marker -Name "covered_stage_range" -Default "") -eq "C19Z117-C19Z122" -and
[int](Get-PropertyValue -Item $marker -Name "covered_stage_count" -Default -1) -eq 6 -and
[string](Get-PropertyValue -Item $marker -Name "latest_compatibility_stage" -Default "") -eq "C19Z122" -and
[string](Get-PropertyValue -Item $marker -Name "release_status" -Default "") -eq "not_approved_outcome_closed_contract_only" -and
[string](Get-PropertyValue -Item $marker -Name "closeout_status" -Default "") -eq "closed_not_approved_package_complete" -and
[string](Get-PropertyValue -Item $marker -Name "boundary_status" -Default "") -eq "closed_not_approved_reopen_required" -and
[string](Get-PropertyValue -Item $marker -Name "reopen_policy" -Default "") -eq "new_explicit_enablement_request_required" -and
[string](Get-PropertyValue -Item $marker -Name "enablement_decision" -Default "") -eq "not_approved" -and
[string](Get-PropertyValue -Item $marker -Name "operator_review_status" -Default "") -eq "closed_without_approval" -and
[string](Get-PropertyValue -Item $marker -Name "enablement_status" -Default "") -eq "not_enabled" -and
[string](Get-PropertyValue -Item $marker -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $marker -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $marker -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
[string](Get-PropertyValue -Item $marker -Name "next_required_phase" -Default "") -eq "explicit_new_enablement_request_only" -and
-not [bool](Get-PropertyValue -Item $marker -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $marker -Name "allows_payload_traffic" -Default $true)
)
$finalNotesCompatible = Test-ArrayContainsAll -Actual @($marker.final_notes) -Expected $requiredFinalNotes
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z126.remote_workspace_real_adapter_not_approved_outcome_closeout_summary_compatibility_smoke.v1")
closeout_summary_present = ($null -ne $closeout)
final_release_fields_compatible = $finalReleaseFieldsCompatible
final_release_values_compatible = $finalReleaseValuesCompatible
final_notes_compatible = $finalNotesCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z127.remote_workspace_real_adapter_not_approved_outcome_final_release_marker_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_final_release_fields = $requiredFinalReleaseFields
required_guardrail_fields = $requiredGuardrailFields
required_final_notes = $requiredFinalNotes
not_approved_outcome_final_release_marker = $marker
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z127 remote workspace real-adapter not-approved outcome final release marker smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z127 remote workspace real-adapter not-approved outcome final release marker smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,166 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z128-remote-workspace-real-adapter-not-approved-outcome-final-release-marker-compatibility-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z128-remote-workspace-real-adapter-not-approved-outcome-final-release-marker-source-result.json"
$requiredFinalReleaseFields = @(
"schema_version",
"source_closeout_schema",
"final_release_status",
"final_release_marker",
"covered_stage_range",
"covered_stage_count",
"latest_compatibility_stage",
"release_status",
"closeout_status",
"boundary_status",
"reopen_policy",
"enablement_decision",
"operator_review_status",
"enablement_status",
"runtime_gate_state",
"runtime_effect",
"operator_default_action",
"next_required_phase",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary",
"final_notes"
)
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
$requiredFinalNotes = @(
"not_approved_outcome_final_release_marked",
"operator_outcome_closed_without_approval",
"new_explicit_enablement_request_required",
"real_runtime_gate_not_enabled",
"process_start_disabled",
"payload_forwarding_disabled"
)
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
function Test-ArrayContainsAll {
param([object[]]$Actual, [string[]]$Expected)
foreach ($item in $Expected) {
if ($Actual -notcontains $item) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z127-remote-workspace-real-adapter-not-approved-outcome-final-release-marker-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$marker = Get-PropertyValue -Item $sourceResult -Name "not_approved_outcome_final_release_marker" -Default $null
$guardrails = Get-PropertyValue -Item $marker -Name "guardrail_summary" -Default $null
$finalNotes = @(Get-PropertyValue -Item $marker -Name "final_notes" -Default @())
$finalReleaseFieldsCompatible = Test-ObjectHasFields -Item $marker -Fields $requiredFinalReleaseFields
$finalReleaseValuesCompatible = (
[string](Get-PropertyValue -Item $marker -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_not_approved_outcome_final_release_marker.v1" -and
[string](Get-PropertyValue -Item $marker -Name "source_closeout_schema" -Default "") -eq "rap.remote_workspace_real_adapter_not_approved_outcome_closeout_summary.v1" -and
[string](Get-PropertyValue -Item $marker -Name "final_release_status" -Default "") -eq "closed_not_approved_final_contract_only" -and
[string](Get-PropertyValue -Item $marker -Name "final_release_marker" -Default "") -eq "c19z127_real_adapter_not_approved_outcome_final_release_marker" -and
[string](Get-PropertyValue -Item $marker -Name "covered_stage_range" -Default "") -eq "C19Z117-C19Z122" -and
[int](Get-PropertyValue -Item $marker -Name "covered_stage_count" -Default -1) -eq 6 -and
[string](Get-PropertyValue -Item $marker -Name "latest_compatibility_stage" -Default "") -eq "C19Z122" -and
[string](Get-PropertyValue -Item $marker -Name "release_status" -Default "") -eq "not_approved_outcome_closed_contract_only" -and
[string](Get-PropertyValue -Item $marker -Name "closeout_status" -Default "") -eq "closed_not_approved_package_complete" -and
[string](Get-PropertyValue -Item $marker -Name "boundary_status" -Default "") -eq "closed_not_approved_reopen_required" -and
[string](Get-PropertyValue -Item $marker -Name "reopen_policy" -Default "") -eq "new_explicit_enablement_request_required" -and
[string](Get-PropertyValue -Item $marker -Name "enablement_decision" -Default "") -eq "not_approved" -and
[string](Get-PropertyValue -Item $marker -Name "operator_review_status" -Default "") -eq "closed_without_approval" -and
[string](Get-PropertyValue -Item $marker -Name "enablement_status" -Default "") -eq "not_enabled" -and
[string](Get-PropertyValue -Item $marker -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $marker -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $marker -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
[string](Get-PropertyValue -Item $marker -Name "next_required_phase" -Default "") -eq "explicit_new_enablement_request_only" -and
-not [bool](Get-PropertyValue -Item $marker -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $marker -Name "allows_payload_traffic" -Default $true)
)
$finalNotesCompatible = Test-ArrayContainsAll -Actual $finalNotes -Expected $requiredFinalNotes
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z127.remote_workspace_real_adapter_not_approved_outcome_final_release_marker_smoke.v1")
final_release_marker_present = ($null -ne $marker)
final_release_fields_compatible = $finalReleaseFieldsCompatible
final_release_values_compatible = $finalReleaseValuesCompatible
final_notes_compatible = $finalNotesCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z128.remote_workspace_real_adapter_not_approved_outcome_final_release_marker_compatibility_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_final_release_fields = $requiredFinalReleaseFields
required_guardrail_fields = $requiredGuardrailFields
required_final_notes = $requiredFinalNotes
not_approved_outcome_final_release_marker = $marker
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z128 remote workspace real-adapter not-approved outcome final release marker compatibility smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z128 remote workspace real-adapter not-approved outcome final release marker compatibility smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,197 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z129-remote-workspace-real-adapter-not-approved-outcome-final-package-index-archive-marker-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z129-remote-workspace-real-adapter-not-approved-outcome-final-package-index-archive-marker-source-result.json"
$requiredArchiveFields = @(
"schema_version",
"source_final_release_schema",
"archive_status",
"archive_marker",
"package_status",
"final_release_status",
"covered_stage_range",
"covered_stage_count",
"latest_compatibility_stage",
"release_status",
"closeout_status",
"boundary_status",
"reopen_policy",
"enablement_decision",
"operator_review_status",
"enablement_status",
"runtime_gate_state",
"runtime_effect",
"operator_default_action",
"next_required_phase",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary",
"archive_notes"
)
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
$requiredArchiveNotes = @(
"not_approved_outcome_final_package_indexed",
"not_approved_outcome_archived_contract_only",
"operator_outcome_closed_without_approval",
"new_explicit_enablement_request_required",
"real_runtime_gate_not_enabled",
"process_start_disabled",
"payload_forwarding_disabled"
)
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
function Test-ArrayContainsAll {
param([object[]]$Actual, [string[]]$Expected)
foreach ($item in $Expected) {
if ($Actual -notcontains $item) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z128-remote-workspace-real-adapter-not-approved-outcome-final-release-marker-compatibility-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$finalRelease = Get-PropertyValue -Item $sourceResult -Name "not_approved_outcome_final_release_marker" -Default $null
$guardrails = Get-PropertyValue -Item $finalRelease -Name "guardrail_summary" -Default $null
$archive = [ordered]@{
schema_version = "rap.remote_workspace_real_adapter_not_approved_outcome_final_package_index_archive_marker.v1"
source_final_release_schema = Get-PropertyValue -Item $finalRelease -Name "schema_version" -Default $null
archive_status = "closed_not_approved_archived_contract_only"
archive_marker = "c19z129_real_adapter_not_approved_outcome_final_package_index_archive_marker"
package_status = "final_package_indexed_and_archived_contract_only"
final_release_status = Get-PropertyValue -Item $finalRelease -Name "final_release_status" -Default $null
covered_stage_range = Get-PropertyValue -Item $finalRelease -Name "covered_stage_range" -Default $null
covered_stage_count = Get-PropertyValue -Item $finalRelease -Name "covered_stage_count" -Default $null
latest_compatibility_stage = Get-PropertyValue -Item $finalRelease -Name "latest_compatibility_stage" -Default $null
release_status = Get-PropertyValue -Item $finalRelease -Name "release_status" -Default $null
closeout_status = Get-PropertyValue -Item $finalRelease -Name "closeout_status" -Default $null
boundary_status = Get-PropertyValue -Item $finalRelease -Name "boundary_status" -Default $null
reopen_policy = Get-PropertyValue -Item $finalRelease -Name "reopen_policy" -Default $null
enablement_decision = Get-PropertyValue -Item $finalRelease -Name "enablement_decision" -Default $null
operator_review_status = Get-PropertyValue -Item $finalRelease -Name "operator_review_status" -Default $null
enablement_status = Get-PropertyValue -Item $finalRelease -Name "enablement_status" -Default $null
runtime_gate_state = Get-PropertyValue -Item $finalRelease -Name "runtime_gate_state" -Default $null
runtime_effect = Get-PropertyValue -Item $finalRelease -Name "runtime_effect" -Default $null
operator_default_action = Get-PropertyValue -Item $finalRelease -Name "operator_default_action" -Default $null
next_required_phase = Get-PropertyValue -Item $finalRelease -Name "next_required_phase" -Default $null
allows_process_start = $false
allows_payload_traffic = $false
guardrail_summary = $guardrails
archive_notes = $requiredArchiveNotes
}
$archiveFieldsCompatible = Test-ObjectHasFields -Item $archive -Fields $requiredArchiveFields
$archiveValuesCompatible = (
[string](Get-PropertyValue -Item $archive -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_not_approved_outcome_final_package_index_archive_marker.v1" -and
[string](Get-PropertyValue -Item $archive -Name "source_final_release_schema" -Default "") -eq "rap.remote_workspace_real_adapter_not_approved_outcome_final_release_marker.v1" -and
[string](Get-PropertyValue -Item $archive -Name "archive_status" -Default "") -eq "closed_not_approved_archived_contract_only" -and
[string](Get-PropertyValue -Item $archive -Name "archive_marker" -Default "") -eq "c19z129_real_adapter_not_approved_outcome_final_package_index_archive_marker" -and
[string](Get-PropertyValue -Item $archive -Name "package_status" -Default "") -eq "final_package_indexed_and_archived_contract_only" -and
[string](Get-PropertyValue -Item $archive -Name "final_release_status" -Default "") -eq "closed_not_approved_final_contract_only" -and
[string](Get-PropertyValue -Item $archive -Name "covered_stage_range" -Default "") -eq "C19Z117-C19Z122" -and
[int](Get-PropertyValue -Item $archive -Name "covered_stage_count" -Default -1) -eq 6 -and
[string](Get-PropertyValue -Item $archive -Name "latest_compatibility_stage" -Default "") -eq "C19Z122" -and
[string](Get-PropertyValue -Item $archive -Name "release_status" -Default "") -eq "not_approved_outcome_closed_contract_only" -and
[string](Get-PropertyValue -Item $archive -Name "closeout_status" -Default "") -eq "closed_not_approved_package_complete" -and
[string](Get-PropertyValue -Item $archive -Name "boundary_status" -Default "") -eq "closed_not_approved_reopen_required" -and
[string](Get-PropertyValue -Item $archive -Name "reopen_policy" -Default "") -eq "new_explicit_enablement_request_required" -and
[string](Get-PropertyValue -Item $archive -Name "enablement_decision" -Default "") -eq "not_approved" -and
[string](Get-PropertyValue -Item $archive -Name "operator_review_status" -Default "") -eq "closed_without_approval" -and
[string](Get-PropertyValue -Item $archive -Name "enablement_status" -Default "") -eq "not_enabled" -and
[string](Get-PropertyValue -Item $archive -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $archive -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $archive -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
[string](Get-PropertyValue -Item $archive -Name "next_required_phase" -Default "") -eq "explicit_new_enablement_request_only" -and
-not [bool](Get-PropertyValue -Item $archive -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $archive -Name "allows_payload_traffic" -Default $true)
)
$archiveNotesCompatible = Test-ArrayContainsAll -Actual @($archive.archive_notes) -Expected $requiredArchiveNotes
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z128.remote_workspace_real_adapter_not_approved_outcome_final_release_marker_compatibility_smoke.v1")
final_release_marker_present = ($null -ne $finalRelease)
archive_fields_compatible = $archiveFieldsCompatible
archive_values_compatible = $archiveValuesCompatible
archive_notes_compatible = $archiveNotesCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z129.remote_workspace_real_adapter_not_approved_outcome_final_package_index_archive_marker_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_archive_fields = $requiredArchiveFields
required_guardrail_fields = $requiredGuardrailFields
required_archive_notes = $requiredArchiveNotes
not_approved_outcome_final_package_index_archive_marker = $archive
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z129 remote workspace real-adapter not-approved outcome final package index archive marker smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z129 remote workspace real-adapter not-approved outcome final package index archive marker smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,83 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$EntryNodeName = "test-1",
[string]$ExitNodeName = "test-2",
[string]$EntryBaseUrl = "http://192.168.200.61:19131",
[string]$ResultPath = "artifacts\c19z13-remote-workspace-mailbox-preflight-attention-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z13-remote-workspace-mailbox-preflight-attention-source-result.json"
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-AttentionStatus {
param([object]$Sink, [string]$AdapterSessionID)
if ($null -eq $Sink) { return $false }
$readiness = Get-PropertyValue -Item $Sink -Name "adapter_runtime_readiness" -Default $null
$rollup = Get-PropertyValue -Item $readiness -Name "last_preflight" -Default $null
if ($null -eq $rollup) { return $false }
return (
[string](Get-PropertyValue -Item $readiness -Name "adapter_session_id" -Default "") -eq $AdapterSessionID -and
[string](Get-PropertyValue -Item $readiness -Name "preflight_attention_status" -Default "") -eq "needs_attention" -and
[string](Get-PropertyValue -Item $rollup -Name "preflight_attention_status" -Default "") -eq "needs_attention" -and
[string](Get-PropertyValue -Item $rollup -Name "operator_status" -Default "") -eq "resync_required" -and
[string](Get-PropertyValue -Item $rollup -Name "operator_severity" -Default "") -eq "warn"
)
}
$source = & powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z12-remote-workspace-mailbox-preflight-status-counts-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-EntryNodeName $EntryNodeName `
-ExitNodeName $ExitNodeName `
-EntryBaseUrl $EntryBaseUrl `
-ResultPath $sourceResultPath
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$adapterSessionID = [string](Get-PropertyValue -Item $sourceResult -Name "adapter_session_id" -Default "")
$observed = Get-PropertyValue -Item $sourceResult -Name "observed" -Default $null
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
adapter_session_id_present = ($adapterSessionID -match "^rap-rw-adapter-session-[0-9a-f]{24}$")
workload_attention_visible = (Test-AttentionStatus -Sink $observed.workload_sink -AdapterSessionID $adapterSessionID)
telemetry_attention_visible = (Test-AttentionStatus -Sink $observed.telemetry_sink -AdapterSessionID $adapterSessionID)
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z13.remote_workspace_mailbox_preflight_attention_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
adapter_session_id = $adapterSessionID
observed = $observed
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z13 remote workspace mailbox preflight attention smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z13 remote workspace mailbox preflight attention smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,171 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z130-remote-workspace-real-adapter-not-approved-outcome-final-package-index-archive-marker-compatibility-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z130-remote-workspace-real-adapter-not-approved-outcome-final-package-index-archive-marker-source-result.json"
$requiredArchiveFields = @(
"schema_version",
"source_final_release_schema",
"archive_status",
"archive_marker",
"package_status",
"final_release_status",
"covered_stage_range",
"covered_stage_count",
"latest_compatibility_stage",
"release_status",
"closeout_status",
"boundary_status",
"reopen_policy",
"enablement_decision",
"operator_review_status",
"enablement_status",
"runtime_gate_state",
"runtime_effect",
"operator_default_action",
"next_required_phase",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary",
"archive_notes"
)
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
$requiredArchiveNotes = @(
"not_approved_outcome_final_package_indexed",
"not_approved_outcome_archived_contract_only",
"operator_outcome_closed_without_approval",
"new_explicit_enablement_request_required",
"real_runtime_gate_not_enabled",
"process_start_disabled",
"payload_forwarding_disabled"
)
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
function Test-ArrayContainsAll {
param([object[]]$Actual, [string[]]$Expected)
foreach ($item in $Expected) {
if ($Actual -notcontains $item) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z129-remote-workspace-real-adapter-not-approved-outcome-final-package-index-archive-marker-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$archive = Get-PropertyValue -Item $sourceResult -Name "not_approved_outcome_final_package_index_archive_marker" -Default $null
$guardrails = Get-PropertyValue -Item $archive -Name "guardrail_summary" -Default $null
$archiveNotes = @(Get-PropertyValue -Item $archive -Name "archive_notes" -Default @())
$archiveFieldsCompatible = Test-ObjectHasFields -Item $archive -Fields $requiredArchiveFields
$archiveValuesCompatible = (
[string](Get-PropertyValue -Item $archive -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_not_approved_outcome_final_package_index_archive_marker.v1" -and
[string](Get-PropertyValue -Item $archive -Name "source_final_release_schema" -Default "") -eq "rap.remote_workspace_real_adapter_not_approved_outcome_final_release_marker.v1" -and
[string](Get-PropertyValue -Item $archive -Name "archive_status" -Default "") -eq "closed_not_approved_archived_contract_only" -and
[string](Get-PropertyValue -Item $archive -Name "archive_marker" -Default "") -eq "c19z129_real_adapter_not_approved_outcome_final_package_index_archive_marker" -and
[string](Get-PropertyValue -Item $archive -Name "package_status" -Default "") -eq "final_package_indexed_and_archived_contract_only" -and
[string](Get-PropertyValue -Item $archive -Name "final_release_status" -Default "") -eq "closed_not_approved_final_contract_only" -and
[string](Get-PropertyValue -Item $archive -Name "covered_stage_range" -Default "") -eq "C19Z117-C19Z122" -and
[int](Get-PropertyValue -Item $archive -Name "covered_stage_count" -Default -1) -eq 6 -and
[string](Get-PropertyValue -Item $archive -Name "latest_compatibility_stage" -Default "") -eq "C19Z122" -and
[string](Get-PropertyValue -Item $archive -Name "release_status" -Default "") -eq "not_approved_outcome_closed_contract_only" -and
[string](Get-PropertyValue -Item $archive -Name "closeout_status" -Default "") -eq "closed_not_approved_package_complete" -and
[string](Get-PropertyValue -Item $archive -Name "boundary_status" -Default "") -eq "closed_not_approved_reopen_required" -and
[string](Get-PropertyValue -Item $archive -Name "reopen_policy" -Default "") -eq "new_explicit_enablement_request_required" -and
[string](Get-PropertyValue -Item $archive -Name "enablement_decision" -Default "") -eq "not_approved" -and
[string](Get-PropertyValue -Item $archive -Name "operator_review_status" -Default "") -eq "closed_without_approval" -and
[string](Get-PropertyValue -Item $archive -Name "enablement_status" -Default "") -eq "not_enabled" -and
[string](Get-PropertyValue -Item $archive -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $archive -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $archive -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
[string](Get-PropertyValue -Item $archive -Name "next_required_phase" -Default "") -eq "explicit_new_enablement_request_only" -and
-not [bool](Get-PropertyValue -Item $archive -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $archive -Name "allows_payload_traffic" -Default $true)
)
$archiveNotesCompatible = Test-ArrayContainsAll -Actual $archiveNotes -Expected $requiredArchiveNotes
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z129.remote_workspace_real_adapter_not_approved_outcome_final_package_index_archive_marker_smoke.v1")
archive_marker_present = ($null -ne $archive)
archive_fields_compatible = $archiveFieldsCompatible
archive_values_compatible = $archiveValuesCompatible
archive_notes_compatible = $archiveNotesCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z130.remote_workspace_real_adapter_not_approved_outcome_final_package_index_archive_marker_compatibility_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_archive_fields = $requiredArchiveFields
required_guardrail_fields = $requiredGuardrailFields
required_archive_notes = $requiredArchiveNotes
not_approved_outcome_final_package_index_archive_marker = $archive
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z130 remote workspace real-adapter not-approved outcome final package index archive marker compatibility smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z130 remote workspace real-adapter not-approved outcome final package index archive marker compatibility smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,200 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z131-remote-workspace-real-adapter-not-approved-outcome-archive-closeout-manifest-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z131-remote-workspace-real-adapter-not-approved-outcome-archive-closeout-manifest-source-result.json"
$requiredManifestFields = @(
"schema_version",
"source_archive_schema",
"manifest_status",
"manifest_marker",
"archive_status",
"package_status",
"final_release_status",
"covered_stage_range",
"covered_stage_count",
"latest_compatibility_stage",
"release_status",
"closeout_status",
"boundary_status",
"reopen_policy",
"enablement_decision",
"operator_review_status",
"enablement_status",
"runtime_gate_state",
"runtime_effect",
"operator_default_action",
"next_required_phase",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary",
"manifest_notes"
)
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
$requiredManifestNotes = @(
"not_approved_archive_closeout_manifested",
"not_approved_branch_closed_until_new_request",
"operator_outcome_closed_without_approval",
"new_explicit_enablement_request_required",
"real_runtime_gate_not_enabled",
"process_start_disabled",
"payload_forwarding_disabled"
)
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
function Test-ArrayContainsAll {
param([object[]]$Actual, [string[]]$Expected)
foreach ($item in $Expected) {
if ($Actual -notcontains $item) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z130-remote-workspace-real-adapter-not-approved-outcome-final-package-index-archive-marker-compatibility-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$archive = Get-PropertyValue -Item $sourceResult -Name "not_approved_outcome_final_package_index_archive_marker" -Default $null
$guardrails = Get-PropertyValue -Item $archive -Name "guardrail_summary" -Default $null
$manifest = [ordered]@{
schema_version = "rap.remote_workspace_real_adapter_not_approved_outcome_archive_closeout_manifest.v1"
source_archive_schema = Get-PropertyValue -Item $archive -Name "schema_version" -Default $null
manifest_status = "closed_not_approved_archive_manifest_complete"
manifest_marker = "c19z131_real_adapter_not_approved_outcome_archive_closeout_manifest"
archive_status = Get-PropertyValue -Item $archive -Name "archive_status" -Default $null
package_status = Get-PropertyValue -Item $archive -Name "package_status" -Default $null
final_release_status = Get-PropertyValue -Item $archive -Name "final_release_status" -Default $null
covered_stage_range = Get-PropertyValue -Item $archive -Name "covered_stage_range" -Default $null
covered_stage_count = Get-PropertyValue -Item $archive -Name "covered_stage_count" -Default $null
latest_compatibility_stage = Get-PropertyValue -Item $archive -Name "latest_compatibility_stage" -Default $null
release_status = Get-PropertyValue -Item $archive -Name "release_status" -Default $null
closeout_status = Get-PropertyValue -Item $archive -Name "closeout_status" -Default $null
boundary_status = Get-PropertyValue -Item $archive -Name "boundary_status" -Default $null
reopen_policy = Get-PropertyValue -Item $archive -Name "reopen_policy" -Default $null
enablement_decision = Get-PropertyValue -Item $archive -Name "enablement_decision" -Default $null
operator_review_status = Get-PropertyValue -Item $archive -Name "operator_review_status" -Default $null
enablement_status = Get-PropertyValue -Item $archive -Name "enablement_status" -Default $null
runtime_gate_state = Get-PropertyValue -Item $archive -Name "runtime_gate_state" -Default $null
runtime_effect = Get-PropertyValue -Item $archive -Name "runtime_effect" -Default $null
operator_default_action = Get-PropertyValue -Item $archive -Name "operator_default_action" -Default $null
next_required_phase = Get-PropertyValue -Item $archive -Name "next_required_phase" -Default $null
allows_process_start = $false
allows_payload_traffic = $false
guardrail_summary = $guardrails
manifest_notes = $requiredManifestNotes
}
$manifestFieldsCompatible = Test-ObjectHasFields -Item $manifest -Fields $requiredManifestFields
$manifestValuesCompatible = (
[string](Get-PropertyValue -Item $manifest -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_not_approved_outcome_archive_closeout_manifest.v1" -and
[string](Get-PropertyValue -Item $manifest -Name "source_archive_schema" -Default "") -eq "rap.remote_workspace_real_adapter_not_approved_outcome_final_package_index_archive_marker.v1" -and
[string](Get-PropertyValue -Item $manifest -Name "manifest_status" -Default "") -eq "closed_not_approved_archive_manifest_complete" -and
[string](Get-PropertyValue -Item $manifest -Name "manifest_marker" -Default "") -eq "c19z131_real_adapter_not_approved_outcome_archive_closeout_manifest" -and
[string](Get-PropertyValue -Item $manifest -Name "archive_status" -Default "") -eq "closed_not_approved_archived_contract_only" -and
[string](Get-PropertyValue -Item $manifest -Name "package_status" -Default "") -eq "final_package_indexed_and_archived_contract_only" -and
[string](Get-PropertyValue -Item $manifest -Name "final_release_status" -Default "") -eq "closed_not_approved_final_contract_only" -and
[string](Get-PropertyValue -Item $manifest -Name "covered_stage_range" -Default "") -eq "C19Z117-C19Z122" -and
[int](Get-PropertyValue -Item $manifest -Name "covered_stage_count" -Default -1) -eq 6 -and
[string](Get-PropertyValue -Item $manifest -Name "latest_compatibility_stage" -Default "") -eq "C19Z122" -and
[string](Get-PropertyValue -Item $manifest -Name "release_status" -Default "") -eq "not_approved_outcome_closed_contract_only" -and
[string](Get-PropertyValue -Item $manifest -Name "closeout_status" -Default "") -eq "closed_not_approved_package_complete" -and
[string](Get-PropertyValue -Item $manifest -Name "boundary_status" -Default "") -eq "closed_not_approved_reopen_required" -and
[string](Get-PropertyValue -Item $manifest -Name "reopen_policy" -Default "") -eq "new_explicit_enablement_request_required" -and
[string](Get-PropertyValue -Item $manifest -Name "enablement_decision" -Default "") -eq "not_approved" -and
[string](Get-PropertyValue -Item $manifest -Name "operator_review_status" -Default "") -eq "closed_without_approval" -and
[string](Get-PropertyValue -Item $manifest -Name "enablement_status" -Default "") -eq "not_enabled" -and
[string](Get-PropertyValue -Item $manifest -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $manifest -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $manifest -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
[string](Get-PropertyValue -Item $manifest -Name "next_required_phase" -Default "") -eq "explicit_new_enablement_request_only" -and
-not [bool](Get-PropertyValue -Item $manifest -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $manifest -Name "allows_payload_traffic" -Default $true)
)
$manifestNotesCompatible = Test-ArrayContainsAll -Actual @($manifest.manifest_notes) -Expected $requiredManifestNotes
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z130.remote_workspace_real_adapter_not_approved_outcome_final_package_index_archive_marker_compatibility_smoke.v1")
archive_marker_present = ($null -ne $archive)
manifest_fields_compatible = $manifestFieldsCompatible
manifest_values_compatible = $manifestValuesCompatible
manifest_notes_compatible = $manifestNotesCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z131.remote_workspace_real_adapter_not_approved_outcome_archive_closeout_manifest_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_manifest_fields = $requiredManifestFields
required_guardrail_fields = $requiredGuardrailFields
required_manifest_notes = $requiredManifestNotes
not_approved_outcome_archive_closeout_manifest = $manifest
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z131 remote workspace real-adapter not-approved outcome archive closeout manifest smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z131 remote workspace real-adapter not-approved outcome archive closeout manifest smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,173 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z132-remote-workspace-real-adapter-not-approved-outcome-archive-closeout-manifest-compatibility-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z132-remote-workspace-real-adapter-not-approved-outcome-archive-closeout-manifest-source-result.json"
$requiredManifestFields = @(
"schema_version",
"source_archive_schema",
"manifest_status",
"manifest_marker",
"archive_status",
"package_status",
"final_release_status",
"covered_stage_range",
"covered_stage_count",
"latest_compatibility_stage",
"release_status",
"closeout_status",
"boundary_status",
"reopen_policy",
"enablement_decision",
"operator_review_status",
"enablement_status",
"runtime_gate_state",
"runtime_effect",
"operator_default_action",
"next_required_phase",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary",
"manifest_notes"
)
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
$requiredManifestNotes = @(
"not_approved_archive_closeout_manifested",
"not_approved_branch_closed_until_new_request",
"operator_outcome_closed_without_approval",
"new_explicit_enablement_request_required",
"real_runtime_gate_not_enabled",
"process_start_disabled",
"payload_forwarding_disabled"
)
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
function Test-ArrayContainsAll {
param([object[]]$Actual, [string[]]$Expected)
foreach ($item in $Expected) {
if ($Actual -notcontains $item) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z131-remote-workspace-real-adapter-not-approved-outcome-archive-closeout-manifest-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$manifest = Get-PropertyValue -Item $sourceResult -Name "not_approved_outcome_archive_closeout_manifest" -Default $null
$guardrails = Get-PropertyValue -Item $manifest -Name "guardrail_summary" -Default $null
$manifestNotes = @(Get-PropertyValue -Item $manifest -Name "manifest_notes" -Default @())
$manifestFieldsCompatible = Test-ObjectHasFields -Item $manifest -Fields $requiredManifestFields
$manifestValuesCompatible = (
[string](Get-PropertyValue -Item $manifest -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_not_approved_outcome_archive_closeout_manifest.v1" -and
[string](Get-PropertyValue -Item $manifest -Name "source_archive_schema" -Default "") -eq "rap.remote_workspace_real_adapter_not_approved_outcome_final_package_index_archive_marker.v1" -and
[string](Get-PropertyValue -Item $manifest -Name "manifest_status" -Default "") -eq "closed_not_approved_archive_manifest_complete" -and
[string](Get-PropertyValue -Item $manifest -Name "manifest_marker" -Default "") -eq "c19z131_real_adapter_not_approved_outcome_archive_closeout_manifest" -and
[string](Get-PropertyValue -Item $manifest -Name "archive_status" -Default "") -eq "closed_not_approved_archived_contract_only" -and
[string](Get-PropertyValue -Item $manifest -Name "package_status" -Default "") -eq "final_package_indexed_and_archived_contract_only" -and
[string](Get-PropertyValue -Item $manifest -Name "final_release_status" -Default "") -eq "closed_not_approved_final_contract_only" -and
[string](Get-PropertyValue -Item $manifest -Name "covered_stage_range" -Default "") -eq "C19Z117-C19Z122" -and
[int](Get-PropertyValue -Item $manifest -Name "covered_stage_count" -Default -1) -eq 6 -and
[string](Get-PropertyValue -Item $manifest -Name "latest_compatibility_stage" -Default "") -eq "C19Z122" -and
[string](Get-PropertyValue -Item $manifest -Name "release_status" -Default "") -eq "not_approved_outcome_closed_contract_only" -and
[string](Get-PropertyValue -Item $manifest -Name "closeout_status" -Default "") -eq "closed_not_approved_package_complete" -and
[string](Get-PropertyValue -Item $manifest -Name "boundary_status" -Default "") -eq "closed_not_approved_reopen_required" -and
[string](Get-PropertyValue -Item $manifest -Name "reopen_policy" -Default "") -eq "new_explicit_enablement_request_required" -and
[string](Get-PropertyValue -Item $manifest -Name "enablement_decision" -Default "") -eq "not_approved" -and
[string](Get-PropertyValue -Item $manifest -Name "operator_review_status" -Default "") -eq "closed_without_approval" -and
[string](Get-PropertyValue -Item $manifest -Name "enablement_status" -Default "") -eq "not_enabled" -and
[string](Get-PropertyValue -Item $manifest -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $manifest -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $manifest -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
[string](Get-PropertyValue -Item $manifest -Name "next_required_phase" -Default "") -eq "explicit_new_enablement_request_only" -and
-not [bool](Get-PropertyValue -Item $manifest -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $manifest -Name "allows_payload_traffic" -Default $true)
)
$manifestNotesCompatible = Test-ArrayContainsAll -Actual $manifestNotes -Expected $requiredManifestNotes
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z131.remote_workspace_real_adapter_not_approved_outcome_archive_closeout_manifest_smoke.v1")
manifest_present = ($null -ne $manifest)
manifest_fields_compatible = $manifestFieldsCompatible
manifest_values_compatible = $manifestValuesCompatible
manifest_notes_compatible = $manifestNotesCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z132.remote_workspace_real_adapter_not_approved_outcome_archive_closeout_manifest_compatibility_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_manifest_fields = $requiredManifestFields
required_guardrail_fields = $requiredGuardrailFields
required_manifest_notes = $requiredManifestNotes
not_approved_outcome_archive_closeout_manifest = $manifest
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z132 remote workspace real-adapter not-approved outcome archive closeout manifest compatibility smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z132 remote workspace real-adapter not-approved outcome archive closeout manifest compatibility smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,208 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z133-remote-workspace-real-adapter-not-approved-outcome-stopped-branch-sentinel-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z133-remote-workspace-real-adapter-not-approved-outcome-stopped-branch-sentinel-source-result.json"
$requiredSentinelFields = @(
"schema_version",
"source_manifest_schema",
"sentinel_status",
"sentinel_marker",
"branch_state",
"continuation_policy",
"manifest_status",
"archive_status",
"package_status",
"final_release_status",
"covered_stage_range",
"covered_stage_count",
"latest_compatibility_stage",
"release_status",
"closeout_status",
"boundary_status",
"reopen_policy",
"enablement_decision",
"operator_review_status",
"enablement_status",
"runtime_gate_state",
"runtime_effect",
"operator_default_action",
"next_required_phase",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary",
"sentinel_notes"
)
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
$requiredSentinelNotes = @(
"not_approved_branch_stopped",
"no_further_not_approved_layers_required",
"new_explicit_enablement_request_required",
"real_runtime_gate_not_enabled",
"process_start_disabled",
"payload_forwarding_disabled"
)
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
function Test-ArrayContainsAll {
param([object[]]$Actual, [string[]]$Expected)
foreach ($item in $Expected) {
if ($Actual -notcontains $item) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z132-remote-workspace-real-adapter-not-approved-outcome-archive-closeout-manifest-compatibility-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$manifest = Get-PropertyValue -Item $sourceResult -Name "not_approved_outcome_archive_closeout_manifest" -Default $null
$guardrails = Get-PropertyValue -Item $manifest -Name "guardrail_summary" -Default $null
$sentinel = [ordered]@{
schema_version = "rap.remote_workspace_real_adapter_not_approved_outcome_stopped_branch_sentinel.v1"
source_manifest_schema = Get-PropertyValue -Item $manifest -Name "schema_version" -Default $null
sentinel_status = "stopped_until_new_explicit_enablement_request"
sentinel_marker = "c19z133_real_adapter_not_approved_outcome_stopped_branch_sentinel"
branch_state = "not_approved_branch_closed"
continuation_policy = "do_not_continue_without_new_explicit_enablement_request"
manifest_status = Get-PropertyValue -Item $manifest -Name "manifest_status" -Default $null
archive_status = Get-PropertyValue -Item $manifest -Name "archive_status" -Default $null
package_status = Get-PropertyValue -Item $manifest -Name "package_status" -Default $null
final_release_status = Get-PropertyValue -Item $manifest -Name "final_release_status" -Default $null
covered_stage_range = Get-PropertyValue -Item $manifest -Name "covered_stage_range" -Default $null
covered_stage_count = Get-PropertyValue -Item $manifest -Name "covered_stage_count" -Default $null
latest_compatibility_stage = Get-PropertyValue -Item $manifest -Name "latest_compatibility_stage" -Default $null
release_status = Get-PropertyValue -Item $manifest -Name "release_status" -Default $null
closeout_status = Get-PropertyValue -Item $manifest -Name "closeout_status" -Default $null
boundary_status = Get-PropertyValue -Item $manifest -Name "boundary_status" -Default $null
reopen_policy = Get-PropertyValue -Item $manifest -Name "reopen_policy" -Default $null
enablement_decision = Get-PropertyValue -Item $manifest -Name "enablement_decision" -Default $null
operator_review_status = Get-PropertyValue -Item $manifest -Name "operator_review_status" -Default $null
enablement_status = Get-PropertyValue -Item $manifest -Name "enablement_status" -Default $null
runtime_gate_state = Get-PropertyValue -Item $manifest -Name "runtime_gate_state" -Default $null
runtime_effect = Get-PropertyValue -Item $manifest -Name "runtime_effect" -Default $null
operator_default_action = Get-PropertyValue -Item $manifest -Name "operator_default_action" -Default $null
next_required_phase = Get-PropertyValue -Item $manifest -Name "next_required_phase" -Default $null
allows_process_start = $false
allows_payload_traffic = $false
guardrail_summary = $guardrails
sentinel_notes = $requiredSentinelNotes
}
$sentinelFieldsCompatible = Test-ObjectHasFields -Item $sentinel -Fields $requiredSentinelFields
$sentinelValuesCompatible = (
[string](Get-PropertyValue -Item $sentinel -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_not_approved_outcome_stopped_branch_sentinel.v1" -and
[string](Get-PropertyValue -Item $sentinel -Name "source_manifest_schema" -Default "") -eq "rap.remote_workspace_real_adapter_not_approved_outcome_archive_closeout_manifest.v1" -and
[string](Get-PropertyValue -Item $sentinel -Name "sentinel_status" -Default "") -eq "stopped_until_new_explicit_enablement_request" -and
[string](Get-PropertyValue -Item $sentinel -Name "sentinel_marker" -Default "") -eq "c19z133_real_adapter_not_approved_outcome_stopped_branch_sentinel" -and
[string](Get-PropertyValue -Item $sentinel -Name "branch_state" -Default "") -eq "not_approved_branch_closed" -and
[string](Get-PropertyValue -Item $sentinel -Name "continuation_policy" -Default "") -eq "do_not_continue_without_new_explicit_enablement_request" -and
[string](Get-PropertyValue -Item $sentinel -Name "manifest_status" -Default "") -eq "closed_not_approved_archive_manifest_complete" -and
[string](Get-PropertyValue -Item $sentinel -Name "archive_status" -Default "") -eq "closed_not_approved_archived_contract_only" -and
[string](Get-PropertyValue -Item $sentinel -Name "package_status" -Default "") -eq "final_package_indexed_and_archived_contract_only" -and
[string](Get-PropertyValue -Item $sentinel -Name "final_release_status" -Default "") -eq "closed_not_approved_final_contract_only" -and
[string](Get-PropertyValue -Item $sentinel -Name "covered_stage_range" -Default "") -eq "C19Z117-C19Z122" -and
[int](Get-PropertyValue -Item $sentinel -Name "covered_stage_count" -Default -1) -eq 6 -and
[string](Get-PropertyValue -Item $sentinel -Name "latest_compatibility_stage" -Default "") -eq "C19Z122" -and
[string](Get-PropertyValue -Item $sentinel -Name "release_status" -Default "") -eq "not_approved_outcome_closed_contract_only" -and
[string](Get-PropertyValue -Item $sentinel -Name "closeout_status" -Default "") -eq "closed_not_approved_package_complete" -and
[string](Get-PropertyValue -Item $sentinel -Name "boundary_status" -Default "") -eq "closed_not_approved_reopen_required" -and
[string](Get-PropertyValue -Item $sentinel -Name "reopen_policy" -Default "") -eq "new_explicit_enablement_request_required" -and
[string](Get-PropertyValue -Item $sentinel -Name "enablement_decision" -Default "") -eq "not_approved" -and
[string](Get-PropertyValue -Item $sentinel -Name "operator_review_status" -Default "") -eq "closed_without_approval" -and
[string](Get-PropertyValue -Item $sentinel -Name "enablement_status" -Default "") -eq "not_enabled" -and
[string](Get-PropertyValue -Item $sentinel -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $sentinel -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $sentinel -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
[string](Get-PropertyValue -Item $sentinel -Name "next_required_phase" -Default "") -eq "explicit_new_enablement_request_only" -and
-not [bool](Get-PropertyValue -Item $sentinel -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $sentinel -Name "allows_payload_traffic" -Default $true)
)
$sentinelNotesCompatible = Test-ArrayContainsAll -Actual @($sentinel.sentinel_notes) -Expected $requiredSentinelNotes
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z132.remote_workspace_real_adapter_not_approved_outcome_archive_closeout_manifest_compatibility_smoke.v1")
manifest_present = ($null -ne $manifest)
sentinel_fields_compatible = $sentinelFieldsCompatible
sentinel_values_compatible = $sentinelValuesCompatible
sentinel_notes_compatible = $sentinelNotesCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z133.remote_workspace_real_adapter_not_approved_outcome_stopped_branch_sentinel_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_sentinel_fields = $requiredSentinelFields
required_guardrail_fields = $requiredGuardrailFields
required_sentinel_notes = $requiredSentinelNotes
not_approved_outcome_stopped_branch_sentinel = $sentinel
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z133 remote workspace real-adapter not-approved outcome stopped branch sentinel smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z133 remote workspace real-adapter not-approved outcome stopped branch sentinel smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,142 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z134-remote-workspace-real-adapter-not-approved-outcome-stopped-branch-sentinel-compatibility-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z134-remote-workspace-real-adapter-not-approved-outcome-stopped-branch-sentinel-source-result.json"
$requiredSentinelFields = @("schema_version", "source_manifest_schema", "sentinel_status", "sentinel_marker", "branch_state", "continuation_policy", "manifest_status", "archive_status", "package_status", "final_release_status", "covered_stage_range", "covered_stage_count", "latest_compatibility_stage", "release_status", "closeout_status", "boundary_status", "reopen_policy", "enablement_decision", "operator_review_status", "enablement_status", "runtime_gate_state", "runtime_effect", "operator_default_action", "next_required_phase", "allows_process_start", "allows_payload_traffic", "guardrail_summary", "sentinel_notes")
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
$requiredSentinelNotes = @("not_approved_branch_stopped", "no_further_not_approved_layers_required", "new_explicit_enablement_request_required", "real_runtime_gate_not_enabled", "process_start_disabled", "payload_forwarding_disabled")
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
function Test-ArrayContainsAll {
param([object[]]$Actual, [string[]]$Expected)
foreach ($item in $Expected) {
if ($Actual -notcontains $item) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z133-remote-workspace-real-adapter-not-approved-outcome-stopped-branch-sentinel-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$sentinel = Get-PropertyValue -Item $sourceResult -Name "not_approved_outcome_stopped_branch_sentinel" -Default $null
$guardrails = Get-PropertyValue -Item $sentinel -Name "guardrail_summary" -Default $null
$sentinelNotes = @(Get-PropertyValue -Item $sentinel -Name "sentinel_notes" -Default @())
$sentinelFieldsCompatible = Test-ObjectHasFields -Item $sentinel -Fields $requiredSentinelFields
$sentinelValuesCompatible = (
[string](Get-PropertyValue -Item $sentinel -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_not_approved_outcome_stopped_branch_sentinel.v1" -and
[string](Get-PropertyValue -Item $sentinel -Name "source_manifest_schema" -Default "") -eq "rap.remote_workspace_real_adapter_not_approved_outcome_archive_closeout_manifest.v1" -and
[string](Get-PropertyValue -Item $sentinel -Name "sentinel_status" -Default "") -eq "stopped_until_new_explicit_enablement_request" -and
[string](Get-PropertyValue -Item $sentinel -Name "sentinel_marker" -Default "") -eq "c19z133_real_adapter_not_approved_outcome_stopped_branch_sentinel" -and
[string](Get-PropertyValue -Item $sentinel -Name "branch_state" -Default "") -eq "not_approved_branch_closed" -and
[string](Get-PropertyValue -Item $sentinel -Name "continuation_policy" -Default "") -eq "do_not_continue_without_new_explicit_enablement_request" -and
[string](Get-PropertyValue -Item $sentinel -Name "manifest_status" -Default "") -eq "closed_not_approved_archive_manifest_complete" -and
[string](Get-PropertyValue -Item $sentinel -Name "archive_status" -Default "") -eq "closed_not_approved_archived_contract_only" -and
[string](Get-PropertyValue -Item $sentinel -Name "package_status" -Default "") -eq "final_package_indexed_and_archived_contract_only" -and
[string](Get-PropertyValue -Item $sentinel -Name "final_release_status" -Default "") -eq "closed_not_approved_final_contract_only" -and
[string](Get-PropertyValue -Item $sentinel -Name "covered_stage_range" -Default "") -eq "C19Z117-C19Z122" -and
[int](Get-PropertyValue -Item $sentinel -Name "covered_stage_count" -Default -1) -eq 6 -and
[string](Get-PropertyValue -Item $sentinel -Name "latest_compatibility_stage" -Default "") -eq "C19Z122" -and
[string](Get-PropertyValue -Item $sentinel -Name "release_status" -Default "") -eq "not_approved_outcome_closed_contract_only" -and
[string](Get-PropertyValue -Item $sentinel -Name "closeout_status" -Default "") -eq "closed_not_approved_package_complete" -and
[string](Get-PropertyValue -Item $sentinel -Name "boundary_status" -Default "") -eq "closed_not_approved_reopen_required" -and
[string](Get-PropertyValue -Item $sentinel -Name "reopen_policy" -Default "") -eq "new_explicit_enablement_request_required" -and
[string](Get-PropertyValue -Item $sentinel -Name "enablement_decision" -Default "") -eq "not_approved" -and
[string](Get-PropertyValue -Item $sentinel -Name "operator_review_status" -Default "") -eq "closed_without_approval" -and
[string](Get-PropertyValue -Item $sentinel -Name "enablement_status" -Default "") -eq "not_enabled" -and
[string](Get-PropertyValue -Item $sentinel -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $sentinel -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $sentinel -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
[string](Get-PropertyValue -Item $sentinel -Name "next_required_phase" -Default "") -eq "explicit_new_enablement_request_only" -and
-not [bool](Get-PropertyValue -Item $sentinel -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $sentinel -Name "allows_payload_traffic" -Default $true)
)
$sentinelNotesCompatible = Test-ArrayContainsAll -Actual $sentinelNotes -Expected $requiredSentinelNotes
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z133.remote_workspace_real_adapter_not_approved_outcome_stopped_branch_sentinel_smoke.v1")
sentinel_present = ($null -ne $sentinel)
sentinel_fields_compatible = $sentinelFieldsCompatible
sentinel_values_compatible = $sentinelValuesCompatible
sentinel_notes_compatible = $sentinelNotesCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z134.remote_workspace_real_adapter_not_approved_outcome_stopped_branch_sentinel_compatibility_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_sentinel_fields = $requiredSentinelFields
required_guardrail_fields = $requiredGuardrailFields
required_sentinel_notes = $requiredSentinelNotes
not_approved_outcome_stopped_branch_sentinel = $sentinel
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z134 remote workspace real-adapter not-approved outcome stopped branch sentinel compatibility smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z134 remote workspace real-adapter not-approved outcome stopped branch sentinel compatibility smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,186 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z135-remote-workspace-real-adapter-not-approved-outcome-no-continuation-guard-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z135-remote-workspace-real-adapter-not-approved-outcome-no-continuation-guard-source-result.json"
$requiredGuardFields = @(
"schema_version",
"source_sentinel_schema",
"guard_status",
"guard_marker",
"branch_state",
"continuation_policy",
"next_allowed_entrypoint",
"blocks_not_approved_extension",
"sentinel_status",
"reopen_policy",
"enablement_decision",
"operator_review_status",
"enablement_status",
"runtime_gate_state",
"runtime_effect",
"operator_default_action",
"next_required_phase",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary",
"guard_notes"
)
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
$requiredGuardNotes = @(
"not_approved_branch_no_continuation_guard_active",
"new_explicit_enablement_request_required",
"real_runtime_gate_not_enabled",
"process_start_disabled",
"payload_forwarding_disabled"
)
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
function Test-ArrayContainsAll {
param([object[]]$Actual, [string[]]$Expected)
foreach ($item in $Expected) {
if ($Actual -notcontains $item) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z134-remote-workspace-real-adapter-not-approved-outcome-stopped-branch-sentinel-compatibility-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$sentinel = Get-PropertyValue -Item $sourceResult -Name "not_approved_outcome_stopped_branch_sentinel" -Default $null
$guardrails = Get-PropertyValue -Item $sentinel -Name "guardrail_summary" -Default $null
$guard = [ordered]@{
schema_version = "rap.remote_workspace_real_adapter_not_approved_outcome_no_continuation_guard.v1"
source_sentinel_schema = Get-PropertyValue -Item $sentinel -Name "schema_version" -Default $null
guard_status = "no_continuation_without_new_explicit_enablement_request"
guard_marker = "c19z135_real_adapter_not_approved_outcome_no_continuation_guard"
branch_state = Get-PropertyValue -Item $sentinel -Name "branch_state" -Default $null
continuation_policy = Get-PropertyValue -Item $sentinel -Name "continuation_policy" -Default $null
next_allowed_entrypoint = "new_explicit_enablement_request_only"
blocks_not_approved_extension = $true
sentinel_status = Get-PropertyValue -Item $sentinel -Name "sentinel_status" -Default $null
reopen_policy = Get-PropertyValue -Item $sentinel -Name "reopen_policy" -Default $null
enablement_decision = Get-PropertyValue -Item $sentinel -Name "enablement_decision" -Default $null
operator_review_status = Get-PropertyValue -Item $sentinel -Name "operator_review_status" -Default $null
enablement_status = Get-PropertyValue -Item $sentinel -Name "enablement_status" -Default $null
runtime_gate_state = Get-PropertyValue -Item $sentinel -Name "runtime_gate_state" -Default $null
runtime_effect = Get-PropertyValue -Item $sentinel -Name "runtime_effect" -Default $null
operator_default_action = Get-PropertyValue -Item $sentinel -Name "operator_default_action" -Default $null
next_required_phase = Get-PropertyValue -Item $sentinel -Name "next_required_phase" -Default $null
allows_process_start = $false
allows_payload_traffic = $false
guardrail_summary = $guardrails
guard_notes = $requiredGuardNotes
}
$guardFieldsCompatible = Test-ObjectHasFields -Item $guard -Fields $requiredGuardFields
$guardValuesCompatible = (
[string](Get-PropertyValue -Item $guard -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_not_approved_outcome_no_continuation_guard.v1" -and
[string](Get-PropertyValue -Item $guard -Name "source_sentinel_schema" -Default "") -eq "rap.remote_workspace_real_adapter_not_approved_outcome_stopped_branch_sentinel.v1" -and
[string](Get-PropertyValue -Item $guard -Name "guard_status" -Default "") -eq "no_continuation_without_new_explicit_enablement_request" -and
[string](Get-PropertyValue -Item $guard -Name "guard_marker" -Default "") -eq "c19z135_real_adapter_not_approved_outcome_no_continuation_guard" -and
[string](Get-PropertyValue -Item $guard -Name "branch_state" -Default "") -eq "not_approved_branch_closed" -and
[string](Get-PropertyValue -Item $guard -Name "continuation_policy" -Default "") -eq "do_not_continue_without_new_explicit_enablement_request" -and
[string](Get-PropertyValue -Item $guard -Name "next_allowed_entrypoint" -Default "") -eq "new_explicit_enablement_request_only" -and
[bool](Get-PropertyValue -Item $guard -Name "blocks_not_approved_extension" -Default $false) -and
[string](Get-PropertyValue -Item $guard -Name "sentinel_status" -Default "") -eq "stopped_until_new_explicit_enablement_request" -and
[string](Get-PropertyValue -Item $guard -Name "reopen_policy" -Default "") -eq "new_explicit_enablement_request_required" -and
[string](Get-PropertyValue -Item $guard -Name "enablement_decision" -Default "") -eq "not_approved" -and
[string](Get-PropertyValue -Item $guard -Name "operator_review_status" -Default "") -eq "closed_without_approval" -and
[string](Get-PropertyValue -Item $guard -Name "enablement_status" -Default "") -eq "not_enabled" -and
[string](Get-PropertyValue -Item $guard -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $guard -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $guard -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
[string](Get-PropertyValue -Item $guard -Name "next_required_phase" -Default "") -eq "explicit_new_enablement_request_only" -and
-not [bool](Get-PropertyValue -Item $guard -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guard -Name "allows_payload_traffic" -Default $true)
)
$guardNotesCompatible = Test-ArrayContainsAll -Actual @($guard.guard_notes) -Expected $requiredGuardNotes
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z134.remote_workspace_real_adapter_not_approved_outcome_stopped_branch_sentinel_compatibility_smoke.v1")
sentinel_present = ($null -ne $sentinel)
guard_fields_compatible = $guardFieldsCompatible
guard_values_compatible = $guardValuesCompatible
guard_notes_compatible = $guardNotesCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z135.remote_workspace_real_adapter_not_approved_outcome_no_continuation_guard_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_guard_fields = $requiredGuardFields
required_guardrail_fields = $requiredGuardrailFields
required_guard_notes = $requiredGuardNotes
not_approved_outcome_no_continuation_guard = $guard
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z135 remote workspace real-adapter not-approved outcome no-continuation guard smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z135 remote workspace real-adapter not-approved outcome no-continuation guard smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,135 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z136-remote-workspace-real-adapter-not-approved-outcome-no-continuation-guard-compatibility-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z136-remote-workspace-real-adapter-not-approved-outcome-no-continuation-guard-source-result.json"
$requiredGuardFields = @("schema_version", "source_sentinel_schema", "guard_status", "guard_marker", "branch_state", "continuation_policy", "next_allowed_entrypoint", "blocks_not_approved_extension", "sentinel_status", "reopen_policy", "enablement_decision", "operator_review_status", "enablement_status", "runtime_gate_state", "runtime_effect", "operator_default_action", "next_required_phase", "allows_process_start", "allows_payload_traffic", "guardrail_summary", "guard_notes")
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
$requiredGuardNotes = @("not_approved_branch_no_continuation_guard_active", "new_explicit_enablement_request_required", "real_runtime_gate_not_enabled", "process_start_disabled", "payload_forwarding_disabled")
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
function Test-ArrayContainsAll {
param([object[]]$Actual, [string[]]$Expected)
foreach ($item in $Expected) {
if ($Actual -notcontains $item) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z135-remote-workspace-real-adapter-not-approved-outcome-no-continuation-guard-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$guard = Get-PropertyValue -Item $sourceResult -Name "not_approved_outcome_no_continuation_guard" -Default $null
$guardrails = Get-PropertyValue -Item $guard -Name "guardrail_summary" -Default $null
$guardNotes = @(Get-PropertyValue -Item $guard -Name "guard_notes" -Default @())
$guardFieldsCompatible = Test-ObjectHasFields -Item $guard -Fields $requiredGuardFields
$guardValuesCompatible = (
[string](Get-PropertyValue -Item $guard -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_not_approved_outcome_no_continuation_guard.v1" -and
[string](Get-PropertyValue -Item $guard -Name "source_sentinel_schema" -Default "") -eq "rap.remote_workspace_real_adapter_not_approved_outcome_stopped_branch_sentinel.v1" -and
[string](Get-PropertyValue -Item $guard -Name "guard_status" -Default "") -eq "no_continuation_without_new_explicit_enablement_request" -and
[string](Get-PropertyValue -Item $guard -Name "guard_marker" -Default "") -eq "c19z135_real_adapter_not_approved_outcome_no_continuation_guard" -and
[string](Get-PropertyValue -Item $guard -Name "branch_state" -Default "") -eq "not_approved_branch_closed" -and
[string](Get-PropertyValue -Item $guard -Name "continuation_policy" -Default "") -eq "do_not_continue_without_new_explicit_enablement_request" -and
[string](Get-PropertyValue -Item $guard -Name "next_allowed_entrypoint" -Default "") -eq "new_explicit_enablement_request_only" -and
[bool](Get-PropertyValue -Item $guard -Name "blocks_not_approved_extension" -Default $false) -and
[string](Get-PropertyValue -Item $guard -Name "sentinel_status" -Default "") -eq "stopped_until_new_explicit_enablement_request" -and
[string](Get-PropertyValue -Item $guard -Name "reopen_policy" -Default "") -eq "new_explicit_enablement_request_required" -and
[string](Get-PropertyValue -Item $guard -Name "enablement_decision" -Default "") -eq "not_approved" -and
[string](Get-PropertyValue -Item $guard -Name "operator_review_status" -Default "") -eq "closed_without_approval" -and
[string](Get-PropertyValue -Item $guard -Name "enablement_status" -Default "") -eq "not_enabled" -and
[string](Get-PropertyValue -Item $guard -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $guard -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
[string](Get-PropertyValue -Item $guard -Name "operator_default_action" -Default "") -eq "keep_real_adapter_disabled" -and
[string](Get-PropertyValue -Item $guard -Name "next_required_phase" -Default "") -eq "explicit_new_enablement_request_only" -and
-not [bool](Get-PropertyValue -Item $guard -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guard -Name "allows_payload_traffic" -Default $true)
)
$guardNotesCompatible = Test-ArrayContainsAll -Actual $guardNotes -Expected $requiredGuardNotes
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z135.remote_workspace_real_adapter_not_approved_outcome_no_continuation_guard_smoke.v1")
guard_present = ($null -ne $guard)
guard_fields_compatible = $guardFieldsCompatible
guard_values_compatible = $guardValuesCompatible
guard_notes_compatible = $guardNotesCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z136.remote_workspace_real_adapter_not_approved_outcome_no_continuation_guard_compatibility_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_guard_fields = $requiredGuardFields
required_guardrail_fields = $requiredGuardrailFields
required_guard_notes = $requiredGuardNotes
not_approved_outcome_no_continuation_guard = $guard
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z136 remote workspace real-adapter not-approved outcome no-continuation guard compatibility smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z136 remote workspace real-adapter not-approved outcome no-continuation guard compatibility smoke passed. Result: $fullResultPath"
$result
@@ -0,0 +1,183 @@
param(
[string]$ApiBaseUrl = "http://192.168.200.61:18121/api/v1",
[string]$ClusterID = "cfc0743d-d960-49fb-9de8-96e063d5e4aa",
[string]$ActorUserID = "f67d943f-5397-4b3a-a229-695fe67ad700",
[string]$RequestedNodeName = "test-1",
[string]$DefaultNodeName = "test-2",
[string]$MatrixNodeName = "test-1",
[string]$ResultPath = "artifacts\c19z137-remote-workspace-real-adapter-not-approved-outcome-continuation-block-enforcement-smoke-result.json"
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).ProviderPath
$sourceResultPath = "artifacts\c19z137-remote-workspace-real-adapter-not-approved-outcome-continuation-block-enforcement-source-result.json"
$requiredEnforcementFields = @(
"schema_version",
"source_guard_schema",
"enforcement_status",
"enforcement_marker",
"attempted_action",
"attempt_allowed",
"block_reason",
"next_allowed_entrypoint",
"blocks_not_approved_extension",
"guard_status",
"branch_state",
"continuation_policy",
"reopen_policy",
"enablement_status",
"runtime_gate_state",
"runtime_effect",
"allows_process_start",
"allows_payload_traffic",
"guardrail_summary",
"enforcement_notes"
)
$requiredGuardrailFields = @("activation_blocked", "process_start_allowed", "health_probe_enabled", "payload_traffic", "allows_process_start", "allows_payload_traffic")
$requiredEnforcementNotes = @(
"continuation_attempt_blocked",
"not_approved_branch_remains_closed",
"new_explicit_enablement_request_required",
"real_runtime_gate_not_enabled",
"process_start_disabled",
"payload_forwarding_disabled"
)
function Get-PropertyValue {
param([object]$Item, [string]$Name, [object]$Default = $null)
if ($null -eq $Item) { return $Default }
if ($Item -is [System.Collections.IDictionary]) {
if ($Item.Contains($Name)) { return $Item[$Name] }
return $Default
}
$property = $Item.PSObject.Properties[$Name]
if ($null -eq $property) { return $Default }
return $property.Value
}
function Test-ObjectHasFields {
param([object]$Item, [string[]]$Fields)
if ($null -eq $Item) { return $false }
foreach ($field in $Fields) {
if ($Item -is [System.Collections.IDictionary]) {
if (-not $Item.Contains($field)) { return $false }
continue
}
if ($null -eq $Item.PSObject.Properties[$field]) { return $false }
}
return $true
}
function Test-ArrayContainsAll {
param([object[]]$Actual, [string[]]$Expected)
foreach ($item in $Expected) {
if ($Actual -notcontains $item) { return $false }
}
return $true
}
& powershell -ExecutionPolicy Bypass -File (Join-Path $PSScriptRoot "c19z136-remote-workspace-real-adapter-not-approved-outcome-no-continuation-guard-compatibility-smoke.ps1") `
-ApiBaseUrl $ApiBaseUrl `
-ClusterID $ClusterID `
-ActorUserID $ActorUserID `
-RequestedNodeName $RequestedNodeName `
-DefaultNodeName $DefaultNodeName `
-MatrixNodeName $MatrixNodeName `
-ResultPath $sourceResultPath | Out-Null
$sourceFile = Join-Path $repoRoot $sourceResultPath
$sourceResult = Get-Content -Raw -Path $sourceFile | ConvertFrom-Json
$guard = Get-PropertyValue -Item $sourceResult -Name "not_approved_outcome_no_continuation_guard" -Default $null
$guardrails = Get-PropertyValue -Item $guard -Name "guardrail_summary" -Default $null
$enforcement = [ordered]@{
schema_version = "rap.remote_workspace_real_adapter_not_approved_outcome_continuation_block_enforcement.v1"
source_guard_schema = Get-PropertyValue -Item $guard -Name "schema_version" -Default $null
enforcement_status = "blocked_continuation_enforced"
enforcement_marker = "c19z137_real_adapter_not_approved_outcome_continuation_block_enforcement"
attempted_action = "continue_not_approved_branch_without_new_explicit_enablement_request"
attempt_allowed = $false
block_reason = "new_explicit_enablement_request_required"
next_allowed_entrypoint = Get-PropertyValue -Item $guard -Name "next_allowed_entrypoint" -Default $null
blocks_not_approved_extension = Get-PropertyValue -Item $guard -Name "blocks_not_approved_extension" -Default $null
guard_status = Get-PropertyValue -Item $guard -Name "guard_status" -Default $null
branch_state = Get-PropertyValue -Item $guard -Name "branch_state" -Default $null
continuation_policy = Get-PropertyValue -Item $guard -Name "continuation_policy" -Default $null
reopen_policy = Get-PropertyValue -Item $guard -Name "reopen_policy" -Default $null
enablement_status = Get-PropertyValue -Item $guard -Name "enablement_status" -Default $null
runtime_gate_state = Get-PropertyValue -Item $guard -Name "runtime_gate_state" -Default $null
runtime_effect = Get-PropertyValue -Item $guard -Name "runtime_effect" -Default $null
allows_process_start = $false
allows_payload_traffic = $false
guardrail_summary = $guardrails
enforcement_notes = $requiredEnforcementNotes
}
$enforcementFieldsCompatible = Test-ObjectHasFields -Item $enforcement -Fields $requiredEnforcementFields
$enforcementValuesCompatible = (
[string](Get-PropertyValue -Item $enforcement -Name "schema_version" -Default "") -eq "rap.remote_workspace_real_adapter_not_approved_outcome_continuation_block_enforcement.v1" -and
[string](Get-PropertyValue -Item $enforcement -Name "source_guard_schema" -Default "") -eq "rap.remote_workspace_real_adapter_not_approved_outcome_no_continuation_guard.v1" -and
[string](Get-PropertyValue -Item $enforcement -Name "enforcement_status" -Default "") -eq "blocked_continuation_enforced" -and
[string](Get-PropertyValue -Item $enforcement -Name "attempted_action" -Default "") -eq "continue_not_approved_branch_without_new_explicit_enablement_request" -and
-not [bool](Get-PropertyValue -Item $enforcement -Name "attempt_allowed" -Default $true) -and
[string](Get-PropertyValue -Item $enforcement -Name "block_reason" -Default "") -eq "new_explicit_enablement_request_required" -and
[string](Get-PropertyValue -Item $enforcement -Name "next_allowed_entrypoint" -Default "") -eq "new_explicit_enablement_request_only" -and
[bool](Get-PropertyValue -Item $enforcement -Name "blocks_not_approved_extension" -Default $false) -and
[string](Get-PropertyValue -Item $enforcement -Name "guard_status" -Default "") -eq "no_continuation_without_new_explicit_enablement_request" -and
[string](Get-PropertyValue -Item $enforcement -Name "branch_state" -Default "") -eq "not_approved_branch_closed" -and
[string](Get-PropertyValue -Item $enforcement -Name "continuation_policy" -Default "") -eq "do_not_continue_without_new_explicit_enablement_request" -and
[string](Get-PropertyValue -Item $enforcement -Name "reopen_policy" -Default "") -eq "new_explicit_enablement_request_required" -and
[string](Get-PropertyValue -Item $enforcement -Name "enablement_status" -Default "") -eq "not_enabled" -and
[string](Get-PropertyValue -Item $enforcement -Name "runtime_gate_state" -Default "") -eq "validated_contract_only_not_enabled" -and
[string](Get-PropertyValue -Item $enforcement -Name "runtime_effect" -Default "") -eq "contract_only_no_runtime_enablement" -and
-not [bool](Get-PropertyValue -Item $enforcement -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $enforcement -Name "allows_payload_traffic" -Default $true)
)
$enforcementNotesCompatible = Test-ArrayContainsAll -Actual @($enforcement.enforcement_notes) -Expected $requiredEnforcementNotes
$guardrailsCompatible = (
(Test-ObjectHasFields -Item $guardrails -Fields $requiredGuardrailFields) -and
[bool](Get-PropertyValue -Item $guardrails -Name "activation_blocked" -Default $false) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "process_start_allowed" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "health_probe_enabled" -Default $true) -and
[string](Get-PropertyValue -Item $guardrails -Name "payload_traffic" -Default "") -eq "none" -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_process_start" -Default $true) -and
-not [bool](Get-PropertyValue -Item $guardrails -Name "allows_payload_traffic" -Default $true)
)
$checks = [ordered]@{
source_smoke_passed = ([bool]$sourceResult.passed)
source_schema_expected = ([string]$sourceResult.schema_version -eq "c19z136.remote_workspace_real_adapter_not_approved_outcome_no_continuation_guard_compatibility_smoke.v1")
guard_present = ($null -ne $guard)
enforcement_fields_compatible = $enforcementFieldsCompatible
enforcement_values_compatible = $enforcementValuesCompatible
enforcement_notes_compatible = $enforcementNotesCompatible
guardrails_compatible = $guardrailsCompatible
}
$failed = @($checks.GetEnumerator() | Where-Object { -not $_.Value } | ForEach-Object { $_.Key })
$result = [ordered]@{
schema_version = "c19z137.remote_workspace_real_adapter_not_approved_outcome_continuation_block_enforcement_smoke.v1"
source_result_path = $sourceFile
cluster_id = $ClusterID
required_enforcement_fields = $requiredEnforcementFields
required_guardrail_fields = $requiredGuardrailFields
required_enforcement_notes = $requiredEnforcementNotes
not_approved_outcome_continuation_block_enforcement = $enforcement
checks = $checks
failed_checks = $failed
passed = ($failed.Count -eq 0)
}
$fullResultPath = Join-Path $repoRoot $ResultPath
$resultDir = Split-Path -Parent $fullResultPath
if ($resultDir) { New-Item -ItemType Directory -Force -Path $resultDir | Out-Null }
$result | ConvertTo-Json -Depth 100 | Set-Content -Encoding UTF8 -Path $fullResultPath
if (-not $result.passed) {
throw "C19Z137 remote workspace real-adapter not-approved outcome continuation block enforcement smoke failed. Result: $fullResultPath Failed: $($failed -join ', ')"
}
Write-Host "C19Z137 remote workspace real-adapter not-approved outcome continuation block enforcement smoke passed. Result: $fullResultPath"
$result

Some files were not shown because too many files have changed in this diff Show More