#1289 Remove shelled nmr calls from package manifests - #1291
Merged
Conversation
Drops the `build` script from `kb`, `lifecycle`, `mcp`, and `run-core`, each of which restated nmr's built-in workspace default (`['compile']`) as the shell string `nmr compile`. `nmr build` no longer warns that those four run nmr behind a shell, where a quiet failure surrenders the whole nested subtree instead of the failing step.
Moves `bundle-skill-helpers.ts` and `copy-content.ts` out of the `codeassembly` package's `build` script and into its `build:post` hook, ahead of `rdy compile`. The package resolves `build` to nmr's built-in default, so `nmr build` no longer warns that it runs nmr behind a shell. The build sequence is unchanged: both steps already ran after the compile.
Adds a root test that fails when a workspace `package.json` declares a script reaching `nmr` in command position, directly or behind `pnpm exec`. The npm lifecycle hooks are exempt, since nmr resolves none of them as a command and `prepublishOnly: "nmr build"` is the established pattern; so are the leaf bins `nmr-compile` and `nmr-fmt`, neither of which starts a nested run.
Replaces the hardcoded `packages/` path and `readdirSync` walk in both root suites with `getWorkspacePackageDirs(findMonorepoRoot(...))` from `@williamthorsen/nmr/workspace`. `pnpm-workspace.yaml` is now the authority, so a pattern added there is covered without editing either suite; a directory under `packages/` holding no manifest is skipped rather than crashing the read; and the discovery lives in one published helper instead of two copies.
…cases Extends the guard to the monorepo root's `package.json`, which nmr resolves as a tier-3 override the way it resolves a package's, and exempts `bootstrap` there by name so the deliberate pnpm-fronted call is recorded where a reader meets it. Root hooks such as `ci:post` are now covered. Widens `shelledNmrPattern` to match `nmr` as a whole token, so `npx nmr`, `pnpm --filter x exec nmr`, and `pnpm --recursive exec nmr` are reported. nmr's own warning inspects only a command's first token and reports none of those, leaving both mechanisms blind to them. Adds direct cases over `findShelledNmrScripts` covering each runner form, the leaf bins, and the exempt names, so an edit that stopped the pattern matching fails the suite instead of passing it. `WorkspaceManifest.name` becomes `GuardedManifest.manifestPath`, which no longer collides with the sibling suite's package-name `name`.
Dependency auditProduction dependency audit passed. |
williamthorsen
marked this pull request as ready for review
August 11, 2026 16:44
4 tasks
8 tasks
williamthorsen
added a commit
that referenced
this pull request
Aug 13, 2026
…e-v0.3.0 codeassembly-v0.9.0 factory-v0.2.6 codeassembly-mcp-v0.2.5 codeassembly-v0.9.0 - #1288 feat: State mutability and name the lede reader at the artifact type lists (#1303) - #1285 fix: Require plain bullets for checklists on both update-jira-ticket paths (#1302) - deps: Upgrade all deps to latest version - #1290 tooling: Upgrade eslint-config-typescript to 10 and complete manifest metadata (#1301) - #1294 feat: Specify artifact mutability and its sanctioned exceptions (#1297) - #1293 fix: Present and classify discretionary findings as actionable (#1295) - #1289 tooling: Remove shelled nmr calls from package manifests (#1291) - #1280 feat: Add a refresh path that edits an existing AGENTS.md instead of redrafting it (#1287) - #1055 feat: Replace the lede doctrine with corpus-derived guidance and retire the changelog-writer (#1286) - deps: Upgrade all deps to latest version - #1264 refactor: Consolidate error-message extraction on toolbelt.errors' describeError (#1284) - #1263 refactor: Retire the deferred-lint mechanism and adopt toolbelt.errors in agents (#1279) - #1262 refactor: Hoist unreadable for-of expressions and promote the lint rule to an error (#1276) - #1267 fix: Extend the no-hard-wrapping instruction to every GitHub composer (#1275) - #1177 fix: Rewrite links, tokens, and template variables in a Markdown file support entry (#1273) - #1176 feat: Add a ticketing-preferences hook to the skills that split and create tickets (#1268) - #1252 fix: Keep a skill's own sections out of injected guidance (#1266) - #1260 refactor: Fix lint and retire rule deferrals (#1265) - #1247 feat: Add a personal tooling-preferences rulebook (#1257) - #1251 feat: Let a rulebook declare that a guidance hook is how it is reached (#1259) - #1246 feat: Guard home-domain writes and record their provenance (#1254) - #1175 feat: Link new tickets to their blockers and parents (#1249) factory-v0.2.6 - deps: Upgrade all deps to latest version - #1290 tooling: Upgrade eslint-config-typescript to 10 and complete manifest metadata (#1301) - deps: Upgrade all deps to latest version - deps: Upgrade all deps to latest version - #1264 refactor: Consolidate error-message extraction on toolbelt.errors' describeError (#1284) kb-v0.6.0 - deps: Upgrade all deps to latest version - #1290 tooling: Upgrade eslint-config-typescript to 10 and complete manifest metadata (#1301) - #1289 tooling: Remove shelled nmr calls from package manifests (#1291) - #1264 refactor: Consolidate error-message extraction on toolbelt.errors' describeError (#1284) - #1261 feat: Attach causes to kb's loader errors and retire its lint deferral (#1272) - #1260 refactor: Fix lint and retire rule deferrals (#1265) codeassembly-lifecycle-v0.4.0 - #1290 tooling: Upgrade eslint-config-typescript to 10 and complete manifest metadata (#1301) - #1289 tooling: Remove shelled nmr calls from package manifests (#1291) - #1261 feat: Attach causes to kb's loader errors and retire its lint deferral (#1272) - #1260 refactor: Fix lint and retire rule deferrals (#1265) codeassembly-mcp-v0.2.5 - deps: Upgrade all deps to latest version - #1290 tooling: Upgrade eslint-config-typescript to 10 and complete manifest metadata (#1301) - #1289 tooling: Remove shelled nmr calls from package manifests (#1291) - #1264 refactor: Consolidate error-message extraction on toolbelt.errors' describeError (#1284) codeassembly-run-core-v0.3.0 - deps: Upgrade all deps to latest version - #1290 tooling: Upgrade eslint-config-typescript to 10 and complete manifest metadata (#1301) - #1289 tooling: Remove shelled nmr calls from package manifests (#1291) - #1264 refactor: Consolidate error-message extraction on toolbelt.errors' describeError (#1284) - #1261 feat: Attach causes to kb's loader errors and retire its lint deferral (#1272) - #1260 refactor: Fix lint and retire rule deferrals (#1265)
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.
What
nmr buildno longer reaches nmr through a shell, so a failing package build reports the step that failed instead of the whole nested subtree, and interrupting a run stops what would have followed. Five packages had abuildoverride doing that:kb,lifecycle,mcp, andrun-corerestated a default nmr already supplies and now resolvebuildto its built-in['compile'], andcodeassembly's two post-compile steps move into itsbuild:posthook. The warning nmr printed for each, five on every build, goes with them.A root test keeps the pattern out: It fails when any manifest in the repo, the monorepo root's included, declares a script reaching
nmr. It catches the forms nmr's own warning misses,npx nmrandpnpm --recursive exec nmramong them, and exempts the npm lifecycle names along with root'sbootstrap.Why
Debugging a failed build meant reading a whole nested subtree to find the one step that failed, and interrupting the run left the rest of the chain still to come. The warnings that flagged this appeared five at a time on every
nmr build, which is how a warning channel stops being read. Nothing needed the overrides in the first place: four of the five restated a default nmr already supplies.Details
🧪 Tests
scripts/__tests__/package-scripts.unit.test.tsreads the monorepo root's manifest and every workspace package's, and fails on a script value reachingnmras a whole token. The root manifest is covered because nmr resolves it as a tier-3 override the way it resolves a package's: at root scopeanchorDiris the monorepo root, which is how a root hook such asci:postruns at all.findShelledNmrScripts, carries direct cases of its own: each runner form it must report (nmr compile,pnpm exec nmr,pnpm --filter x exec nmr,pnpm --recursive exec nmr,npx nmr), each form it must pass over (nmr-compile,nmr-fmt --check, annmr/binpath segment), and the exempt names. An edit that stopped the pattern matching fails the suite rather than passing it green.prepublishOnly: "nmr build"is the upstream-sanctioned pattern, plusbootstrapin the root manifest, which builds and deploys from a fresh checkout where the barenmrbinary is not yet on the path.getWorkspacePackageDirs(findMonorepoRoot(...))in@williamthorsen/nmr/workspace, replacing a hardcodedpackages/path and areaddirSyncwalk in each.pnpm-workspace.yamlis now the authority on what counts as a package, and a directory holding no manifest is skipped rather than crashing the read.⚙️ Tooling
kb,lifecycle,mcp, andrun-coredrop"build": "nmr compile"and resolvebuildto nmr's built-in['compile'].mcpandrun-coreare left with noscriptsblock;kbandlifecyclekeepprepublishOnly. Publication is unaffected either way, since the reusable publish workflow runsrelease-kit publishrather than a recursive build.codeassemblymovestsx scripts/bundle-skill-helpers.tsandtsx scripts/copy-content.tsout of itsbuildscript and intobuild:post, ahead ofrdy compile. Both were already post-compile work, so the hook runs them where they ran before:rdy verify, compile, bundle, copy,rdy compile. A hook rather than a step list, becauseworkspaceScriptshas no per-package keying and a tier-2buildlist would impose this chain on every package.fleet's":"build stays, an intentional skip whose removal would start compiling a tsx-run server, as dofactory's andforeman's vite builds, which invoke no nmr..config/nmr.config.tsneeds no entry, contrary to what the warning suggests:resolveScriptreturns on thepackage.jsonhit before reaching the registry, so a config entry stays shadowed while an override remains. Reported upstream as Shelled-nmr diagnostic reports the wrong remedy, no origin, and only half the crossings node-monorepo-tools#656.Closes #1289