Stack detection
Most wrong frontend edits are correct code for a stack the project does not use. Detection is
cheap; guessing is not.
When this fires
Before the first edit in a frontend repo you have not already mapped in this session, and again
whenever you catch yourself about to say "this project probably uses". It does not fire for a repo
whose conventions you have already read this session.
Procedure
- Find the right
package.json first. In a monorepo the root one describes the workspace,
not the app. Locate the nearest package.json above the file you are changing, and note
whether a root one also exists — dependencies can be hoisted, tooling config usually is.
- Read dependencies and scripts together.
dependencies and devDependencies say what is
installed; scripts say what is actually run. The dev, build and test commands are the ones to
use later — do not invent npm run dev if the project spells it differently.
- Get real versions from the lockfile, not the range.
^15.0.0 in package.json is a
constraint. The lockfile (package-lock.json, pnpm-lock.yaml, yarn.lock, bun.lock*) holds
the resolved version, and its filename is also the only reliable statement of which package
manager to invoke. Two lockfiles present is a finding, not a choice.
- Read the config files that exist, and note the ones that do not. Build/framework config,
TypeScript config, lint and format config, test config. Their presence tells you the tool is
wired in; their contents tell you path aliases, strictness, and what is excluded.
- Confirm the routing and rendering model from the directory layout, not from the framework
name. Which route directories exist, where the entry file is, whether routes are file-based or
declared in code, and — for frameworks with a server/client split — which files carry the
directives that mark the boundary.
- Identify the styling layer and its version. A config file's existence does not pin a major
version; check the lockfile and how the stylesheet imports the framework. Also check for a CSS
modules / styled-in-JS / vanilla CSS layer coexisting with it — mixed styling is common and
changes what you should write.
- Check for a vendored component layer.
components.json or an equivalent marker plus a UI
directory means components were copied in and are now project source (see shadcn-ui).
- Open two or three real source files in the area you will touch. Config says what is
possible; the source says what this team actually does — import style, file naming, test
colocation, state library usage.
- Write the stack down in one short block, with the file each claim came from, and a separate
list of what you could not determine. That list is part of the result.
What a signal does and does not license
- A framework in
dependencies → the framework is installed. It does not tell you which
router, rendering mode or directory convention is in use. Read the layout.
- A config file exists → that tool runs. It does not tell you the major version, and majors
move config between files. The lockfile decides.
- TypeScript installed → files are typed. It does not mean
strict is on, that any is
discouraged, or that generated types are current. Read tsconfig and one real file.
- A test framework installed → tests can run. It does not mean they pass, cover this area, or
are run in CI. Running them is a separate act.
- A state or data library installed → it is available somewhere. It does not mean the module
you are editing uses it. Grep the directory you are in.
components.json present → a component CLI was used at some point. It does not mean the
components are unmodified, current, or the only UI layer.
- A lockfile entry → what would install.
node_modules is what actually runs; if behaviour
contradicts the lockfile, the installed tree is the tiebreaker.
Checklist
Failure handling
- No lockfile — versions are unknown. Say so; do not resolve the range in your head. Behaviour
that depends on a major version has to be checked against the installed tree or the docs.
- Conflicting signals (two lockfiles, a config for a tool that is not installed, a framework
version that contradicts the layout) — report the conflict. Picking one silently makes you the
source of the next bug.
- Vendored or patched dependencies (
patches/, resolutions/overrides, a checked-in fork) —
upstream documentation may not describe what runs here. Read the patch.
- Detection disagrees with the user — say what you found and where, then follow the user.
Do not quietly rewrite their description of their own project.
Evidence to report
A short stack block — package manager, framework and version, router/rendering model, styling
layer and version, component layer, TypeScript posture, test setup, dev/build/test commands — with
the file each line came from, followed by an explicit "not determined" list. A stack summary with
no file references is a guess with formatting.
1---2name: stack-detection3description: Establish what a frontend project actually uses — framework, router, package manager, styling, component layer, TypeScript posture — from package.json, lockfiles, config files and the source itself, before loading framework-specific guidance or writing a line of code. Fires at the start of any frontend task in an unfamiliar or half-remembered repo, and whenever you are about to assume a convention. Not for choosing a stack for a new project, and not a substitute for reading the code you are about to change.4---56# Stack detection78Most wrong frontend edits are correct code for a stack the project does not use. Detection is9cheap; guessing is not.1011## When this fires1213Before the first edit in a frontend repo you have not already mapped in this session, and again14whenever you catch yourself about to say "this project probably uses". It does not fire for a repo15whose conventions you have already read this session.1617## Procedure18191. **Find the right `package.json` first.** In a monorepo the root one describes the workspace,20 not the app. Locate the nearest `package.json` above the file you are changing, and note21 whether a root one also exists — dependencies can be hoisted, tooling config usually is.222. **Read dependencies and scripts together.** `dependencies` and `devDependencies` say what is23 installed; `scripts` say what is actually run. The dev, build and test commands are the ones to24 use later — do not invent `npm run dev` if the project spells it differently.253. **Get real versions from the lockfile, not the range.** `^15.0.0` in `package.json` is a26 constraint. The lockfile (`package-lock.json`, `pnpm-lock.yaml`, `yarn.lock`, `bun.lock*`) holds27 the resolved version, and its filename is also the only reliable statement of which package28 manager to invoke. Two lockfiles present is a finding, not a choice.294. **Read the config files that exist, and note the ones that do not.** Build/framework config,30 TypeScript config, lint and format config, test config. Their presence tells you the tool is31 wired in; their contents tell you path aliases, strictness, and what is excluded.325. **Confirm the routing and rendering model from the directory layout**, not from the framework33 name. Which route directories exist, where the entry file is, whether routes are file-based or34 declared in code, and — for frameworks with a server/client split — which files carry the35 directives that mark the boundary.366. **Identify the styling layer and its version.** A config file's existence does not pin a major37 version; check the lockfile and how the stylesheet imports the framework. Also check for a CSS38 modules / styled-in-JS / vanilla CSS layer coexisting with it — mixed styling is common and39 changes what you should write.407. **Check for a vendored component layer.** `components.json` or an equivalent marker plus a UI41 directory means components were copied in and are now project source (see `shadcn-ui`).428. **Open two or three real source files** in the area you will touch. Config says what is43 possible; the source says what this team actually does — import style, file naming, test44 colocation, state library usage.459. **Write the stack down in one short block, with the file each claim came from, and a separate46 list of what you could not determine.** That list is part of the result.4748## What a signal does and does not license4950- A framework in `dependencies` → the framework is installed. It does **not** tell you which51 router, rendering mode or directory convention is in use. Read the layout.52- A config file exists → that tool runs. It does **not** tell you the major version, and majors53 move config between files. The lockfile decides.54- TypeScript installed → files are typed. It does **not** mean `strict` is on, that `any` is55 discouraged, or that generated types are current. Read `tsconfig` and one real file.56- A test framework installed → tests can run. It does **not** mean they pass, cover this area, or57 are run in CI. Running them is a separate act.58- A state or data library installed → it is available somewhere. It does **not** mean the module59 you are editing uses it. Grep the directory you are in.60- `components.json` present → a component CLI was used at some point. It does **not** mean the61 components are unmodified, current, or the only UI layer.62- A lockfile entry → what would install. `node_modules` is what actually runs; if behaviour63 contradicts the lockfile, the installed tree is the tiebreaker.6465## Checklist6667- [ ] The `package.json` read is the one governing the file being changed68- [ ] Package manager identified from the lockfile, not from habit69- [ ] Framework and styling **versions** taken from the lockfile70- [ ] Routing/rendering model confirmed from the directory layout71- [ ] Dev, build and test commands quoted from `scripts`72- [ ] At least two real source files read for convention73- [ ] Unknowns written down rather than filled in by assumption7475## Failure handling7677- **No lockfile** — versions are unknown. Say so; do not resolve the range in your head. Behaviour78 that depends on a major version has to be checked against the installed tree or the docs.79- **Conflicting signals** (two lockfiles, a config for a tool that is not installed, a framework80 version that contradicts the layout) — report the conflict. Picking one silently makes you the81 source of the next bug.82- **Vendored or patched dependencies** (`patches/`, resolutions/overrides, a checked-in fork) —83 upstream documentation may not describe what runs here. Read the patch.84- **Detection disagrees with the user** — say what you found and where, then follow the user.85 Do not quietly rewrite their description of their own project.8687## Evidence to report8889A short stack block — package manager, framework and version, router/rendering model, styling90layer and version, component layer, TypeScript posture, test setup, dev/build/test commands — with91the file each line came from, followed by an explicit "not determined" list. A stack summary with92no file references is a guess with formatting.