Compatibility and scope
What viv's byte-identical promise actually covers, what stays out of scope, and how that's checked before every release.
What viv promises
For the commands listed in the README's Scope
section, viv writes a vendor/ directory — including vendor/composer/* —
and, for update, update-lock and require/remove, a composer.lock
that are byte-identical to what Composer 2.10 (the version pinned in
devbox.json) writes for the same composer.json, lock file and cache
state. This is the contract, not an implementation detail: if viv's output
differs from Composer's for a covered command, that's a bug.
Composer moves; viv tracks one version of it at a time. Each release states which Composer version it targets, and the compat sweep (see below) is run against that version before the release ships.
What is not covered
- Plugins. Only the native adapters viv ships (see
docs/plugin-strategy.md) are covered. Any other plugin is out of scope; viv either skips it or thecomposershim hands the invocation to the real Composer. - Commands Composer keeps.
create-project,init,search,config,global,self-update,diagnose,licenses,dependsand the rest of the long tail stay with Composer and aren't part of viv's contract. - Progress wording on stdout during a run. Only the files viv writes and
the plain-text output of commands such as
show,whyandvalidateare contractual. Spinner text, progress counters and similar in-flight chatter can change between releases without notice. - Timing. The numbers in the README track viv's speed but aren't a promise; a release can get slower on a given machine without breaking the contract, though the project tries hard not to let that happen.
Works with
- Repositories: Packagist, Private Packagist and Satis, including a local
file://mirror,path,vcs, git and GitHub sources; redirects are followed. - Packages: zip and tar dists, a git checkout when there is no dist,
preferred-install: source, sha1 checks, credentials fromauth.jsonandCOMPOSER_AUTH. - Autoload: PSR-4, PSR-0, classmap and files;
--optimize-autoloaderand--classmap-authoritative;platform_check.php;vendor/binproxies; lifecycle scripts. - Commands:
install, full and partialupdateincluding--minimal-changesand Composer's default blocking of versions with a security advisory (--no-blockingto allow them),add,rm,dump-autoload, and offline mode with--offlineorCOMPOSER_DISABLE_NETWORK. - Plugins: the ones with native adapters, listed under Plugins.
Reasons not to use viv
- It is pre-1.0. Minor releases can change behaviour and flags; the
release notes and
JOURNAL.mdcall those out. The output contract (vendor/andcomposer.lockidentical to Composer's) is the one thing that does not move. - Windows is not supported. Linux and macOS only.
- Some Composer plugins stop the install. symfony/flex and any plugin
without a native adapter make viv exit with an error naming the plugin.
--no-pluginsinstalls as Composer would without them, but the plugin's work is not done. Of the 10 pinned test projects, 1 is in this position:symfony/demo, for symfony/flex, which viv refuses by design. - The shim needs a real Composer for everything else. It maps
install,dump-autoload,normalize,create-project,update,requireandremoveto viv;searchand the rest go to the Composer on yourPATH, and with no real Composer installed they fail. vendor/files are read-only by default. viv hardlinks them from a shared store, so an edit insidevendor/fails instead of changing every project on the machine. If you patch vendor files by hand, install with--link-mode copy, or--link-mode clonefor writable files sharing the store's disk space where the filesystem supports it.- Two
updategaps.--ignore-platform-reqsonupdate,requireandremoveaffects the autoload write, not the solve, so a package pinned to a PHP this interpreter lacks still fails to resolve (#242). And a version filtered out byminimum-stabilityis still reported as not found rather than as filtered; Composer names the cause.
Is it safe to try
viv's contract is that its output matches Composer's byte for byte. Before
every release, a compatibility sweep installs a mix of pinned popular
projects and a random sample of Packagist packages with both Composer and
viv, then compares the results. The v0.13.0 sweep: 34 rows identical, 0 differ, 6
skipped, and all 10 pinned projects resolve the same composer.lock as
Composer as well as installing the same vendor/.
One pinned project still needs --no-plugins, for a plugin viv refuses by
design rather than one it has yet to port. See Plugins
below.
How compatibility is checked
Every release runs the compatibility
sweep
against a mix of pinned popular projects and a random sample of Packagist
packages, comparing viv's vendor/ and composer.lock against Composer's
own. Per-release results live in
compat/results/;
ongoing sweeps of public projects outside that pinned set are tracked in
compat/hunted.md.
The Compare page turns the newest numbers into a table.
For the curious, here's exactly what a plain install writes, and how a path renders inside it — the detail the sweep checks byte for byte.
vendor/autoload.php, and in vendor/composer/:
| File | When |
|---|---|
autoload_namespaces.php, autoload_psr4.php, autoload_classmap.php, autoload_static.php, autoload_real.php |
always |
autoload_files.php |
any files entry, else deleted |
platform_check.php |
config.platform-check not false and at least one PHP or ext requirement, else deleted |
ClassLoader.php, InstalledVersions.php, LICENSE |
verbatim copies from Composer (MIT); vivace embeds them from src/autoload/templates/ |
installed.json, installed.php |
always |
vendor/bin/* |
packages with bin, per config.bin-compat (src/bin.rs) |
No .gitignore is written. Files are only rewritten when their bytes change.
Standard layout gives $vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir);.
A path under the vendor dir renders as $vendorDir . '/psr/log/src', anything
else relative to the project as $baseDir . '/src'. Paths are normalised
first: // collapsed, ./ and .. resolved, trailing slash stripped, so
"src/" and "src" both give /src. In autoload_static.php the same paths
render as __DIR__ . '/..' . '/psr/log/src' and __DIR__ . '/../..' . '/src'.
Versioning
viv is pre-1.0: expect breaking changes between minor releases, and expect
them to be called out in JOURNAL.md. Within that, a minor release may:
- add new commands or flags,
- make internals faster,
- change stdout progress wording or timing.
A minor release may not change the output bytes (vendor/, composer.lock,
or the contractual stdout of show/why/validate) that Composer's pinned
version would produce for a given input, unless it's fixing a bug in a
previous release's output.
When a release moves the pinned Composer version, its JOURNAL.md entry
says so, and the compat sweep results in compat/results/ are regenerated
against the new version as part of that release (see
AGENTS.md, "Before a release").