Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .changeset/check-repo-typescript.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
"uptide": patch
---

`check` compiles your code with the repository's own TypeScript (the `node_modules/typescript`
its build uses, found the way `fix` has found it since verification), and falls back to the
bundled compiler only when the repository installs none. Errors and their positions are the
ones your `tsc` would print: a repository on TypeScript 4.9 is no longer judged by TypeScript
6's rules. The coverage line says which compiler judged: `compiled 355 of 356 files in 5
workspaces with the repo's TypeScript 4.9.5`.

A workspace without a tsconfig of its own is now configured by the nearest one above it (a
monorepo root whose `include` covers the workspace), for its options only; synthetic defaults
apply only when no tsconfig exists anywhere.
4 changes: 3 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,9 @@ open a **Pack request** issue, and say you are writing it.
These are what make Uptide safe to run on someone else's code. A pull request that breaks
one is not merged, whatever else it does.

- `check` makes no LLM call and executes nothing from the repository it reads.
- `check` makes no LLM call and runs no script or code of the repository it reads. The one
package it loads from the repository's `node_modules` is its installed `typescript`, to
compile with (as `verify` does); the bundled compiler stands in only when there is none.
- Dependencies are installed with lifecycle scripts disabled; third-party code is never run
outside the designed verification step of `fix`.
- `fix` and `verify` work in a temporary clone. The user's checkout, services, git config
Expand Down
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,14 @@ zod 3.25.76 → 4.6.5 major · latest on npm verifie
stripe (packages/api) 14.25.0 → 23.0.0 9 majors behind · latest on npm verified ✗ 4 breaking in 3 files 1 auto-fixable · 3 need the agent (LLM)

zod 28 breaking · compiled against 4.6.5: 28 new type errors
compiled 11 of 11 files in 2 workspaces
compiled 11 of 11 files in 2 workspaces with the repo's TypeScript 5.9.3
✗ New error API (required_error → error) 24 sites auto-fixable
✗ ZodTypeDef removed 1 fix, 3 errors needs the agent (LLM)
✗ .ip() removed monitoring.ts:6 needs the agent (LLM)
! 15 deprecated calls (.uuid, .datetime, .email, ...) 8 auto-fixable

stripe 4 breaking · compiled against 23.0.0: 4 new type errors
compiled 7 of 7 files in 1 workspace
compiled 7 of 7 files in 1 workspace with the repo's TypeScript 5.9.3
✗ Test fixture casts widened renewal.test.ts:7 auto-fixable
✗ Subscription billing period moved to items 2 sites needs the agent (LLM)
✗ apiVersion no longer matches the SDK client.ts:9 needs the agent (LLM)
Expand Down Expand Up @@ -216,8 +216,9 @@ compiler rejects the code against the target, the runtime probe saw the export g
Everything else the declaration diff suggests is **possible impact**: the types changed where
your code uses them, nothing confirmed that it breaks, so it is listed under its own line and
never counted as breaking. Under every analyzed package one line says how much of the code that
uses it the compiler judged (`compiled 11 of 11 files in 2 workspaces`); when files were
skipped, the verdict says "types partly verified" and why.
uses it the compiler judged and which compiler (`compiled 11 of 11 files in 2 workspaces with
the repo's TypeScript 5.9.3`: your own `node_modules/typescript`, the bundled one only when you
install none); when files were skipped, the verdict says "types partly verified" and why.

