Deployment scheme: layered settings + the Brewfile host contract - #133
Open
JPDuchesne wants to merge 16 commits into
Open
Deployment scheme: layered settings + the Brewfile host contract#133JPDuchesne wants to merge 16 commits into
JPDuchesne wants to merge 16 commits into
Conversation
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
2 tasks
Co-authored-by: Cursor <cursoragent@cursor.com>
dev is public and hardcodes no org content, so the baseline manifest moves out of dev's distribution: the org names its repo in the new baseline_repo setting and dev reads baseline/dependencies.rb from it, caching a machine-local copy next to the stamp. dev up refreshes the cache (falling back to it offline) and converges on digest drift; the per-command nag stays O(1) against the cache, never the network. Settings resolution is now layered gitconfig-style — ENV over the user file over the brew-prefix system file an org deployment formula ships. Co-authored-by: Cursor <cursoragent@cursor.com>
The tap now ships dev-core (the org-blank tool) and a slim dev deployment formula (org config + dependency edge). Every org's install becomes `brew install <org>/<tap>/dev`; individuals install dev-core and hand-write the user config. release.rb rewrites both formulas' url+sha in lockstep. Co-authored-by: Cursor <cursoragent@cursor.com>
Brew converges brew: the org tooling list lives in the deployment formula's Brewfile (etc/dev/Brewfile) and dev up only triggers brew — throttled brew update, scoped upgrade of the self-named deployment_formula, then brew bundle install. Deletes the baseline fetch/cache/stamp/nag machinery; settings drop baseline_repo for deployment_formula. Co-authored-by: Cursor <cursoragent@cursor.com>
config list / get <key> / set <key> <value> over the layered settings, with the source layer shown gitconfig --show-origin style. Known-keys only, from the Settings::KNOWN_KEYS registry — the one list both the command and the resolver read. set merge-writes the user file as plain string-keyed YAML. Global command, dispatched before any dev.yml lookup. Co-authored-by: Cursor <cursoragent@cursor.com>
Measured no-op costs on a converged host (brew update ~0.5s, scoped upgrade ~0.4s, brew bundle ~0.9s) don't justify a daily stamp that delays deployment fixes by up to 24h. Every dev up now runs the full chain; brew's own HOMEBREW_AUTO_UPDATE_SECS stays the only network rate limiter, tunable through brew rather than dev. Co-authored-by: Cursor <cursoragent@cursor.com>
Codecov's patch check flagged the warn-only branches (failed upgrade, failed bundle), the production Executor bodies, and ConfigCommand#call as untested. Co-authored-by: Cursor <cursoragent@cursor.com>
Tap-qualified segments were missing @ and + — a versioned deployment formula (org/tap/dev@2) was falsely rejected as malformed, silently stopping self-updates in exactly the tap-qualified case the guard serves. Also reject the invalid two-segment form and require brew's canonical lowercase spelling (brew stores taps downcased). Co-authored-by: Cursor <cursoragent@cursor.com>
The domain model dev actually has: every run carries a host half (ui), and a project half only when a dev.yml encloses the cwd. ProjectContext groups root/ruby/python/build_container/runner; project! makes the registration invariant explicit (project commands only exist when the project does). Groundwork for a project-optional Runner — no behavior change yet. Co-authored-by: Cursor <cursoragent@cursor.com>
up is a hybrid command: the host half (converge + cd RC hook) always runs; the project half provisions only when a project context exists, otherwise up prints the fresh-box bootstrap message and succeeds. This is the business logic bin/dev's rescue was homing at the wrong layer. Co-authored-by: Cursor <cursoragent@cursor.com>
The Runner now constructs without a dev.yml: manifest resolution moves into run (inside the exit_for mapping, where project-input errors like the removed ruby: key already map to clean exits), the projectless catalog registers only up (backed by a builtin-only executor and a null dependency service), and a lookup miss maps to the no-dev.yml refusal. bin/dev's rescue block — host converge for up, the refusal for everything else — was this exact logic homed at the wrong layer; it is deleted, not moved. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Host::Converge was a single-#run method object, and "what keeping a host converged consists of" lived in no class — up hand-assembled converge + RC hook while plan/learnings hand-assembled skills + learnings sync. HostService names that responsibility with four domain verbs (converge_tooling, install_rc_hook, install_skills, sync_learnings); the brew layer becomes converge_tooling's private implementation. Co-authored-by: Cursor <cursoragent@cursor.com>
The skills + learnings refresh pair was hand-assembled from individual collaborators at each hook point. Plan::Accessor and InstallDepsCommand now inject the one host service and call its verbs, which also replaces install-deps' untyped synchronizer param with a typed collaborator. Learnings::Accessor keeps its direct collaborators: its sync subcommand is the explicit blocking refresh, a different contract from the warn-only hooks. Co-authored-by: Cursor <cursoragent@cursor.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.
Summary
Host layer for dev#102 (plans#26), final shape: the host baseline is no longer a manifest dev fetches and converges — it is a Homebrew contract shipped by the org's deployment formula. And dev's dispatch is now project-optional, so
dev upon a fresh box (no dev.yml anywhere) converges the host and succeeds.dev-coreis the public core formula; each org ships a thindevformula depending on it that installsconfig.ymland an orgBrewfileinto$(brew --prefix)/etc/dev/(pkgetc).Dev::Host::Converge, first step of everydev up): scoped self-update —brew upgrade <deployment_formula>, the setting validated against brew's canonical token shape (bare or fully tap-qualified, lowercase,@/+allowed) — thenbrew bundleagainst the org Brewfile. Warn-only: a failed host step never blocks project provisioning. No self-update throttle; brew is its own rate limiter.Dev::Settingsresolves each key gitconfig-style — ENV → user file (~/.config/dev/config.yml) → system file (pkgetc, shipped by the deployment formula). The newdev configglobal builtin manages the user layer.ExecutionContextsplits into a host half (always present) and a nilableProjectContext;Runnerconstructs without a dev.yml and serves a projectless catalog of exactlyup, wired with a builtin-only executor and a null dependency service.upis a hybrid command — its host half (converge +dev cdRC hook) always runs; outside a project it prints the bootstrap message and exits 0.bin/devrescues nothing: the no-dev.yml refusal and the removed-ruby:-key error both map insideRunner#exit_for, at the layer that owns the run.Verification
srb tcclean.Wheretable (tap-qualified@/+accepted, two-segment and uppercase rejected); projectless Runner paths (context assembly, up-only catalog, no-dev.yml refusal,ruby:-key mapping insiderun); the hybridupoutside a project; the builtin-only executor's wiring guard; the null dependency service.