fix(provisioner): remove 12-char UUID truncation (KI-010) #1213

Closed
infra-lead wants to merge 2 commits from fix/provisioner-no-uuid-truncation into staging
3 changed files with 22 additions and 32 deletions
+6 -9
View File
@@ -604,20 +604,17 @@ jobs:
set -euo pipefail
# `needs.*.result` is one of: success | failure | cancelled | skipped | null.
# We assert success per dep (not != failure) — see RFC §2 reasoning above.
# Null results are skipped: they come from Phase 3 (continue-on-error: true
# suppresses status) or from jobs still in-flight. The sentinel succeeds
# rather than blocking PRs on Phase 3 noise.
# Null results are skipped: they come from jobs still in-flight.
# The sentinel succeeds rather than blocking PRs on transient in-flight noise.
results='${{ toJSON(needs) }}'
echo "$results"
echo "$results" | python3 -c '
import json, sys
ns = json.load(sys.stdin)
# Phase 3 masked: jobs with continue-on-error: true may report "failure"
# Remove when mc#774 handler test failures are resolved.
PHASE3_MASKED = {"platform-build"}
# Exclude null (Phase 3 suppressed / in-flight) from the bad list.
# Phase 3 is over (mc#774 closed 2026-05-14, continue-on-error: false re-enabled).
# All non-null/non-skipped/non-cancelled deps must succeed.
bad = [(k, v.get("result")) for k, v in ns.items()
if v.get("result") not in ("success", None, "cancelled", "skipped") and k not in PHASE3_MASKED]
if v.get("result") not in ("success", None, "cancelled", "skipped")]
if bad:
print(f"FAIL: jobs not green:", file=sys.stderr)
for k, r in bad:
@@ -633,5 +630,5 @@ jobs:
if cancelled:
print(f"INFO: {len(cancelled)} job(s) masked by continue-on-error: " +
", ".join(k for k, _ in cancelled), file=sys.stderr)
print(f"OK: all {len(ns)} required jobs succeeded (or Phase-3 suppressed)")
print(f"OK: all {len(ns)} required jobs succeeded")
'
@@ -129,24 +129,19 @@ const (
)
// ConfigVolumeName returns the Docker named volume for a workspace's configs.
// Full workspaceID is used to avoid container-name aliasing that breaks
// A2A routing (KI-010). Docker volume names support the full UUID length.
func ConfigVolumeName(workspaceID string) string {
id := workspaceID
if len(id) > 12 {
id = id[:12]
}
return fmt.Sprintf("ws-%s-configs", id)
return fmt.Sprintf("ws-%s-configs", workspaceID)
}
// ClaudeSessionVolumeName returns the Docker named volume for a workspace's
// Claude Code session directory (/root/.claude/sessions). Separate from the
// config volume so it can be discarded independently (via WORKSPACE_RESET_SESSION
// or ?reset=true) without wiping the user's config. Issue #12.
// Full workspaceID is used to avoid aliasing (KI-010).
func ClaudeSessionVolumeName(workspaceID string) string {
id := workspaceID
if len(id) > 12 {
id = id[:12]
}
return fmt.Sprintf("ws-%s-claude-sessions", id)
return fmt.Sprintf("ws-%s-claude-sessions", workspaceID)
}
// Provisioner manages Docker containers for workspace agents.
@@ -164,12 +159,11 @@ func New() (*Provisioner, error) {
}
// ContainerName returns the Docker container name for a workspace.
// Full workspaceID is used to avoid container-name collisions that break
// A2A peer routing (KI-010). Docker caps container names at 128 chars;
// a 36-char UUID is well within limits.
func ContainerName(workspaceID string) string {
id := workspaceID
if len(id) > 12 {
id = id[:12]
}
return fmt.Sprintf("ws-%s", id)
return fmt.Sprintf("ws-%s", workspaceID)
}
// containerNamePrefix is the shared prefix every workspace container
@@ -350,15 +350,16 @@ func TestTierEscalation(t *testing.T) {
}
// TestContainerName verifies the naming convention.
// No truncation: full workspaceID is used to avoid container-name collisions
// that break A2A peer routing (KI-010).
func TestContainerName(t *testing.T) {
tests := []struct {
id string
want string
}{
{"short", "ws-short"},
{"exactly12ch", "ws-exactly12ch"},
{"longer-than-twelve-characters", "ws-longer-than-"},
{"abc", "ws-abc"},
{"ab078012-c305-42be-b93d-b6e8a78d5409", "ws-ab078012-c305-42be-b93d-b6e8a78d5409"},
}
for _, tt := range tests {
@@ -370,15 +371,15 @@ func TestContainerName(t *testing.T) {
}
// TestConfigVolumeName verifies config volume naming.
// No truncation — full workspaceID used (KI-010).
func TestConfigVolumeName(t *testing.T) {
tests := []struct {
id string
want string
}{
{"short", "ws-short-configs"},
{"exactly12ch", "ws-exactly12ch-configs"},
{"longer-than-twelve-characters", "ws-longer-than--configs"},
{"abc", "ws-abc-configs"},
{"ab078012-c305-42be-b93d-b6e8a78d5409", "ws-ab078012-c305-42be-b93d-b6e8a78d5409-configs"},
}
for _, tt := range tests {
@@ -392,17 +393,15 @@ func TestConfigVolumeName(t *testing.T) {
// ---------- #12 — claude-sessions volume naming ----------
// TestClaudeSessionVolumeName_Deterministic: same ID → same volume name, and
// the name follows the ws-<id[:12]>-claude-sessions shape used everywhere
// else in the provisioner.
// the name follows the ws-<full-id>-claude-sessions shape (no truncation, KI-010).
func TestClaudeSessionVolumeName_Deterministic(t *testing.T) {
tests := []struct {
id string
want string
}{
{"short", "ws-short-claude-sessions"},
{"exactly12ch", "ws-exactly12ch-claude-sessions"},
{"longer-than-twelve-characters", "ws-longer-than--claude-sessions"},
{"abc", "ws-abc-claude-sessions"},
{"ab078012-c305-42be-b93d-b6e8a78d5409", "ws-ab078012-c305-42be-b93d-b6e8a78d5409-claude-sessions"},
}
for _, tt := range tests {
got := ClaudeSessionVolumeName(tt.id)