Repository navigation
An enterprise upgrade with the team's own libraries, and its video - #13
Merged
Merged
Conversation
…h to upgrade first A scope with a registry of its own, apart from the default one, is how a company publishes its libraries, so a blocker from one is most likely the team's, and waiting for it would wait on themselves. The plan says so on the blocker, and when several are, it names the order to upgrade them in, the one another needs first. A company-wide mirror set as the default registry says nothing about owners, so it doesn't count.
When the plan says a blocker comes from the person's own registry, the choices gain one: upgrade it first, in its own repository, in the plan's order, and publish once they say so. A new reference covers a library workspace: no app to run, each library's peer ranges and version to move, building in dependency order, and publishing only with their go-ahead.
A dev container, .nvmrc, engines field or CI workflow pinned below what the new Angular needs fails for the team while this machine passes. The skill raises such a pin, keeping its form, and says so under hand fixes, and the report names it. It still never switches this machine's Node.
…and a dev container Acme's portal is on Angular 21, and its UI comes from Acme's own libraries, published to a private registry, whose peer ranges stop at 21. So the libraries have to move first. The example holds both repositories as templates from Angular CLI 21 and pnpm 12, a Verdaccio config standing in for the company registry, and a setup script that publishes the 21.4.0 libraries and makes each a git repository. The portal's dev container pins Node 22.12, which Angular 21 accepts and 22 doesn't, keeps node_modules in a volume of its own, and reaches the host's registry through a small forwarder.
…ecisions The enterprise example and the plan's own-registry blockers are done. The decisions record what marks a library as the team's, why the skill raises the repo's own Node pins, and why the example is templates a setup script copies out.
Angular drops the whitespace between elements, so the two ran together.
In the enterprise run, ng update quoted @acme/ui-core 21.4.0's Angular 21 peers right after 22.0.0 was published, because pnpm 12's minimumReleaseAge kept the new release out. The guide now says how to recognise it, that pnpm add exempts a version it's given by name, and that exempting relaxes a safeguard, so it's the person's call.
…ions Angular CLI 22's update resolver drops versions younger than pnpm's minimum-release-age, 24 hours by default from pnpm 11, or yarn 4's npmMinimalAgeGate, and never reads minimumReleaseAgeExclude. So a just-published internal release fails ng update's peer check for a day even once exempted, as the enterprise run found and update-resolver.js in @angular/cli 22.2.6 confirms. The guide gives both ways on: wait, or install the exempted releases first and update with --allow-dirty, with the person's go-ahead.
In the enterprise run the agent searched JSON and YAML files for Node pins, reported none, and missed the dev container's Dockerfile, which pinned node:22.12.0. The dev container then failed: Angular 22's CLI needs 22.22.3. The plan now reads .nvmrc, .node-version, .tool-versions, engines and volta in package.json, FROM lines in a dev container or root Dockerfile, the dev container's image and node feature, and node-version in GitHub workflows, and names each pin below a major's Node range under that major, or under the current one when the app is already there. A bare major such as 22 floats to its newest release, so it counts as fitting.
…ran into Claude Code took the libraries and then the portal to Angular 22 in six turns. The roadmap records the two release-age snags and the missed dev container pin, and where the skill now covers each. The dev container, rebuilt afterwards, builds and serves the app on Node 22.23.4.
The narration, scenes and build scripts for the HyperFrames video, which go from the plan to the rebuilt dev container. The voice, the timings and the render are made again from them, so they stay out. The example's README points here, and fonts are marked binary.
… line The render's text runs a little wider than the snapshots', so @acme/ui-forms broke at its hyphen in the intro, the date split in option 1, and a title bar wrapped.
Verdaccio 7 refuses any Node below 24, so a reader on Node 22 stopped at step 1, and Angular CLI 22 refuses Node 24 below 24.15 later in the hop. Setup also runs git init, which the list left out.
…its Node corepack@latest is 0.36.0, which declares Node 22.12 unsupported, so the image warned EBADENGINE and changed with each corepack release. 0.34.7 is the last corepack whose engines include Node 22.12, and it still starts pnpm 12 from package.json's packageManager.
…lume
A Docker volume name is shared by the whole machine, and setup makes a new
copy each run, so every copy's container installed into the same
acme-portal-node-modules. ${devcontainerId} differs between folders and
stays the same when the container is rebuilt.
…the app's checks in it A plain devcontainers up reuses a container made before the upgrade, on its old Node, and reports success once pnpm install is done, before anything runs Angular. So the README now rebuilds it and runs the build and tests through exec, with --watch=false so the tests don't wait for changes in a terminal. VS Code's local window offers Rebuild and Reopen in Container; Rebuild Container shows only inside the container.
The container reaches the registry through host.docker.internal, which Docker Desktop leads to the host's 127.0.0.1, where Verdaccio listens. Docker Engine on Linux has no such name by default, and a 127.0.0.1 listener wouldn't answer the bridge address anyway, so there the container's install can't fetch the @acme packages.
… release is minutes old pnpm holds a release back for its first day, so every reader who goes on straight after publishing the libraries' 22.0.0 meets this stop, and Angular CLI 22's ng update applies the same rule itself, ignoring the exemption. The recorded run took two extra turns on it, and step 3 said nothing about it.
…to 22 The registry keeps what each run publishes. Rerun from the same clone after a walkthrough, setup only checked that 21.4.0 was there, so the new portal's plan said Angular 22 was reachable and the walkthrough's first step didn't happen. Setup now stops before copying anything, and both it and the README say how to start over.
The search for the repo's root walks up from the project to the first .git. The test project sat in a plain temp folder, so a git repo anywhere above it, such as a dotfiles repo in the home folder, became the root, and the test's own workflow stopped being read.
At warn, Verdaccio 7 prints nothing at all once it's listening, so the terminal step 1 asks the reader to leave running stays blank and looks stuck. At info it prints its address and "server started", then a line per request, and only @acme traffic reaches it.
From Angular CLI 22.1, ng update skips a release younger than the package manager's minimum release age, a day by default on pnpm 11 and newer and on yarn 4, and reads no exemption list. So a library the team published that morning fails the peer check. The guide said to install it first and run the plan's update with --allow-dirty, which update.mjs rejects, along with any dirty tree. update.mjs now takes --install-first <library>. From a clean tree it adds each one in the form package.json gives it now (^, ~ or exact, as ng update writes it), checks that only package.json and the lockfile changed, and then updates the rest with --allow-dirty. An exemption pnpm writes on its own stops it before the update, because that's the person's call. yarn is found the way pnpm is, through its shim and with no shell. Checked with real pnpm 12.16.1, yarn 4.18.1 and git against a release ten minutes old: the caret and a tilde survive, a devDependency stays one, and strict pnpm fails before anything changes. Typed into cmd.exe, `pnpm add pkg@^2.0.0` saved "2.0.0", since ^ is cmd's escape character, so the script does the add itself.
…t must be named Ten one-line changes to plan.mjs left every test passing: dropping any of .nvmrc, .tool-versions, the dev container's image or node feature, engines or volta, the walk to the repo's root, the pins on an app already there, making engines always fit, or counting a scope pointed at the default registry as the team's. The pins test only had values that fit for most of them, and the mirror half of the own-registry test passed even with nothing to block. Now one run holds a value below the range in every place the plan reads, an app already on the target names its pin, an app below the repo root gets the root's workflow, and a scope set to the default registry blocks without a word about owners. Each of those changes now fails its test.
A dev container tag such as typescript-node:5.2-24-bookworm starts with the image's own version, and the plan read 5.2 as the Node, so the pin was below for every Angular and no Node the agent picked could clear it. The Node is now the two-digit major after that prefix, and a tag that is only the image's version, such as 5-trixie, names none. Read against all 1056 typescript-node tags on MCR, none gives a wrong major. engines ">=24" passed for Angular 22, whose CLI stops on Node 24.0 to 24.14. An engines range now fits only when the lowest Node it allows is in Angular's range. An engines range nothing meets, such as ">=22 <20", or a number past 2^53 in .nvmrc threw from semver and ended the plan with a stack trace. A value semver can't hold now counts as no pin, as one it can't read already did.
devcontainer.json allows comments, and so does a workflow's YAML. A commented-out image or node-version was listed as a pin to raise, and a commented-out image above the one in use hid the real one, since only the first image line is read. Whole-line comments are now dropped first.
…npm and yarn The section said pnpm won't resolve a release younger than its minimum release age. pnpm 12.16.1 installed one ten minutes old as soon as it was named, and wrote the exemption itself. What skips it is ng update, from Angular CLI 22.1 (22.0.9 has no such filter), which reads no exemption list. The check it gave, `pnpm config get minimumReleaseAge`, prints undefined for the one-day default, so the release's age from npm view is the check now. yarn 4 gates the same day by default and refuses the release itself with YN0016 until .yarnrc.yml preapproves it, and nothing covered it. The way on was to pass --allow-dirty to an update.mjs that rejects it. Now it's to commit the approved exemption by itself, which a frozen install still accepts, and run the plan's command with --install-first, except for a library that ships migrations, which ng update would skip. A release-age exemption joins the list of things that wait for the person's approval.
…s warning Passing an args array with shell: true makes Node 24 print DEP0190, a security deprecation warning, during every Windows setup. Node joins the arguments with spaces anyway, and setup's pnpm arguments are fixed, with none from input and none with a space, so they now go as one command line. git still runs without a shell.
Step 4e installs what ng update didn't move, which on pnpm is a pnpm add of the plan's newest fitting release. When that release is under a day old, pnpm 12.16.1 writes it into minimumReleaseAgeExclude in pnpm-workspace.yaml, creating the file if it has to, and says only "Added 1 entry". That's a safeguard relaxed without the person, so the step now says to ask before committing it, and to undo the whole add on a no, since a frozen install with only the entry removed still installed the release from the lockfile.
…dy there Already on Angular 22 with a dev container on node:22.12.0, the plan printed "Nothing to upgrade" and then the Node pins below what Angular 22 needs. The skill's already-there path ended with "That's the whole job", and step 4e only knew the per-major label, so a pin an earlier upgrade left behind would stay failing on the team's machines. The path now raises them, in the patch commit or one of their own, and 4e names both labels.
When no library was ready, every one left went in by name, so a library that only needed two that need each other could come before what it needs, and the first library's needs were never shown, so a cycle read like a clean order. An optional peer also counted as a need, which could make a cycle out of a plain dependency and print the order backwards. Optional peers no longer count. When nothing is ready, one library whose every need leads back to it starts, so the ones that only need it come after. Every library with needs says so, and the note names the libraries that need each other, to upgrade and publish together.
The release-age section said yarn 4. yarn's own releases say otherwise: 4.9.4 has no npmMinimalAgeGate, 4.10.1 added it with a default of 0, and 4.15.0 made the default a day, which 4.18.1 prints as 1440. Angular's CLI reads whatever yarn prints, so an app on yarn 4.0 to 4.14 isn't held back unless someone set the gate. The section, its pointer and libraries.md now say yarn 4.15 or newer.
… is the team's A licensed vendor serves its packages from a registry set for its scope, as Infragistics does for @Infragistics, the same way a team serves its own. The plan called any such registry "your own", stated as fact, so a vendor's blocker read as the team's library to upgrade first. The blocker now says the npm config sets a registry for its scope, with the URL, and the note keeps "If they are your team's" before offering to upgrade first. With only one library, the note no longer gives an order of one.
…ames The check that a key from the app's own .npmrc is in npm's listing used `in`, which also matches names every object has, such as constructor. Object.hasOwn matches only what npm listed.
javascript-node has no 5.2.5-20-bookworm tag on MCR. 4.0.10-20-bookworm is published, and it still starts with the image's own dotted version.
engines "*" was listed as a Node pin below the range, since its lowest Node is 0.0.0, so the agent was told to raise a value that keeps the team on no Node in particular and can't be raised and keep its form. semver also makes "*" of ">=0" and "x". A range with an upper bound, such as "<20", still starts at 0.0.0 and is still named.
…erves this app The plan reads Node pins in the app's folder and at the repo's root, and now the root's package.json too. In a monorepo the root's Dockerfile, workflows and engines can belong to another service, and step 4e said to raise every pin listed, inside the Angular upgrade's commit, where this app's checks can't catch what the change does there. A pin whose path starts with ../ is now raised only when it builds, tests or runs this app, and the report says which were left.
The plan no longer calls a scope's registry "your own", since a licensed vendor sets one up the same way. It says the registry comes from the npm config, and offers the order only if the libraries are the team's, so the walkthrough's first step says the same.
… as the skill does Step 3 said pnpm holds a release back for its first day. pnpm 12.16.1 installs a release it's asked for by name and writes the exemption itself. What skips it is ng update, from Angular CLI 22.1, which reads no exemption list, and that's what the troubleshooting guide and update.mjs --install-first now work from. The step now gives the same cause and the same two ways on.
A list written one version a line stopped at the first comment, whether after an item (- 22.x # current) or on a line between items, since the comment line was left blank and the value stopped at the #. The versions after it were never checked. Comment lines now go with their line break, and a comment after a value goes too.
… it left The review found that a scope's registry doesn't show who owns a library, that the plan missed a pnpm workspace's root .npmrc, and that the release-age way through needed the runner. M7's account of the run also blamed pnpm for what ng update did.
The video keeps the run as it happened, so the README names the plan wording and the release-age story that have changed since.
Setup pinged localhost:4873 without reading the answer, then published Acme's libraries to whatever registry was there. 4873 is Verdaccio's default port, so that could be a company Verdaccio. Setup now stops before copying anything if the ping fails, or if the registry serves react: the example's has only @acme packages and answers 401 for react.
…pace's root pnpm 12.16.1 refuses to add to a workspace root whose pnpm-workspace.yaml lists two or more package patterns (ERR_PNPM_ADDING_TO_ROOT) unless -w says the root is meant, so --install-first stopped there before the update. It now passes -w when the app's folder holds a pnpm-workspace.yaml that lists packages, and never below the root, where -w would add to the root instead of the app.
…om the example's Such a registry answers 401 for react, as the example's does, so it passes the new check, and a real Verdaccio set up that way did. Setup would then publish to it only if npm has a token for it. The comment names the check that would stop it, for when that matters.
…an install pnpm writes an app's lockfile and exemptions at the workspace root, and git restore . run in the app's folder left those changed, so the next update refused a tree that wasn't clean. git restore :/ covers the whole repository. pnpm makes an untracked pnpm-workspace.yaml only beside package.json when there was none, so that advice stays.
… ng won't start on it Node 24.14 is in a gap of Angular 22's range, ^22.22.3 || ^24.15.0 || >=26.0.0. It isn't below every version in the range, so the plan said unsupported, which the skill takes as fine to carry on, and Angular 22's ng exits with an error on it. Now only a Node above every version in the range is unsupported, and one in a gap is too old, since a newer Node fits.
…ly two of them In a ring of three, where a needs b, b needs c and c needs a, the note said only that a and b need each other and go out together, though c has to go with them. It now names each group of libraries that need each other, directly or through others, however many are in it. Pairs, two separate pairs and chains read as before.
…er the repo keeps them The plan reads a fixed set of files, and the skill searches the repo for the rest, so the old title promised more than the test checks.
…at fits, as its control It showed a pin below the range gets named on an app already on the target, and a plan that named every pin passed it too. A dev container on node:24-bookworm, inside Angular 22's range, now has to name none.
…npm workspace root Up to Angular CLI 22.0, ng update fetches every dependency's metadata with its own client, which reads only the app folder's .npmrc and the user's and global ones. Run below a pnpm workspace root whose .npmrc gives the team's scope a registry, CLI 21.2.29 stopped with a 404 from the public registry, while pnpm and the plan found the package. CLI 22.1 and newer ask pnpm instead.
…other together The plan now names every library in a cycle and says no order works for them, while blocked.md only said to follow the plan's order.
pnpm 12.16.1 ran a dry-run publish on main and on master, and refused one on upgrade/angular-22 with ERR_PNPM_GIT_NOT_CORRECT_BRANCH: Branch is not on 'master|main'.
…ase on its own line The plan now calls Node 24.14 too old for Angular 22, whose range is ^22.22.3 || ^24.15.0 || >=26.0.0, and CLI 22.2.6 exits on it. A newer 24 such as 24.15 is enough there, so asking only to switch Node sent people looking for a new major.
… too old The plan now calls a Node in a gap of Angular's range too-old, and Node 23 and 25 sit in gaps of Angular 22's ^22.22.3 || ^24.15.0 || >=26.0.0. CLI 22.2.6's bin/ng.js only warns on those two and runs, and CLI 21.2.29 warns on every odd major, so the skill's reason, that ng will refuse to run, was wrong there. Angular still doesn't support them, so the advice to stop and switch stays.
…, not a comment Its control matched /need each other/ anywhere in plan.mjs, and two comments there say it too, so the test still passed on a plan.mjs whose note no longer did.
When b needs x, and x and y need each other, the order read x, b, y, though the note says x and y go out together. Each group of libraries that need each other now goes into the order whole.
packages Such a registry answers 401 for react, as the example's does, so it got through, and setup would publish to it if npm had a token for it. The example's lets anyone read @acme packages.
…und deferred A Node in a gap of Angular's range now counts as too old. Node 27 on Angular 22, and ng update's .npmrc lookup below other workspaces, go to M8.
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
M7 takes an app built like a company's from Angular 21 to 22. It's an Angular 21 portal on pnpm 12, set up for a dev container. Its internal UI libraries live in their own repo, reach it through a private registry, and accept only Angular 21. Claude Code with ng-hop did the upgrade from the Windows host in six turns. It stopped and offered to upgrade the team's libraries first, then published them. It got past a release-age rule inside
ng updateand upgraded the app. A review of the whole milestone then found real problems, and they're fixed here.examples/enterprise/holdsacme-ui,acme-portaland a local Verdaccio, as templates thatsetup.mjscopies out. Its README walks through the run, and the setup stops on a registry that isn't the example's or that an earlier run already moved to 22..npmrc. It didn't before, so a blocked app looked reachable..nvmrc,.node-version,.tool-versions, a Dockerfile, the dev container,enginesandvolta(in the app's folder and at the repo's root), and from GitHub workflows. The run missed the dev container's pin, and the container then refused Angular 22:^22.22.3 || ^24.15.0 || >=26.0.0, now counts as too old, sincengwon't start on it. The plan called it unsupported before, which the skill reads as carry on.ng updateskips a release younger than the package manager's minimum release age, and reads no exemption list.update.mjs --install-firstinstalls such a library first, and the troubleshooting guide gives the cause and both ways on.docs/video/enterprise/holds the source of a 7:52 tutorial made from the real run with HyperFrames. The video file isn't in the repo.Checked
npm testpasses on Windows: 296 tests, 287 pass, 9 skipped (the POSIX-only ones). The plan, update and skill tests also passed on Node 18.20 and 20.20 before the last plan fix.--install-first; the Angular CLI's source for the age filter; Docker Desktop and@devcontainers/clifor the example's dev container; Verdaccio 7.0.2 for the setup.hyperframes checkreports no findings, and the render is 7:52 at 1080p30, -16 LUFS.