The tier is on every row of `check`, in the HTML report and in the pull request. A generic
`fix` needs a provider API key (there are no rules to fall back on; without a key it says
Expand Down Expand Up @@ -350,6 +351,9 @@ for assisted fixes in `uptide fix`, and only with your own API key from ANTHROPI
OPENAI_API_KEY or GEMINI_API_KEY: for each
site the rules cannot migrate, the finding, the enclosing function and the compiler
error. `uptide fix --no-llm` turns assisted fixes off. No account.
`check` and `verify` load the `typescript` package your repository installs (resolved from its
node_modules) to compile with, and never run your repository's scripts or other code outside
`fix`'s verification step.
Anonymous telemetry is off by default and asks for consent in an interactive terminal.
Set UPTIDE_TELEMETRY=0 to disable it. No IP, code, paths or repo names are collected.
Other network use: your npm registry for package metadata and tarballs, npm's advisory
Expand All @@ -362,7 +366,7 @@ There is no Uptide server. With telemetry off (the default):
| | Where it runs | What leaves your machine |
| --- | --- | --- |
| `list`, `plan` | locally | package names/versions requested from your npm registry, and `list` sends public packages' names and installed versions to npm's advisory endpoint; metadata only, no source code |
| `check` | locally | nothing of yours; it downloads package tarballs from your npm registry. No LLM call, and nothing in your repository is executed. |
| `check` | locally | nothing of yours; it downloads package tarballs from your npm registry. No LLM call. It loads your installed `typescript` package to compile with, and runs no script or other code of yours. |
| `fix`, rules and verification | locally, in a temporary clone | nothing of yours |
| `fix`, assisted fixes | Your chosen provider's API, with **your** environment API key | per site no rule covers: the finding, the enclosing function or declaration, and the compiler error |
| `pr`, `fix --pr` | GitHub, through your own `gh` | the branch and the pull request, when you say so |
Expand Down
21 changes: 14 additions & 7 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@ isolates, what it does not, and how to report a problem.

## Isolation model

**`uptide check`** executes nothing from your repository. It reads your files and lockfile,
downloads the installed and the target version of the dependency from your npm registry as
tarballs (no install, no lifecycle scripts), compiles your code against the target in
memory, and loads the two versions of the *dependency* in a sandboxed Node process to
compare runtime behavior. It makes no LLM call.
**`uptide check`** runs none of your repository's scripts or code. It reads your files and
lockfile, downloads the installed and the target version of the dependency from your npm
registry as tarballs (no install, no lifecycle scripts), compiles your code against the
target in memory, and loads the two versions of the *dependency* in a sandboxed Node process
to compare runtime behavior. It makes no LLM call. To compile, `check` (like `verify`) loads
the `typescript` package your repository installs, resolved from its `node_modules` (the
nearest one above the workspace; never `NODE_PATH` or a global install), in Uptide's own
process; the bundled compiler is used only when the repository installs none. That package is
the one thing from your `node_modules` that `check` loads, and it is the compiler your own
build already runs.

**`uptide fix`** and **`uptide verify`** work in a temporary clone, never in your checkout:

Expand All @@ -25,7 +30,8 @@ compare runtime behavior. It makes no LLM call.
- Git hooks are disabled for every command Uptide runs.
- Verification runs **your code**: your type-check, your tests, your formatter and linter,
inside the clone. This is the one place repository code is executed, and it is the point
of the command. Tests that need a database, cache or queue do not run unless you pass
of the command. The type-check loads the clone's installed `typescript` package, as
`check` does, and compiles with it in Uptide's process; no script of yours runs for it. Tests that need a database, cache or queue do not run unless you pass
`--with-services --yes`, after Uptide prints what they would connect to.
- Child processes for install and tests do not receive your LLM API key or GitHub token.

Expand Down Expand Up @@ -55,7 +61,8 @@ command, and what happened that the model above says should not.
You will get an answer within 7 days. Fixes are released under the `next` dist-tag first
and credited to you unless you prefer otherwise.

Reports that matter most: anything executed during `check`; a lifecycle script or git hook
Reports that matter most: anything executed during `check` beyond loading the repository's
installed `typescript` package; a lifecycle script or git hook
that ran; a write outside the temporary clone or `.git/uptide/`; code sent anywhere other
than described above; a secret reaching a child process.

Expand Down
31 changes: 28 additions & 3 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,10 @@ project references one level down) or, without one, from `**/*.ts,tsx` minus
`node_modules`, `dist` and `build`. A scoped program (the files that use one package) also
holds the ambient `.d.ts` files the tsconfig includes (`vite-env.d.ts`, `css.d.ts`): nothing
imports them, and without them `*.module.css` imports and `declare global` names fail.
Nothing in the repository is executed.
Nothing in the repository is executed, with one deliberate load: the repository's installed
`typescript` package (resolved from its `node_modules`, see the compile section) is loaded
into Uptide's process to compile with, as `verify` has done since #40; no script or other
code of the repository runs.

The compiler bundled with ts-morph is TypeScript 6, whose defaults for an option a tsconfig
leaves unset differ from TypeScript 5's: `strict` on, no automatic `@types` inclusion, a
Expand Down Expand Up @@ -352,19 +355,41 @@ inside pkg@ver, results may be incomplete"). Those never become findings: with
`skipLibCheck` an unresolved import inside a declaration file makes the type `any`,
which hides errors rather than inventing them.

