fix(up): start shared-only (hub) workspaces and self-heal the network - #91
Merged
Merged
Conversation
Two drift bugs made `devstack up` a silent no-op that started nothing — the exact symptom hit on a `devstack init` starter workspace (shared pg/redis/minio, no projects): 1. Selective-up derived the shared set only from projects' `uses` (profile.sharedUsedBy). A hub / shared-only workspace has no project referencing its shared services, so `active.Shared` was empty and the shared phase brought up nothing. Now a project-less workspace that declares `shared:` brings up its full declared shared stack. 2. The network phase had a constant fingerprint, so once satisfied it was skipped forever — even after the external `devstack_shared` network was removed out-of-band (docker network prune, a Docker Desktop / WSL restart). Every subsequent compose-up then failed with "network devstack_shared declared as external, but could not be found". Marked the phase AlwaysRun: EnsureNetwork is a cheap create-if-missing under the lock, so re-running every time is correct and self-healing. Tests: a hub workspace brings up all declared shared services (and no compose-up phase); the network re-ensures after out-of-band removal; the happy-path re-run now expects network to re-run (AlwaysRun) rather than skip. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem (reported live)
devstack upfrom adevstack initstarter workspace (shared pg/redis/minio, no projects) printed a wall of[ok]/[skipped]but started nothing — no containers, nodevstack_sharednetwork:Root cause — two drift bugs
Hub / shared-only workspaces never came up. Selective-up (spec 12) derives the shared set from projects'
uses(profile.sharedUsedBy). A workspace withshared:but no projects has nothing referencing those services, soactive.Sharedwas empty and the shared phase no-op'd. This is exactly whatdevstack initscaffolds.The network phase was fingerprint-cached with a constant fingerprint, so once satisfied it skipped forever. When the external
devstack_sharednetwork was removed out-of-band (adocker network prune, a Docker Desktop / WSL restart),upskipped re-creating it and every compose-up failed withnetwork devstack_shared declared as external, but could not be found.Fix
shared:now brings up its full declared shared stack (sortedSharedNames).AlwaysRun:EnsureNetworkis a cheap create-if-missing under the lock, so re-running every time is correct and self-healing.Verification
Ran against the real reported workspace — network re-created (51ms) and all three shared services came up healthy:
Tests
TestBuildUpHubWorkspaceBringsUpAllShared— a hub workspace brings up all declared shared services and builds no compose-up phase.TestNetworkPhaseSelfHeals— the network re-ensures after out-of-band removal.TestBuildUpHappyPathre-run assertion updated:networknow re-runs (AlwaysRun) rather than skips.Known follow-up (not in this PR)
Container-level drift (a manual
docker rmof a shared container while config is unchanged) still leaves the shared phase fingerprint-satisfied → skipped. Shared services ship withrestart: unless-stoppedso restarts self-recover; manual removal is recovered viadoctor --fix/ shared reconcile. A future change can fold live-container liveness into the shared phase gate.🤖 Generated with Claude Code