···133133 // These will be merged with pipeline env vars by the framework
134134 workflowEnv := map[string]string{
135135 "TANGLED_ARCHITECTURE": spec.Architecture,
136136+ // HOME must be writable; we run as user 10000 so default /root won't work
137137+ "HOME": "/tmp",
136138 }
137139138140 workflow := &models.Workflow{
+5-2
internal/jobbuilder/job_template.go
···291291 AllowPrivilegeEscalation: &[]bool{false}[0],
292292 RunAsNonRoot: &[]bool{true}[0],
293293 RunAsUser: &[]int64{10000}[0],
294294- ReadOnlyRootFilesystem: &[]bool{true}[0],
294294+ // Note: ReadOnlyRootFilesystem is NOT set for the runner container
295295+ // because user-defined images may need to write to various locations
296296+ // (e.g., /go/pkg, ~/.cache, /var/tmp) that we can't predict or mount
295297 Capabilities: &corev1.Capabilities{
296298 Drop: []corev1.Capability{"ALL"},
297299 },
···332334333335// buildEnvironmentVariables creates the environment variables for the runner container.
334336// All environment variables come from WorkflowSpec.Environment, which includes:
335335-// - Engine-specific vars (PATH, TANGLED_ARCHITECTURE) set in InitWorkflow
337337+// - Engine-specific vars (PATH, TANGLED_ARCHITECTURE, HOME) set in InitWorkflow
336338// - Pipeline-level vars (TANGLED_REPO_*, TANGLED_REF, CI, etc.) injected by framework
337339func buildEnvironmentVariables(config WorkflowConfig) []corev1.EnvVar {
338340 var env []corev1.EnvVar
···375377 // Add set -e for error handling, safe.directory config to handle ownership mismatch
376378 // (emptyDir volumes are root-owned but we run as user 10000)
377379 script := "set -e\n" +
380380+ "git config --global init.defaultBranch main\n" +
378381 "git config --global advice.detachedHead false\n" +
379382 "git config --global safe.directory /tangled/workspace\n" +
380383 strings.Join(config.CloneCommands, "\n") + "\n" +