Skip to content

feat: a stack can run shifted on this machine (#387, ADR-0082) - #401

Merged
7schmiede merged 1 commit into
mainfrom
feat/port-shift
Oct 31, 2026
Merged

7schmiede merged 1 commit into
mainfrom
feat/port-shift

Conversation

@7schmiede

Copy link
Copy Markdown
Member

Closes #387. This is the third and last pull request for review card C (ADR-0082): it builds points 3–7. The card and its approval of 2026-10-30 are in #396.

What it does

  • Registry: each stack's entry gets shift and claims ({main, lanes}). Both are omitted when empty, so older entries read as shift 0 with no claims. Writes read, modify and write back the entry and keep fields they do not set.
  • Shift: the main tree's stack is moved by the shift on the same path a lane's offset uses (lane_stack). That moves services, their proxies and dev-server fronts, main_ports, the placeholders and urls.
    • The main overlay moves every published host port and announced port, and keeps the Compose project, container names, networks and volumes.
    • Lanes are numbered shift + n × step.
  • Claims: the daemon writes them at start and when they change (checked every 5 s). New lane numbers avoid other registered stacks' claims. Entries whose folder or stack file is gone claim nothing.
  • up pre-check (main tree):
    • Collision with a running stack: refused, naming the stack and the ports, and suggesting devstack up --shift auto or --shift <n>.
    • Collision with a stopped stack: a warning.
    • --shift auto|<n>|none sets the shift, which then sticks.
    • Changing the shift of a running stack is refused ("devstack down first"), the same way a different profile is.
    • --shift is refused in a worktree.
  • Elsewhere: MCP up takes shift. POST /hub/stacks/{id}/up {shift} starts a stack with one. /v1/stack and the hub's stack list carry shift, and devstack status prints it. The stack page shows "shifted +N" and a Start with shift button for a stopped stack that collides; the mock hub covers it.
  • Docs: stack-file.md gets a section Several stacks on one machine (including what does not follow: ports hard-coded outside placeholders). Also dashboard-api.md, skill devstack, the changelog, and ADR-0082 (now built in full, with the decisions below).

Decided by the agent alone:

  • auto starts at 1000, never 0.
  • An explicit shift must be a multiple of 1000.
  • Claims are refreshed by a 5 s check, not hooked into every change.
  • A lane keeps its number; only new numbers avoid other stacks' claims.
  • The pre-check uses the declared ports plus the last run's claims, so a first start does not know ports only a Compose file publishes.

Tests

  • Unit, devstack-config / compose / daemon / hub: 378 passed. New tests cover the registry round trip with and without the new fields, free_number with other stacks' claims, the shift overlay, auto, the refuse and warn decisions, and the hub body parsing. The compat files are untouched and green.
  • CLI: 55 passed.
  • Integration on native Windows: the full daemon_lifecycle suite passed (20 tests), including the new a_second_stack_on_the_same_ports_runs_shifted. It checks four steps: the second stack is refused and names the first; --shift auto runs it on +1000; a restart keeps the shift; --shift none returns it.
  • Docker: DEVSTACK_INTEGRATION_COMPOSE=1 passed (5 tests), including the new a_shifted_stacks_containers_move_with_it. Host and announced ports move, the name stays, and --shift none returns them.
  • UI: typecheck, 129 tests and the build pass.
  • Not verified: a lane on a pool node beside a shifted main tree (the node is told shift + n × step, but this was not run end to end), live lanes beside a shifted main tree, and macOS beyond CI.

🤖 Generated with Claude Code

The last step of review card C. A stack's registry entry holds a port
shift (machine-local, never in the stack file) and the ports its daemon
claims. `devstack up` refuses to start on ports a running stack holds,
names it and offers `--shift auto|<n>`; a stopped stack's claims only
warn. The shift moves every port a lane offset moves (services and their
proxies and forwards, container host and announced ports, `urls`,
placeholders, lanes) and keeps the Compose project, containers and
volumes; it sticks until `--shift none`. New lane numbers avoid other
stacks' claims. MCP `up`, `POST /hub/stacks/{id}/up {shift}` and a
"Start with shift" button on the stack page take it too.

Closes #387

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@7schmiede
7schmiede merged commit 343643c into main Oct 31, 2026
13 checks passed
@7schmiede
7schmiede deleted the feat/port-shift branch October 31, 2026 10:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Several stacks on one machine collide on the same ports

1 participant