Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .cursor/rules/learnings-index.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ propose a retirement, a consolidation, or a glob-scoped sub-index split.
bare `Then`/`Expect` statements are assertions; outside a transformed
class they silently assert nothing.
→ .agents/skills/gem-rspock--rspock/ (ships in the rspock gem; linked by
`dev up` / `dev install-deps`)
`dev up` / `dev deps install`)

## process

Expand All @@ -40,7 +40,7 @@ propose a retirement, a consolidation, or a glob-scoped sub-index split.
- [architecture/module-map] What owns what: src/dev is the typed CLI core;
each `lib/dev/<module>` is one feature whose Accessor is its only CLI
surface. → .cursor/skills/architecture/module-map/
- [architecture/hook-points] `dev up` / `install-deps` / `dev plan` double
- [architecture/hook-points] `dev up` / `dev deps install` / `dev plan` double
as the idempotent hygiene hooks (skill links, learnings sync): never
raise, network only as a bounded pull before distribution.
→ .cursor/skills/architecture/hook-points/
Expand Down
4 changes: 2 additions & 2 deletions .cursor/skills/architecture/hook-points/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
name: hook-points
description: >-
MUST be used when adding side-work (links, syncs, renders) to dev up,
dev install-deps, or dev plan — the rules hygiene rides by.
dev deps install, or dev plan — the rules hygiene rides by.
---

# dev hook points: hygiene rides, never blocks

`dev up` / `dev install-deps` (`Runner#install_locked_deps`) and every
`dev up` / `dev deps install` (`Runner#install_locked_deps`) and every
`dev plan` invocation (`Plan::Accessor#run`) double as the refresh points
for agent-facing hygiene: shipped-skill links, gem-skill links, the
knowledge repo cache pull, and the org-invariants render + project link.
Expand Down
4 changes: 2 additions & 2 deletions .cursor/skills/learnings/command-runner-exec/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ failed `dev up` as installed). Only outcome-independent work may move
before the exec point.

Right (dev#85): Runner sets `wait: true` on ExecutionContext for
`STAMPING_COMMANDS` (`up`, `install-deps`); CommandRunner then runs the
`STAMPING_COMMANDS` (`dev up`, `dev deps install`); CommandRunner then runs the
child spawn-and-wait (`Kernel.system`) instead of exec-replace, raising
`CommandFailedError` with the child's exit status on failure, which
Runner turns into `Kernel.exit` — stamp only on success, exit code
Expand All @@ -35,6 +35,6 @@ passthrough, no double process tree). Diagnostic signature of a missing
wait: an exec-style provisioning command "succeeds" but the staleness
gate keeps reporting "never installed" — fatal in a CI=true shell.

learned-from: dev#73 build pass (dev up never stamped; install-deps
learned-from: dev#73 build pass (dev up never stamped; dev deps install
did); fixed by CommandRunner wait mode in dev#85
date: 2026-08-03
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,5 @@ tmp/
# knowledge repo's invariants)
.cursor/rules/org-invariants.mdc

# Generated by dev install-deps/up: skill links from lockfile-matched gems
# Generated by dev deps install/up: skill links from lockfile-matched gems
.agents/skills/
38 changes: 19 additions & 19 deletions README.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion data/Deps.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
# (e.g. "Boost::"), targets are prefixed automatically. This replaces hardcoded dep checks.

if(NOT EXISTS "${CMAKE_SOURCE_DIR}/deps.lock.cmake")
message(FATAL_ERROR "deps.lock.cmake not found. Run: dev update-deps, commit the lockfile, then run dev up or build.")
message(FATAL_ERROR "deps.lock.cmake not found. Run: dev deps update, commit the lockfile, then run dev up or build.")
endif()
include("${CMAKE_SOURCE_DIR}/deps.lock.cmake")

Expand Down
10 changes: 5 additions & 5 deletions docs/deps-architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ ecosystem's canonical form and validated at the DSL boundary (a cmake

## The resolve pipeline

`dev update-deps` runs:
`dev deps update` runs:

