Primate compatibility and migration
Flog's automated Primate acceptance fixture lives at
internal/cli/testdata/primate-compat. It is deliberately smaller than the real
repository but preserves its relevant shape: apps/*, packages/*, the
scripts workspace, prerelease package versions, workspace:^ dependencies,
peer dependencies, npm aliases, package scripts, publishable packages, and the
root lifecycle-script allowlist.
TestPrimateEndToEndCompatibility copies that fixture to a clean temporary
directory and uses only flog plus an in-process npm-compatible HTTP registry. It
checks:
- clean installation, canonical
flog.lock, workspace links, annpm:alias, peer resolution, and one allowed versus one denied install script; - deletion of every
node_modulesfollowed by locked offline installation from flog's verified cache; - dependency-ordered recursive build and test scripts;
- every publishable package's dry-run file list and rewritten final manifest
against
expected-pack.json; - rejection of omitted and
latesttags for Primate-style prereleases; - recursive public prerelease publication under
next, including registry-requested web authentication and dependency-first upload order; - stable publication under
latest; and - refusal to publish a maintenance version behind an existing newer
latest.
The suite never reads, writes, or publishes the real Primate checkout. The registry binds only to the test process and stores uploads in memory.
Current real-repository result#
An isolated git archive of Primate origin/master at
c45c8e28c2fb83a8eb996f50f273c3e77c063c7c was tested with:
flog lock --cwd /path/to/archive
The clean run writes a complete lockfile without adding peer-provider
boilerplate to Primate's manifests. Derived importer providers include rxjs,
React type packages, @babel/core, @internationalized/date, linkedom, and
the website framework peers. Repeating flog lock with matching manifests does
not access registry metadata or change the lock bytes.
Before automatic required-peer installation, the same migration process stopped before writing a lockfile with diagnostics such as:
peer dependency missing for importer apps/angular (@app/angular): @angular/common@22.2.2 requires rxjs@^6.5.3 || ^7.4.0
Peer diagnostics name the consuming importer path (and its package name). For
transitive failures, a deterministic via chain identifies the dependency path
from that importer to the package declaring the peer.
The four manifests that directly declare @angular/common—apps/angular,
apps/frontend, apps/website, and packages/angular—do not declare rxjs.
After adding compatible providers in a disposable copy, strict resolution next
reports that a consumer of workspace @primate/react@0.23.0-pre does not
provide @types/react-dom@19. Primate currently relies on its existing package
manager to supply undeclared peers automatically. Flog now resolves these
required providers automatically and records their exact placement and versions
in flog.lock; those four manifests do not need boilerplate rxjs
declarations. Optional peers remain absent unless explicitly provided.
Compared with npm, flog deliberately keeps its non-hoisted nested layout and
does not silently override incompatible explicit providers. All consumers in
one peer environment must share one compatible provider or resolution fails.
Generate the first real flog.lock only after a clean flog lock run
succeeds and inspect the derived peerProviders edges.
Root manifest#
Change the package-manager declarations to the reviewed flog release:
{
"packageManager": "flog@<version>",
"devEngines": {
"packageManager": {
"name": "flog",
"version": "<accepted-range>",
"onFail": "error"
}
}
}
Keep the existing workspace patterns. Keep allowScripts.esbuild and
allowScripts.oracledb only after reviewing those exact packages; flog denies
all other dependency lifecycle scripts by default.
Remove nub.lock, generate the lock for both the contributor glibc environment
and Alpine website builder, inspect it, and commit it:
flog lock \
--target linux/x64/glibc \
--target linux/x64/musl
Flog's only supported prerelease lock schema is version 0. If the migration
branch already contains the same target-matrix shape from an earlier development
artifact labeled version 2, preserve the reviewed graph and change only the root
version value from 2 to 0. Verify that one-line diff before running plain
locked installs. Flog intentionally does not parse or rewrite the old label.
Discard and regenerate an earlier single-target development lock instead of
relabeling its incompatible shape.
Do not remove language-specific locks such as Gemfile.lock. Contributor, CI,
and release builds should then use plain flog install, which strictly requires
the represented target graph to match and does not rewrite an existing lock.
The website image in apps/website/Containerfile should also use plain
flog install; install itself now enforces the missing-lock assertion.
No npm, pnpm, yarn, bun, or Nub command should update the dependency tree or lockfile after the switch.
Command migration#
Root scripts map as follows:
| Current command | Flog command |
|---|---|
| Nub recursive package/app build | flog build |
| Nub recursive package-only build | flog run -r --workspace='./packages/**' build |
| Nub recursive app-only build | flog run -r --workspace='./apps/**' build |
nub run -r test |
flog run -r --if-present test |
| Nub app-only tests | flog run --workspace='./apps/**' --if-present test |
selected app test:backend / test:frontend |
use the same workspace selector plus --if-present |
nub publish -r --access=public for current prereleases |
flog publish -r --access public --tag next |
nub update -r -i -L |
flog upgrade -r --latest (review the complete plan, then confirm) |
nub view <package> version |
flog view <package> version |
Within package scripts, replace nub run <script> with
flog run <script>. Replace npx proby with proby; flog places dependency
binaries on the script PATH without invoking another package manager. For a
direct shell invocation, use flog x proby.
Remove app prebuild hooks whose only purpose is to recursively query and build
an app's dependencies.
Build from the monorepo root with the first-class source graph instead:
flog build --workspace apps/name
For the website deployment builder, use the same production closure for both
steps and keep the app's own "build": "primate build" script:
RUN flog install --workspace apps/website --prod
RUN flog build --workspace apps/website --prod
Do not call flog build --prod from the website's prebuild; that would select
the website again while it is already being built. --prod omits the Angular,
Go, database, Python, React, Ruby, Solid, Svelte, test, and Vue workspaces that
are reachable only through website development edges, while retaining registry
compilers, types, generators, and shared root build tooling for every selected
source workspace. It does not produce the final runtime image; copy the bundled
website output in the later image stage as before.
For package/app release validation, retain every permitted result even when an earlier package fails:
flog run -r --if-present --no-bail --jobs auto --json test > test-results.json
Failed selected dependencies block their selected dependents, while later independent packages still run. The versioned report retains success, failure, signal, missing/skipped, blocked, and interrupted outcomes and exits nonzero if the release validation is incomplete or unsuccessful.
flog build is itself the top-level graph operation. Nested Flog invocations
used for other package-local work still coordinate successful equivalent work,
but no nested dependency-only prebuild is needed for ordinary app builds.
Dependency maintenance uses recursive within-range dry runs and deterministic
JSON. Crossing range bounds uses flog upgrade --recursive --latest only at a
real terminal with explicit confirmation; it cannot be scripted or combined
with JSON or dry-run output. Both paths preserve workspace protocols, aliases,
prerelease channels, and peer compatibility contracts. After confirmation,
flog upgrade publishes the reviewed manifest and lockfile bytes and installs
their exact graph before reporting success.
See deliberate dependency updates.
Packing review#
Before replacing Primate's recursive publication command, review
flog publish --recursive --dry-run as a complete release set. Flog blocks a
workspace dependency compatibility-line transition when a published dependent
is omitted or under-bumped, and reports the transitive package chain. See
workspace release sets. Use the dedicated incomplete-set
override only for a documented compatibility exception.
Build packages, then inspect every publishable artifact without writing it:
flog run -r --workspace='./packages/**' build
flog pack -r --workspace='./packages/**' --dry-run --json > pack-plan.json
The schema-versioned pack plan includes all publishable package matches under
packages and records private matches such as @primate/test under omitted
with their importer path and private reason. Private apps outside the selection
do not appear in either collection.
Review each file list and final manifest. In particular, every workspace:
specifier must have become its concrete published range. To retain archives for
manual inspection, replace --dry-run with --destination ./artifacts.
Publishing#
Primate package versions ending in -pre require an explicit non-latest tag:
flog publish -r --access public --tag next
Omitting --tag or specifying --tag latest is intentionally rejected. The
interactive command displays the complete plan before confirmation and handles
OTP or registry web authentication. Automation must opt in explicitly with
--yes and should also write --summary <path> for audit and safe resume.
For a stable release, omit --tag to use latest only after reviewing the
plan. Flog refuses to move latest behind a newer registry version and
revalidates registry state before each package upload. See
publishing.md for the remaining non-atomic registry race.
Primate's explicit --access public is preserved in the complete plan, each
package's npm-compatible upload document, and the machine-readable summary.
This is required for first publication of a scoped package because scoped
packages otherwise default to restricted. After an accepted upload, flog
confirms that the exact version is anonymously readable before reporting
published. A registry that rejects, ignores, or cannot verify the requested
access produces a failed, mismatched, or unverified outcome rather than an
implicit fallback or false public-success report; an accepted upload may already
be irreversible.
Performance sample#
On the development host, the representative fixture completed a clean local registry install in about 45 ms, locked offline reinstall in about 50 ms, recursive build and test in about 480 ms, pack inspection in about 12 ms, and web-authenticated recursive publication in about 210 ms. These numbers are diagnostic rather than a performance gate; the fixture is intentionally much smaller than Primate. The expected real-project cost driver is the initial fully nested, non-hoisted install and sequential workspace scripts.
Migration gate#
Do not open a Primate migration change until all of the following are true:
- a clean real-Primate
flog lockrun resolves every required peer, and all automatic provider placements and remaining explicit-provider conflicts have been inspected; - the generated real
flog.lock, build/test results, pack plan, and dry-run publication plan have been inspected by a human; and - within-range recursive upgrades have been dry-run and reviewed, and any
flog upgrade --recursive --latestplan has been inspected and explicitly confirmed at a real interactive terminal.