Monorepo workspaces (jarvy workspace)¶
jarvy workspace is the read-only inspection surface for monorepo
projects that declare a [workspace] block in their root jarvy.toml.
PRD-047 MVP. Workspace-aware
jarvy setup --project <name>orchestration is intentionally deferred — surfacing the resolved structure first lets users debug inheritance before we add a command that mutates the environment based on it.
Declaring a workspace¶
# /repo/jarvy.toml — workspace root
[workspace]
members = ["apps/web", "apps/api", "packages/shared"]
# What sections members inherit from the root config. Empty list is
# treated as ["provisioner"] (the most common case) when inspecting
# resolved tools.
inherit = ["provisioner", "hooks"]
[provisioner]
git = "latest"
docker = "latest"
Each member directory may optionally have its own jarvy.toml that
adds tools or overrides inherited values:
# /repo/apps/web/jarvy.toml
[provisioner]
node = "20"
docker = "24.0" # overrides workspace's docker = "latest"
Members without their own jarvy.toml inherit the workspace defaults
unchanged.
CLI¶
# Enumerate members + their resolved tool sets
jarvy workspace --file ./jarvy.toml list
# Show one member's resolved config (with inheritance applied + provenance)
jarvy workspace --file ./jarvy.toml show apps/web
# Validate that members exist on disk and their jarvy.toml parses
jarvy workspace --file ./jarvy.toml validate
All three subcommands accept --format json for AI agents / CI.
Sample output¶
$ jarvy workspace --file ./jarvy.toml list
Workspace: /repo
Inherits: provisioner, hooks
Members (3):
[ok ] apps/web docker, git, node
[ok ] apps/api go, golangci-lint
[MISS] packages/shared (uses workspace defaults)
$ jarvy workspace --file ./jarvy.toml show apps/web
Project: apps/web
Path: /repo/apps/web
Config: /repo/apps/web/jarvy.toml
Inherits sections: provisioner, hooks
Tools (3):
docker = "24.0" (overridden)
git = "latest" (inherited)
node = "20"
The (overridden) / (inherited) annotations come from comparing
the merged provisioner table to the raw root + member tables — gives
a direct answer to "where did this tool come from?"
$ jarvy workspace --file ./jarvy.toml validate
Validating workspace at /repo
warn: packages/shared: no jarvy.toml (workspace defaults apply)
2 ok, 1 warning(s), 0 error(s).
Validate exits 0 when there are no errors (warnings are advisory)
and CONFIG_ERROR (2) when any member's directory is missing or its
jarvy.toml fails to parse.
Glob member patterns¶
members = ["apps/*"] expands at config-load time to every immediate
subdirectory of apps/. Skips .dotfile directories. Exact paths
and globs can mix:
[workspace]
members = [
"apps/*", # expand all immediate children
"packages/shared", # plus this exact member
]
exclude = [
"apps/legacy",
"apps/*-deprecated",
]
Glob support is intentionally minimal: only * (any path component
run, no /), no **, no ?, no character classes. The patterns
real monorepos write (apps/*, packages/*-server) all work; if you
need more, fall back to exact paths.
exclude = [...] is applied AFTER expansion using the same matcher
so apps/* + exclude = ["apps/legacy"] gives you every sibling of
legacy minus legacy itself.
jarvy setup --project <name>¶
# Setup one member explicitly:
jarvy setup --project apps/web
# Auto-detect from cwd:
cd apps/web && jarvy setup
# Same as `--project current`:
jarvy setup --project current
The runner reads the member's own jarvy.toml (with workspace
inheritance applied). Members WITHOUT a per-member jarvy.toml get a
synthesized merged config written to a tempfile so setup still has
something to install against.
Auto-context detection: when invoked WITHOUT --project, setup
walks up from cwd to find a workspace root and checks whether cwd
sits inside a declared member. If so, setup scopes implicitly and
prints Detected workspace member \apps/web` — scoping setup to
this member.to stderr. Pass--project
The same auto-context applies to jarvy drift and jarvy doctor —
both honor cwd's enclosing member so cd apps/web && jarvy drift
check "just works."
jarvy context¶
Read-only diagnostic that shows what jarvy thinks the current execution context is. Useful as a sanity check before running setup in a new repo.
$ cd apps/web
$ jarvy context --file /repo/jarvy.toml
Jarvy execution context
=======================
Working dir: /repo/apps/web
--file arg: /repo/jarvy.toml
Workspace: /repo
Root config: /repo/jarvy.toml
Members (3):
apps/api
→ apps/web
packages/shared
Current member: apps/web
Auto-context: `jarvy setup` would scope to `apps/web` (override with --project).
Resolved setup file: /repo/apps/web/jarvy.toml
Supports --format json for AI agents / CI.
Inheritance semantics¶
Member configs merge with the root via
crate::workspace::merge_configs:
- For sections in
inherit, member values completely override root values except forprovisioner, which is merged tool-by-tool (member wins on conflict). - For sections NOT in
inherit, the member gets only what's in its own jarvy.toml.
If inherit = [] (or omitted), BOTH the workspace CLI surface AND
the production setup resolver treat it as ["provisioner"] — the
common monorepo case (members share the root toolset) works without
explicit config. Routed through WorkspaceConfig::effective_inherit()
so CLI display and production setup cannot disagree.
What's deferred¶
- Per-member install parallelism —
jarvy setup --project apps/*runs members sequentially today. The existing per-tool parallelism (PRD-001 / rayon) stops at the workspace member boundary. [workspace.members.<name>]inline overrides — alternative to per-memberjarvy.toml. Per-member files cover the case adequately for now.