The compiler is the repository's own (`src/adapters/typescript/compiler.ts`): the
`node_modules/typescript` in the workspace or the nearest ancestor that installs one, the way
its build and `fix`'s verification find it, never one from `NODE_PATH` or a global folder. Its
errors, at its positions, are the ones the repository's `tsc` prints; the newer bundled
compiler moves some (a JSX child's "not assignable to ReactNode" sits on the element's first
line in TypeScript 4.9 and on the child in 6) and judges by its own defaults. The bundled
compiler still parses the repository for Signal A (ts-morph is built on it), and when it is
also the one that judges (the repository installs none), the overlay shares the ts-morph
program. Otherwise a baseline program is built once per workspace with the repository's
compiler, from the ts-morph program's root files and the options that compiler reads from the
repository's tsconfig (an option a newer compiler dropped or defaults differently is the
repository's compiler's to read), with the same workspace-source `paths`; the overlay shares
that baseline instead. A workspace without a tsconfig of its own is configured by the nearest
one above it (a monorepo root whose `include` covers the workspace: excalidraw-app reads the
root's `jsx: react-jsx`), for its options only, never its file list; both programs read it,
and only a repository with no tsconfig anywhere gets synthetic defaults. The host's resolver is installed both ways the compilers ask
(`resolveModuleNameLiterals` from TypeScript 5, `resolveModuleNames` before it), and the cause
tracers take the compiler with the programs: node kinds and flags are its, not the bundled
one's. Which compiler judged is part of the coverage (`compilers`), and the coverage line names
it (`with the repo's TypeScript 4.9.5`, or `with the bundled TypeScript 6.0.2`; a monorepo
whose workspaces differ names each).

Every compile says what it covered (`CompileSignal.coverage`): the files it was asked about
(the package's usage files and the files importing them, per workspace) and how many it
type-checked, with a reason for each file it did not: outside the workspace's tsconfig, a
workspace whose baseline is structurally broken, an invalid tsconfig. Merged across
workspaces into `PackageReport.compile.coverage` (`compiled 355 of 356 files in 5
workspaces; skipped: ...`), it is printed under every analyzed package, and a package whose
workspaces with the repo's TypeScript 4.9.5; skipped: ...`), it is printed under every analyzed package, and a package whose
files were not all compiled gets the verdict "types partly verified" with that line instead
of "compiled against <version>" (`src/check/verdict.ts`).

Cost is proportional to the package, not the repository. The overlay is a raw compiler
`Program` that shares the baseline's parsed and bound source files and its module
resolutions (only the target and its linked dependencies are parsed, once per compile);
its host canonicalizes paths exactly as ts-morph does, since `createProgram` rewrites
its host canonicalizes paths exactly as the baseline's does, since `createProgram` rewrites
`file.path` on shared files. Only the files Signal A found usages in, plus the files
importing those, are type-checked, on both sides; a file that neither imports the package
nor imports a file that does cannot see a type of it change. Baseline diagnostics are
Expand Down
13 changes: 8 additions & 5 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,11 +108,14 @@ It is never counted as breaking.
### Coverage

Under every analyzed package, one line says how much of the code that uses it the compiler
judged: `compiled 355 of 356 files in 5 workspaces; skipped: most files cannot resolve their
imports at the installed version (1)`. The files are the ones that import the package and the
ones importing those, per workspace, each compiled under its own tsconfig; a skipped one
says why (not in the workspace tsconfig, a workspace whose baseline cannot resolve its
imports, an invalid tsconfig). When not every file was compiled, the verdict says `types
judged, and with which compiler: `compiled 355 of 356 files in 5 workspaces with the repo's
TypeScript 4.9.5; skipped: most files cannot resolve their imports at the installed version
(1)`. The compiler is the repository's own `node_modules/typescript` (the one its build runs,
so the errors and their lines are the ones `tsc` would print); only a repository that installs
none is judged by the bundled one, and the line says so (`with the bundled TypeScript 6.0.2`).
The files are the ones that import the package and the ones importing those, per workspace,
each compiled under its own tsconfig; a skipped one says why (not in the workspace tsconfig,
a workspace whose baseline cannot resolve its imports, an invalid tsconfig). When not every file was compiled, the verdict says `types
partly verified: compiled N of M files ...` instead of `compiled against <version>`: a clean
result only covers what was compiled.

Expand Down
3 changes: 3 additions & 0 deletions packages/cli/src/privacy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ export const PRIVACY = `Privacy:
OPENAI_API_KEY or GEMINI_API_KEY: for each
site the rules cannot migrate, the finding, the enclosing function and the compiler
error. \`uptide fix --no-llm\` turns assisted fixes off. No account.
\`check\` and \`verify\` load the \`typescript\` package your repository installs (resolved from its
node_modules) to compile with, and never run your repository's scripts or other code outside
\`fix\`'s verification step.
Anonymous telemetry is off by default and asks for consent in an interactive terminal.
Set UPTIDE_TELEMETRY=0 to disable it. No IP, code, paths or repo names are collected.
Other network use: your npm registry for package metadata and tarballs, npm's advisory
Expand Down
Loading
Loading