feat(cli): one-repo three-zone deployment lifecycle - #577
Merged
Conversation
Introduce the repo-root resolver as the single location authority for repo-scoped commands, and the three-zone conventions: repo root holds profile.yml, data/, and the root .env; build/ is the regenerable render; var/ is runtime state.
osprey init materializes a deployment repo from a preset; osprey set is the single profile writer; users seed/passwd/remove absorb the deploy account verbs; build takes no arguments and renders the current repo's build/ from its profile.yml.
… refusal up/down/restart/status/logs replace the deploy group; up starts the as-built render and refuses on profile-vs-build drift; reset scopes runtime state precisely and preserves var/audit unless --purge-audit.
…tainers build renders into build/.tmp and swaps on success; personas render at build time with their own merged config; container images carry a real three-zone repo at /app/<name> with a pinned interpreter.
…r machinery BREAKING CHANGE: the deploy verb group, the bare-osprey interactive menu, osprey profile new/try, osprey claude regen/chat/status, build --force, positional build, and config set-control-system / set-epics-gateway are removed. Successors: top-level lifecycle verbs, osprey init, osprey set, plain osprey build, and osprey users.
… roots Resolve config from the render and secrets from the repo-root .env via one shared rule; thread the configured agent-data base_dir through both sides of the artifact pointer contract; guard absolute base_dirs; read transcripts from the agent's render dir; repoint retired _agent_data readers; detect podman containers.
…ites The invariants suite enforces the resolver and retired-spelling criteria structurally, with a per-pattern liveness proof so a broken pattern cannot read as a pass; e2e suites move onto the init/build two-step and repo naming.
CI runs the init/build two-step and top-level verbs; the README quick start teaches the live commands; run_va.sh mounts var/agent_data via a profile.yml walk-up and reports a skipped mount instead of silence; tool excludes cover var/.
…esign Reconciles the retired deploy-group CLI surface with main's archiver store and recorder, collaborative panels, and gated e2e lanes on the three-zone lifecycle.
…esign Brings in the preset persona-role tiers, the panel focus-echo fix, bridge startup ordering, and panel-confirmed start requests, reconciled onto the three-zone lifecycle.
Sweeps retired command spellings (deploy group, profile new, config set-*, claude group) from the how-to and reference pages, rewrites the secrets model around the single repo .env, and corrects persona-render and container-relocation guidance to match the build-time model.
…esign Brings in agent-action visibility across the terminal UI and MCP tools, reconciled onto the three-zone lifecycle.
Long post-release dev versions overflowed the banner box; the negative gap made repeat() throw, leaving the welcome overlay undismissable.
…esign Brings in the executor write-gate hardening and agent-activity attribution fixes, reconciled onto the three-zone lifecycle.
The hello-world smoke test seeded nothing into the repo .env because the provider key was only exported at the up step; up's .env preflight then refused. Export it at init so the shell-harvest writes the secret store the preflight expects.
The render/start split left --dev with no way to reach sidecar images: up never re-renders, and only build stages wheels into service contexts. osprey build --dev now renders the dev flavor (wheel staged, OSPREY_DEV emitted); osprey up --dev starts it, refusing a pinned render with the remedy named, and up --build --dev / init --up --dev thread dev through the chain. A plain up of a dev render warns that the images carry the local checkout. Also aligns the auth-perimeter e2e persona stub with the render contract: a persona project now carries its image-context copy (.image/<name>/build/), which is what the persona image is built from.
Same render-contract alignment as the auth-perimeter stub: a persona project carries the .image/<name>/build/ copy its image is built from, so the hand-rolled heterogeneous local-mode personas now write both copies.
The CLI entry loads ./.env into os.environ with override=True, which erased a divergent shell export before the deploy could see it: the shadow preflight compared the store against itself and never warned, and compose was handed the overridden copy — the opposite of its documented shell-over-env-file precedence, and different behavior depending on the operator's cwd. The entry-load now records the shell values it replaces (accumulating across repeat loads, first-seen wins), and the start path overlays them back for both the shadow warning and the environment compose receives.
A deployed service reads the host-flattened config the compose generator staged, whose project_root names a path on the build machine. Preferring that key unconditionally sent the agent-data root outside the mounted volume inside containers — artifacts saved successfully into a directory nothing observes. The configured value now wins only when it names a directory that exists here (the same rule compose_generator.resolve_repo_root documents), then the repo derived from the config path, then the configured value for config-less --runtime-root layouts.
Three fixtures still assumed deploy-time rendering: archiver-world built without --dev but started with it (the dev-render gate now refuses that), dispatch-overlay appended its trigger to build/ after the render had already copied the dispatcher's mounted triggers file, and openobserve-telemetry pinned its host port by editing build/config.yml after the compose files were rendered. Each now expresses its input where the build reads it: --dev on the build, source-zone mutation before the build, and an init-time override for the port, with a fail-fast render assertion.
test_discovery_rewire launched osprey web against the real shell resolver and test_status_sections' logs tests probed for a container runtime, so both passed on developer machines and failed on runners without those binaries. The first now stubs resolve_shell_command like its sibling module; the second takes the file's existing runtime fixture.
The scaffold-gallery suite rendered the control-assistant preset for every test. Under coverage measurement that render is an order of magnitude more expensive, and the one matrix cell that measures coverage exceeded its job timeout grinding through it. Bake a single session-scoped render and hand each test a private copy with its embedded absolute paths re-anchored, so mutations never reach the shared bake.
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.
Replaces OSPREY's two coexisting project-materialization shapes and fragmented lifecycle CLI with one gold-standard form: a git repo that IS the deployment — tracked source (
profile.yml, single root.env) at the root, a 100%-disposablebuild/, and durablevar/state — operated by a flat, zero-argument verb set (init,set,validate,build,up,down,restart,status,logs,reset,chat,users …,scaffold ci) under one repo-discovery rule.Highlights:
osprey initmaterializes a preset into the three-zone repo shape (git-initialized,.envseeded from the shell, anchored.gitignore, CI files);--upchains build+up as the one-shot try.osprey buildre-rendersbuild/atomically (render intobuild/.tmp, swap on success — a failed render preserves the previous good build) and stamps a profile fingerprint into the build manifest.osprey up/restartnever render: they verify the profile fingerprint against the build manifest and refuse on drift, naming the changed keys (--buildto re-render,--as-builtas the knowing override). All containers/volumes carry acom.osprey.repo-idlabel.osprey resetis the factory wipe that never existed: plan-print, typed confirmation, removal scoped to project name + repo identity.How it hangs together