Compare commits

...

2 Commits

Author SHA1 Message Date
infra-lead e28e996f1c fix(provisioner): remove 12-char UUID truncation from container/volume names (KI-010)
CI / all-required (pull_request) Blocked by required conditions
Harness Replays / Harness Replays (pull_request) Blocked by required conditions
Runtime PR-Built Compatibility / PR-built wheel + import smoke (pull_request) Blocked by required conditions
qa-review / approved (pull_request) Waiting to run
security-review / approved (pull_request) Waiting to run
Lint curl status-code capture / Scan workflows for curl status-capture pollution (pull_request) Successful in 25s
Harness Replays / detect-changes (pull_request) Successful in 31s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 29s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 1m38s
Lint workflow YAML (Gitea-1.22.6-hostile shapes) / Lint workflow YAML for Gitea-1.22.6-hostile shapes (pull_request) Successful in 1m50s
Runtime PR-Built Compatibility / detect-changes (pull_request) Successful in 1m46s
Lint pre-flip continue-on-error / Verify continue-on-error flips have run-log proof (pull_request) Successful in 2m57s
lint-required-context-exists-in-bp / lint-required-context-exists-in-bp (pull_request) Successful in 2m54s
lint-continue-on-error-tracking / lint-continue-on-error-tracking (pull_request) Successful in 3m29s
audit-force-merge / audit (pull_request) Has been skipped
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 44s
MCP Stdio Transport Regression / MCP stdio with regular-file stdout (pull_request) Successful in 2m53s
CI / Detect changes (pull_request) Successful in 2m28s
E2E API Smoke Test / detect-changes (pull_request) Successful in 2m28s
gate-check-v3 / gate-check (pull_request) Successful in 39s
sop-tier-check / tier-check (pull_request) Successful in 38s
lint-mask-pr-atomicity / lint-mask-pr-atomicity (pull_request) Successful in 3m42s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 9s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 7m35s
CI / Python Lint & Test (pull_request) Successful in 8m47s
CI / Canvas (Next.js) (pull_request) Failing after 23m58s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
CI / Platform (Go) (pull_request) Failing after 25m41s
sop-checklist / na-declarations (pull_request) N/A: (none)
Handlers Postgres Integration / detect-changes (pull_request) Has been cancelled
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Has been cancelled
sop-checklist / all-items-acked (pull_request) Has been cancelled
ContainerName(), ConfigVolumeName(), and ClaudeSessionVolumeName() were
truncating workspace UUIDs to 12 characters, causing Docker container-name
collisions. This broke A2A peer routing because the A2A adapter embeds
the container name (ws-<id[:12]>) which is not globally unique.

Fix: use the full workspaceID for all three functions. Docker limits
container names to 128 chars; a 36-char UUID (ws-<uuid>) is well within
that limit. Volume names are unbounded.

Updated tests to reflect no-truncation behavior.

Refs: KI-010, internal#412
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-15 16:34:31 +00:00
infra-lead 1b8190d271 fix(ci): remove stale PHASE3_MASKED from all-required sentinel (DISCOVERY #1167)
sop-checklist / all-items-acked (pull_request) [info tier:low] acked: 0/7 — missing: comprehensive-testing, local-postgres-e2e, staging-smoke, +4 — body-unfilled: comprehensive-testing, l
audit-force-merge / audit (pull_request) Has been skipped
mc#774 was closed 2026-05-14, re-enabling continue-on-error: false on
platform-build. The PHASE3_MASKED workaround that suppressed platform-build
failures in the sentinel is now stale and was creating a false-green signal
on the all-required CI gate.

Also removes Phase 3 references from comments and success message.
2026-05-15 10:58:30 +00:00
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)