1. **Lock** — for each integration with a registered `Locker`, run it over
that integration's declarations. Today that is bundler only:
Expand Down Expand Up @@ -146,12 +146,12 @@ ecosystem's canonical form and validated at the DSL boundary (a cmake
(`brew:` → `zlib:` → attrs) so the on-disk key carries the same
(integration, name) identity the resolver keys on. The reader also
accepts the pre-nesting flat format; that shim is deleted once every
consumer repo's lockfiles have been rewritten by `update-deps`.
consumer repo's lockfiles have been rewritten by `dev deps update`.

`dev install-deps` reads the lockfile and hands each integration its pins;
`dev deps install` reads the lockfile and hands each integration its pins;
no resolution happens at install time.

### Resolution flow (`dev update-deps`)
### Resolution flow (`dev deps update`)

```mermaid
sequenceDiagram
Expand Down Expand Up @@ -196,7 +196,7 @@ sequenceDiagram
Note over lock: writes deps.lock and build-deps.lock, nested by integration
```

### Install flow (`dev install-deps`)
### Install flow (`dev deps install`)

```mermaid
sequenceDiagram
Expand Down
2 changes: 1 addition & 1 deletion lib/dev/agent_bootstrap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ def sudoers_content

# The brew escalation edge (the Homebrew single-user gap on cooperative
# machines): the agent may run exactly brew as the prefix owner,
# NOPASSWD, so `dev install-deps` converges formulae without a human
# NOPASSWD, so `dev deps install` converges formulae without a human
# even though the prefix belongs to the enrolling user. The owner is
# stat'd here at bootstrap time — never hardcoded — and re-running
# register re-converges the edge. Omitted when the host has no brew or
Expand Down
2 changes: 1 addition & 1 deletion lib/dev/deps/accessor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ def xcode_developer_dir
sig { params(integration: Symbol, name: String).returns(Dependency) }
def find_dep(integration, name)
dep = @lockfile.read.find { |d| d.integration == integration && d.name == name }
raise NotLockedError, "#{name} (#{integration}) is not in the lockfile — run dev update-deps" unless dep
raise NotLockedError, "#{name} (#{integration}) is not in the lockfile — run dev deps update" unless dep

dep
end
Expand Down
2 changes: 1 addition & 1 deletion lib/dev/deps/bundler_integration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ module Deps
# Installs the locked gems with `bundle install` against the Gemfile/
# Gemfile.lock that BundlerRepository generated and committed. The install is
# frozen: it must match the committed lockfile exactly, so install never
# silently re-resolves (re-resolution is `dev update-deps`'s job).
# silently re-resolves (re-resolution is `dev deps update`'s job).
#
# The individual locked deps are informational here — bundler installs the
# full graph from the Gemfile.lock — so install_all only needs to know there
Expand Down
2 changes: 1 addition & 1 deletion lib/dev/deps/bundler_locker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class LockError < StandardError; end

GENERATED_HEADER = <<~HEADER
# Generated by dev from dependencies.rb. Do not edit.
# Add or change gems in dependencies.rb and run `dev update-deps`.
# Add or change gems in dependencies.rb and run `dev deps update`.
HEADER

# @param project_root [Pathname, String] root the Gemfile/Gemfile.lock live in
Expand Down
2 changes: 1 addition & 1 deletion lib/dev/deps/bundler_repository.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def initialize(project_root:)
def find(id)
pin = pins.fetch(id.name) do
raise MissingGemError,
"gem #{id.name.inspect} is not in #{LOCKFILE} — run `dev update-deps`"
"gem #{id.name.inspect} is not in #{LOCKFILE} — run `dev deps update`"
end

Package.new(
Expand Down
4 changes: 2 additions & 2 deletions lib/dev/deps/cmake_integration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,14 @@ class ExtractError < StandardError; end
DEPS_CMAKE_HEADER = <<~TEXT
# CMake dep variables: repo+sha or url+hash per runtime dep. Generated by dev - do not edit.
# Lists below are per build target (app vs test), not per dev environment (ci/dev).
# Edit dependencies.rb and run dev update-deps to change.
# Edit dependencies.rb and run dev deps update to change.

TEXT

TARGETS_CMAKE_HEADER = <<~TEXT
# Runtime targets/includes per dep. Generated by dev - do not edit.
# Used by cmake/Deps.cmake when resolving each build target (app / test).
# Edit dependencies.rb and run dev update-deps to change.
# Edit dependencies.rb and run dev deps update to change.

TEXT

Expand Down
4 changes: 2 additions & 2 deletions lib/dev/deps/dsl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -540,7 +540,7 @@ def xcode(version, **spec)
#
# Dual-writes: the existing @brew/groups entry feeds the container build
# path (bin/install-build-deps.rb), while the additional declaration
# rides the resolver -> lockfile -> install pipeline so `dev install-deps`
# rides the resolver -> lockfile -> install pipeline so `dev deps install`
# installs it on the host too. BrewIntegration skips already-installed
# formulae, so the host install is idempotent.
#
Expand Down Expand Up @@ -571,7 +571,7 @@ def brew(name, **opts)

# Scope member declarations to an environment ("ci" / "dev"). The env
# name is a first-class declaration field (like host), landing in the
# lockfile's env section so install-deps filters it to the matching
# lockfile's env section so `dev deps install` filters it to the matching
# environment — never smuggled through the constraint hash.
#
# @param name [String, Symbol] environment name
Expand Down
2 changes: 1 addition & 1 deletion lib/dev/deps/ficsit_integration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def install(dep)
if platforms.nil? || platforms.empty?
raise MissingPlatformsError,
"#{dep.name}@#{dep.version} has no resolved platforms — declare it in a " \
"group with a platform and run dev update-deps"
"group with a platform and run dev deps update"
end

platforms.each { |platform, target| install_platform(dep, platform, target) }
Expand Down
2 changes: 1 addition & 1 deletion lib/dev/deps/gem_skill_linker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ module Deps
# A gem's skill is part of what installing that dependency means —
# installing rspock without its skill would be an incomplete install,
# exactly like installing it without its executables. So `dev up` /
# `dev install-deps` finish by scanning the resolved (lockfile-matched)
# `dev deps install` finish by scanning the resolved (lockfile-matched)
# gem set for skills/*/SKILL.md and linking each one project-scoped as
# .agents/skills/gem-<gem>--<skill> (gitignored; an agent-neutral dir so
# the mechanism isn't Cursor-locked). A skill-set change rides the same
Expand Down
2 changes: 1 addition & 1 deletion lib/dev/deps/gh_integration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ def matching_assets(dep)

raise NoMatchingAssetsError,
"no locked assets matching #{pattern.inspect} for #{dep.metadata["repo"]}@#{dep.version} " \
"— check the assets: glob, or run dev update-deps"
"— check the assets: glob, or run dev deps update"
end

# Extract all downloaded archives into extracted_dir. Split archives
Expand Down
8 changes: 4 additions & 4 deletions lib/dev/deps/lockfile.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,19 @@ module Deps
# The reader also accepts the legacy flat format (name-keyed, integration
# recorded in the value hash) so repos keep installing from lockfiles
# written before the nesting; that shim is deleted once every consumer
# repo's lockfiles have been rewritten by update-deps (issue #146).
# repo's lockfiles have been rewritten by dev deps update (issue #146).
class Lockfile
extend T::Sig

HEADER = <<~COMMENT
# Generated by dev. Do not edit.
# Edit dependencies.rb and run dev update-deps to change.
# Edit dependencies.rb and run dev deps update to change.
COMMENT

# Header line recording the SHA-256 of the dependencies.rb the lockfiles
# were generated from. The staleness check (Dev::Deps::Staleness) compares
# it against the current manifest to detect "declarations changed but
# update-deps wasn't run". A comment so the YAML payload stays pure deps.
# dev deps update wasn't run". A comment so the YAML payload stays pure deps.
MANIFEST_DIGEST_PREFIX = "# dependencies-digest: "

DEPS_LOCK_FILE = "deps.lock"
Expand Down Expand Up @@ -69,7 +69,7 @@ def lock(deps, manifest_digest: nil)

# The manifest digest recorded when the lockfiles were generated, or nil
# for lockfiles predating the staleness check (treated as unknown, not
# stale — legacy locks shouldn't nag until their next update-deps).
# stale — legacy locks shouldn't nag until their next dev deps update).
#
# @return [String, nil] SHA-256 hex
sig { returns(T.nilable(String)) }
Expand Down
2 changes: 1 addition & 1 deletion lib/dev/deps/pip_integration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ module Deps
# Installs each locked package into the project-local venv (.venv) that
# ShadowenvPython provisions — the Python analogue of LuaRocks installing
# into lua_modules/. The venv is ensured here (created if absent) so
# `dev install-deps` works on a fresh clone, before any command has run
# `dev deps install` works on a fresh clone, before any command has run
# ShadowenvPython.setup!. pip resolves the transitive tree at install.
class PipIntegration < Integration
extend T::Sig
Expand Down
6 changes: 3 additions & 3 deletions lib/dev/deps/registry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ module Deps
# arguments each side needs (drawn from a context the runner assembles).
module Registry
# Install location for a type:
# :host installed on the host by `dev install-deps`
# :container installed inside the build container (not by install-deps)
# :host installed on the host by `dev deps install`
# :container installed inside the build container (not by dev deps install)
# :both installed on the host and, separately, in the container
HOST = :host
CONTAINER = :container
Expand Down Expand Up @@ -296,7 +296,7 @@ def schemes
end

# Build the integration-type -> Locker hash for types whose ecosystem
# tool owns the whole-set solve. update-deps runs these before the
# tool owns the whole-set solve. dev deps update runs these before the
# Resolver so each tool lockfile is materialized when find reads it.
#
# @param project_root [Pathname] project root (threaded to lockers that need it)
Expand Down
10 changes: 5 additions & 5 deletions lib/dev/deps/staleness.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ module Deps
# with two O(1) digest comparisons at every command start:
#
# 1. manifest vs lockfile: dependencies.rb digest against the digest
# recorded in the lockfile header by `dev update-deps` →
# "declarations changed — run dev update-deps".
# recorded in the lockfile header by `dev deps update` →
# "declarations changed — run dev deps update".
# 2. lockfile vs installed stamp: a digest of the lockfile contents
# against the stamp written after the last fully-successful install →
# "lock changed since last install — run dev up".
Expand All @@ -27,7 +27,7 @@ module Deps
# semantics — CI environments install fresh, so a mismatch there is a
# pipeline bug, not a reminder).
#
# Stamps catch sequence drift (edit without update-deps, lock bump without
# Stamps catch sequence drift (edit without dev deps update, lock bump without
# dev up), not out-of-band mutation of installed artifacts — that's a
# deferred doctor-style per-integration sweep.
class Staleness
Expand Down Expand Up @@ -80,14 +80,14 @@ def manifest_message

recorded = Lockfile.new(dir: @project_root).manifest_digest
# No digest recorded: a legacy lockfile (predates the check) — stay
# quiet until its next update-deps stamps one. No lockfile at all is
# quiet until its next dev deps update stamps one. No lockfile at all is
# layer-2's problem (nothing was ever installed either).
return nil unless recorded

current = Digest::SHA256.file(manifest.to_s).hexdigest
return nil if current == recorded

"dependencies.rb changed since the lockfiles were generated — run dev update-deps"
"dependencies.rb changed since the lockfiles were generated — run dev deps update"
end

# Layer 2: have the lockfiles changed since the last successful install
Expand Down
4 changes: 2 additions & 2 deletions lib/dev/deps/steam_integration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ def steam_platform_for(platform)

# Confirm the installed depot matches the locked buildid. A mismatch means
# the lock is stale (the public branch moved) — surface it so the user
# re-runs dev update-deps rather than silently testing a different build.
# re-runs dev deps update rather than silently testing a different build.
#
# @param dep [Dependency]
# @param server_dir [Pathname]
Expand All @@ -128,7 +128,7 @@ def verify_build_id(dep, server_dir)

raise BuildMismatchError,
"#{dep.name}: expected buildid #{dep.version}, installed #{installed_build.inspect} " \
"— run dev update-deps to re-pin"
"— run dev deps update to re-pin"
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/dev/host_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ module Dev
# operation shares the same contract: no user arguments, idempotent, and
# warn-only (host hygiene rides other commands and must never block
# them). Commands compose these verbs — `dev up`'s host half is
# converge_tooling + install_rc_hook; `dev plan` and `install-deps`
# converge_tooling + install_rc_hook; `dev plan` and `dev deps install`
# refresh the cheap artifact pair on every invocation.
#
# Anything host-scoped but not convergence-shaped (a user-facing verb
Expand Down
2 changes: 1 addition & 1 deletion lib/dev/learnings/accessor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ module Dev
module Learnings
# Dispatch for `dev learnings …` — the explicit surface over the learnings
# read path. Passive distribution rides dev's hook points (`dev up` /
# `install-deps` / `dev plan`); these verbs are the manual override and
# `dev deps install` / `dev plan`); these verbs are the manual override and
# the inspection:
#
# - `sync` — refresh the whole read path now (blocking): pull the
Expand Down
2 changes: 1 addition & 1 deletion lib/dev/learnings/synchronizer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def project_rules_file(project_root)
Pathname(project_root).join(*ORG_INVARIANTS_RULE_SUBDIRS)
end

# The passive hook entry (`dev up` / `install-deps` / `dev plan`): pull
# The passive hook entry (`dev up` / `dev deps install` / `dev plan`): pull
# inline within the cache's short timeout (falling back to the current
# cache when the network is slower, or offline), then distribute.
# Never raises: learnings sync is hygiene riding another command, and
Expand Down
4 changes: 2 additions & 2 deletions lib/dev/shadowenv_python.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ module Dev
# Mirrors ShadowenvRuby/ShadowenvLua. Triggered when dependencies.rb declares
# `python "3.12"`. This module owns only the interpreter + the (empty) venv;
# the package set is installed into that venv by Dev::Deps::PipIntegration on
# `dev install-deps`, exactly as LuaRocks fills lua_modules/.
# `dev deps install`, exactly as LuaRocks fills lua_modules/.
module ShadowenvPython
extend T::Sig
include Kernel
Expand Down Expand Up @@ -63,7 +63,7 @@ def setup!(python_version:, project_root:)

# Ensure Homebrew python@<version> is installed and a project-local .venv
# exists, built with that exact interpreter. Idempotent and safe to call from
# both setup! (per command) and PipIntegration (install-deps), so the venv is
# both setup! (per command) and PipIntegration (dev deps install), so the venv is
# guaranteed present before packages install into it.
#
# @param python_version [String] e.g. "3.12"
Expand Down
2 changes: 1 addition & 1 deletion lib/dev/shadowenv_ruby.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def detect_homebrew_ruby_version
end

# Guarded provisioning: the O(1) provisioned? check first, so callers on
# every-command paths (CommandRunner, the up/install-deps builtins) pay
# every-command paths (CommandRunner, the `dev up`/`dev deps install` builtins) pay
# nothing after the first run.
sig { params(ruby_version: String, project_root: T.any(String, Pathname)).void }
def ensure!(ruby_version:, project_root:)
Expand Down
2 changes: 1 addition & 1 deletion lib/dev/skill_installer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ module Dev
# - org knowledge skills (machine knowledge cache) → ~/.cursor/skills
# - gem-shipped skills (lockfile-matched gems) → <project>/.agents/skills
#
# Called from cheap, idempotent hook points (`dev up` / `install-deps` /
# Called from cheap, idempotent hook points (`dev up` / `dev deps install` /
# `dev plan`), so there is no separate setup step and `brew upgrade`
# refreshes shipped skills automatically (symlinks resolve through the
# installed tree, wherever brew put it).
Expand Down
2 changes: 1 addition & 1 deletion src/dev/builtins/check_command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

module Dev
module Builtins
# `dev check`: report the dependency-state freshness the staleness guard
# `dev deps check`: report the dependency-state freshness the staleness guard
# would act on, and exit non-zero when anything drifted.
class CheckCommand < BuiltinCommand
extend T::Sig
Expand Down
Loading
Loading