Skip to content

feat(dev): one project per cloud dev environment - #480

Merged
adiman9 merged 9 commits into
mainfrom
feat/dev-env-lease
Oct 31, 2026
Merged

adiman9 merged 9 commits into
mainfrom
feat/dev-env-lease

Conversation

@adiman9

@adiman9 adiman9 commented Oct 29, 2026

Copy link
Copy Markdown
Contributor

One project per cloud dev environment. a4 dev up now holds the environment it runs against, so two projects can't overwrite each other's deployments in one environment.

What changes

  • Taking the environment. a4 dev up takes the environment's lease before it deploys anything (POST /api/dev/environments/:env/lease), renews it every 20 s and releases it on exit. A killed run's lease lapses after about a minute.
  • A second project is refused. Its a4 dev up gets devenv-environment-leased with the holder's name and deploys nothing. The message names a4 dev up --take-over and a4 dev branch (a copy of the environment for the second project).
  • --take-over (new flag) moves the environment to this project. The previous project's deployments there stop being served.
  • Fenced writes. Protocol deployment create, activate and disable, and environment stack builds, carry the lease generation in the Arete-Dev-Lease-Generation header.
  • Losing the environment. When a renewal or any write is refused with devenv-lease-lost, a4 dev up stops:
    • it prints "this environment was taken over by ; nothing more was written" and exits non-zero;
    • its automations runner and its file watcher stop with it;
    • it never retries the write under a new lease.
  • Status. a4 dev status (a "held by" line, and lease in --json) and the dev_status MCP tool show who holds the environment.
  • Docs. A "One project per environment" section in cli/local-development.mdx, and the flag in cli/commands.mdx.

Fork state (funded wallets, warps, scenarios, resets) stays shared and unaffected.

Compatibility

An Arete API without environment leases answers the lease route with a plain 404 (no error code). a4 dev up then runs exactly as before, without a lease (one line in a4.log). Status omits the holder. The API also accepts writes without the header while no lease is held, so this release and the API change can ship in either order. The intended order is the API first, then this release.

Tests

  • commands::dev::lease unit tests:
    • lease-lost detection in typed and API errors, and the exit message;
    • project ids are stable per checkout, differ across checkouts, and don't contain the path.
  • cargo test -p a4-cli --bin a4 -- commands::dev: 105 passed.
  • cargo test -p arete-mcp: 284 passed.
  • cargo clippy -p arete-mcp -p a4-cli --tests -- -D warnings: clean.
  • The server-side behaviour (refusal, takeover, fenced writes) is tested against the API with a database.

a4 dev up takes the environment's lease before it deploys anything,
renews it while it runs and releases it on exit. Another project's run is
refused with the holder's name and the ways out (--take-over, or
a4 dev branch for its own copy).

Protocol deploys and environment stack builds carry the lease generation.
When a renewal or a write is refused because the environment was taken
over, a4 dev up stops its automations runner and watcher and exits
non-zero ("this environment was taken over by <holder>; nothing more was
written"), without retrying under a new lease.

a4 dev status and the dev_status MCP tool show which project holds the
environment. An Arete API without environment leases answers the lease
route with a plain 404, and a4 dev up then runs without a lease.
@vercel

vercel Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
arete-docs Ready Ready Preview Oct 31, 2026 3:04pm UTC

Request Review

@greptile-apps

greptile-apps Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; no new actionable issue remains.

Summary

Adds one project per cloud dev environment through renewable leases and checks on deployment writes.

  • Each cloud dev environment serves one project's deployments at a time.
  • Status reports which project holds a cloud dev environment.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[a4 dev up] --> B[Start local services]
  B --> C{Take environment lease}
  C -->|Refused| D[Stop local services and exit]
  C -->|Held| E[Deploy with lease generation]
  E --> F[Renew lease and watch files]
  F -->|Stop or lease lost| G[Stop runner and local services]
  G --> H[Release lease with a short timeout]
  H --> I[Shut down runtime without waiting for abandoned work]
  I --> J[Exit]
Loading

Reviews (7) · Last reviewed commit: "fix(cli): a takeover mid-build ends a4 d..." · Reviewed by Greptile

Comment thread cli/src/commands/dev/up.rs Resolved
Comment thread cli/src/commands/dev/api.rs Resolved
Comment thread cli/src/commands/dev/lease.rs Resolved
Comment thread cli/src/commands/dev/up.rs Resolved
- A disable refused because the environment was taken over stops the run
  instead of counting as already disabled.
- A project that takes its environment back redeploys: the other
  project's takeover disabled what it left there.
- A refused renewal stops the session at once.
- The test API serves environment leases; a test covers the refusal, the
  take-over and the generation on stack builds.
- Docs formatting.
A token or port failure no longer leaves the environment held until the
lease expires: the lease is taken just before anything deploys, and a
refused take shuts the local endpoint and UI down.
# Conflicts:
#	docs/src/content/docs/cli/local-development.mdx
#	rust/arete-mcp/src/dev_env.rs
Comment thread cli/src/commands/dev/up.rs Resolved
Comment thread cli/src/commands/dev/lease.rs Resolved
# Conflicts:
#	cli/src/commands/dev/mod.rs
#	cli/tests/dev_environments.rs
#	docs/src/content/docs/cli/local-development.mdx
#	rust/arete-mcp/src/dev_env.rs
#	rust/arete-mcp/src/server.rs
… stay apart

The automations update and the stack lookup ran outside the session's
cancellable waits, so a stalled request held up a confirmed takeover for as
long as it took. They now give way to a lost lease and to a signal.

The project id hashed only the absolute path, so projects at one path on
different machines or containers counted as the same project and took the
environment without --take-over. The id now also covers a random id kept in
the checkout.
Comment thread rust/arete-mcp/src/dev_env.rs Resolved
Comment thread cli/src/commands/dev/up.rs Resolved
# Conflicts:
#	cli/src/commands/dev/mod.rs
#	rust/arete-mcp/src/server.rs
…n the API

a4 dev up takes the lease before it loads the stacks in arete.lock, but those
loads carried no generation, so the API refused them and startup reported
ready without them. They now carry it, and a takeover during a load ends the
run instead of being reported as one failed stack.

Stopping released the lease before it stopped the UI and proxy, on a request
with a 150-second timeout. Local services now stop first, and the release gives
up after five seconds; the lease then expires by itself.
Comment thread cli/src/commands/dev/up.rs Resolved
@adiman9
adiman9 merged commit a7fa8dd into main Oct 31, 2026
35 checks passed
@adiman9
adiman9 deleted the feat/dev-env-lease branch October 31, 2026 16:40

This branch was successfully deployed

1 active deployment
Preview — db894b45 Deployed Oct 31, 2026 by vercel[bot]
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.

1 participant