@redue/cli 0.1.0-alpha.2 → 0.1.0-alpha.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/CHANGELOG.md +491 -0
  2. package/README.md +107 -2
  3. package/bin/redue.mjs +132 -50
  4. package/docs/README.md +2 -1
  5. package/docs/user/agents.md +299 -15
  6. package/docs/user/configuration.md +28 -2
  7. package/docs/user/direct-typescript.md +209 -0
  8. package/docs/user/getting-started.md +23 -2
  9. package/docs/user/json.md +2 -0
  10. package/docs/user/node-script.md +154 -0
  11. package/docs/user/npm-launcher-notice.md +42 -0
  12. package/docs/user/pilot.md +146 -0
  13. package/docs/user/receipt-selection-notice.md +60 -0
  14. package/docs/user/supported-projects.md +56 -23
  15. package/docs/user/tests-direct.md +132 -0
  16. package/package.json +4 -3
  17. package/src/agent-integration.mjs +388 -0
  18. package/src/agent-policy.md +28 -0
  19. package/src/cached-state.mjs +46 -8
  20. package/src/claude-hook.mjs +449 -0
  21. package/src/cli.mjs +263 -40
  22. package/src/daemon.mjs +433 -94
  23. package/src/decision-profile.mjs +15 -1
  24. package/src/decision-validation.mjs +10 -0
  25. package/src/declared-plan.mjs +29 -6
  26. package/src/direct-run-certificate.mjs +252 -0
  27. package/src/direct-typescript-probe.mjs +24 -0
  28. package/src/direct-typescript.mjs +185 -0
  29. package/src/glob.mjs +8 -0
  30. package/src/history-worker.mjs +21 -13
  31. package/src/index.mjs +72 -19
  32. package/src/installed-inputs.mjs +7 -1
  33. package/src/linux-inotify.py +39 -10
  34. package/src/node-onboarding.mjs +172 -53
  35. package/src/node-script.mjs +364 -0
  36. package/src/npm-integrity.mjs +124 -0
  37. package/src/npm-typecheck-environment.mjs +36 -0
  38. package/src/npmrc.mjs +16 -0
  39. package/src/observation.mjs +34 -12
  40. package/src/path-identity.mjs +8 -0
  41. package/src/plan-worker.mjs +17 -17
  42. package/src/pnpm-inputs.mjs +114 -54
  43. package/src/pnpm-shim-templates.json +180 -0
  44. package/src/pnpm-shims.mjs +54 -0
  45. package/src/pnpm-typecheck-probe.mjs +11 -6
  46. package/src/presentation.mjs +9 -1
  47. package/src/probe-deadline.mjs +37 -0
  48. package/src/probe-result.mjs +7 -3
  49. package/src/project-location.mjs +34 -0
  50. package/src/run-recorder.cjs +91 -0
  51. package/src/state-store.mjs +37 -1
  52. package/src/test-direct.mjs +560 -0
  53. package/src/test-recorder.cjs +406 -0
  54. package/src/typescript-contract-probe.mjs +4 -187
  55. package/src/typescript-input-facts.mjs +92 -0
  56. package/src/typescript-invocation.mjs +92 -0
  57. package/src/typescript-list.mjs +108 -13
  58. package/src/typescript-qualification.mjs +29 -49
  59. package/src/workspace-manifests.mjs +24 -0
  60. package/src/yarn-workspace-typecheck-probe.mjs +2 -1
@@ -0,0 +1,154 @@
1
+ # General recipe (typecheck and lint scripts)
2
+
3
+ The general recipe records a project's own typecheck or lint script, run
4
+ through its package manager, and reports later whether that result still
5
+ applies to the files you have. It is a lower tier than the strict recipes:
6
+ `redue init` tries the [direct TypeScript recipe](direct-typescript.md), the
7
+ pnpm and Yarn TypeScript recipes and the [direct test recipe](tests-direct.md)
8
+ first, and uses the general recipe for a root typecheck or lint script none of
9
+ them takes. Test scripts use the direct test recipe; `init` does not propose
10
+ the general recipe for them.
11
+
12
+ It supports a project installed by npm, pnpm 9-12 (isolated or hoisted
13
+ `node_modules`) or Yarn with the `node-modules` linker, with one lockfile kind
14
+ matching any `packageManager` declaration, on macOS and Linux with Node 22 or
15
+ 24. Workspaces are supported for root scripts. `init --recipe node-script`
16
+ writes general checks only.
17
+
18
+ ## Setup
19
+
20
+ ```sh
21
+ redue init
22
+ redue start
23
+ redue run lint
24
+ redue status --sync
25
+ ```
26
+
27
+ The config gets one field for the recipe, written by `init`:
28
+
29
+ ```json
30
+ {"name": "lint", "script": "lint", "kind": "lint", "qualification": "node-script-v1", "inputs": ["**", "..."]}
31
+ ```
32
+
33
+ `redue run lint` runs `npm run lint` (or `pnpm run lint`, `yarn run lint`) with
34
+ REDUE's recorder loaded in every Node process, and npm's and pnpm's update
35
+ checks turned off. With the Claude Code hook, `npm run lint`, `pnpm lint` and,
36
+ for a script that is one plain command such as `eslint .`, that command itself
37
+ and `npx eslint .` are recognized like typecheck and test commands.
38
+ `status` and the hook's skip message name the recipe that made the receipt.
39
+
40
+ ## What REDUE claims
41
+
42
+ CURRENT means the last run's inputs are unchanged:
43
+
44
+ - every file in the checkout, except `.git`, every `node_modules`, the runner
45
+ caches listed below and the files the run itself wrote;
46
+ - the installed files in the root's and every workspace package's
47
+ `node_modules`, and Yarn's install state, by file identity;
48
+ - the lockfile and `package.json`, as files of the checkout;
49
+ - the package manager's executable and Node, by file identity, and the exact
50
+ script;
51
+ - every environment variable the run's JavaScript read directly, and always
52
+ `TZ`, `LANG`, `PATH`, `HOME`, `CI`, `XDG_CONFIG_HOME` and every `NODE_*`,
53
+ `LC_*`, `VITE_*`, `DYLD_*`, `LD_*`, `npm_config_*`, `pnpm_config_*`,
54
+ `PNPM_*`, `COREPACK_*` and `YARN_*` variable;
55
+ - the executables of npm, pnpm, Yarn and Corepack on PATH, by file identity,
56
+ since a script may run another package manager;
57
+ - small files read outside the checkout (up to 1 MB), by content, and lookups
58
+ outside it, asked again before each answer.
59
+
60
+ Runner caches are not inputs, and a run may read and write them: `.cache`,
61
+ `.vite`, `.vitest`, `.turbo`, `.nx`, `.swc`, `.jest-cache`, `.parcel-cache`,
62
+ `.angular/cache`, `.next/cache` and `.tmp` directories, `*.tsbuildinfo`,
63
+ `.eslintcache`, `.stylelintcache`, `.prettiercache`, and the
64
+ `_tmp_<pid>_<hex>` files pnpm writes and removes at every run. A file with
65
+ such a path that git does not ignore is source: reading it makes the run
66
+ UNVERIFIED. At the top of each `node_modules`, only `.cache`, `.vite`,
67
+ `.vite-temp`, `.vitest` and `.vitest-cache` are left out, and `*.tsbuildinfo`
68
+ anywhere in it.
69
+
70
+ Two installed files are rewritten by tools during a run. pnpm rewrites
71
+ `node_modules/.pnpm-workspace-state-v1.json` at the first run after an
72
+ install, through a temporary `node_modules/.tmpXXXXXX` file. vue-tsc writes
73
+ `node_modules/.vue-global-types/*.d.ts` before it reads them. These files stay
74
+ inputs. When they are the only files a run changed, and the run wrote them,
75
+ the result is kept with the files as the run left them. A recorded process
76
+ counts as writing a file it wrote; for pnpm's files, the pnpm executable must
77
+ have run. A later change to them makes the result STALE.
78
+
79
+ It does not mean a rerun would give the same result: a test that depends on
80
+ the time or on randomness can pass now and fail later on the same inputs.
81
+
82
+ ## Programs
83
+
84
+ The recorder sees only Node processes. Every other program a run starts must be
85
+ one REDUE can account for:
86
+
87
+ - `sh` or `dash`, or `bash` without `BASH_ENV`, running a command that, read
88
+ as text, starts only Node programs, package managers and shell builtins
89
+ (`cd`, `echo`, `true`, `export`, `exec` and a few more). Words are looked up
90
+ on the PATH the shell got. A Node program is the Node executable, a file
91
+ whose `#!` line runs Node, or a pnpm launcher. A version manager's `node`
92
+ shim (asdf, nodenv) is a script and does not count. Other shells read
93
+ startup files and are refused. The shell must still have REDUE's recorder in
94
+ `NODE_OPTIONS`: npm's and pnpm's `node-options` setting replaces it, and a
95
+ run under such a setting is UNVERIFIED;
96
+ - a pnpm launcher in `node_modules/.bin`: the `#!/bin/sh` script pnpm writes,
97
+ which runs `sed`, `dirname`, `uname` or `readlink` before it starts Node. It
98
+ is trusted only when its text is exactly one of the templates pnpm 8.0 to
99
+ 12.14 write (`src/pnpm-shim-templates.json`), with only the bin file and
100
+ `NODE_PATH` filled in;
101
+ - the pnpm executable (pnpm 12, also the copy Corepack downloads): REDUE cannot
102
+ see what it does, so the scripts it runs are read as text the same way,
103
+ their `pre` and `post` scripts included. Filters and recursive runs
104
+ (`--filter`, `-r`) are refused. At least one Node process it starts must
105
+ have loaded the recorder, and its configuration files outside the checkout
106
+ (`pnpm/rc`, `pnpm/config.yaml`, `~/.npmrc`, the global `npmrc`) are kept by
107
+ content;
108
+ - `ps -A -o ppid,pid`, which pnpm loaded into Corepack runs.
109
+
110
+ Anything else makes the result UNVERIFIED and names the program: `git`,
111
+ `sleep`, `find`, and native programs from installed packages such as esbuild,
112
+ turbo, tsgo, tsgolint and biome, whose reads, writes and network use REDUE
113
+ cannot see. `init` reads the script the same way and keeps a script that
114
+ starts such a program recording-only, with the program in the reason.
115
+
116
+ A command line may use `&&`, `||`, `;`, `|`, quotes without expansions,
117
+ globs in arguments and redirection to `/dev/null`. Variables, command
118
+ substitution, input redirection, subshells, here-documents and `command -p`
119
+ are refused. Setting or clearing `NODE_OPTIONS`, `PATH`, `REDUE_RUN_RECORD`,
120
+ `BASH_ENV`, `ENV`, or npm's or pnpm's `node-options` or `script-shell` is
121
+ refused, since it could stop a Node process from loading the recorder.
122
+ `node --env-file` and Node's config-file options are refused: Node reads those
123
+ files natively. `process.loadEnvFile` is recorded as a read.
124
+
125
+ ## The guard
126
+
127
+ As in the [direct test recipe](tests-direct.md#the-guard), the result is kept
128
+ but marked UNVERIFIED, with the reason, when the run read a git-ignored file
129
+ REDUE does not observe and did not create first, changed an input during the
130
+ run (a formatter's `--write`, a generated `version.ts`), listed a directory or
131
+ read a large file outside the checkout, used the network, or started a program
132
+ REDUE does not account for. Reads through a workspace package's link in
133
+ `node_modules` count where the link leads.
134
+
135
+ The package managers' own installations, Corepack's downloads and npm's log
136
+ directory are toolchain, identified by the package manager's executable, not
137
+ inputs.
138
+
139
+ ## Limits
140
+
141
+ - On macOS, a file created and removed directly in the project root during a
142
+ run (pnpm 12 does this) can be reported by the file watcher as an event on
143
+ the root itself, an observation gap that makes the run UNVERIFIED. Linux
144
+ reports the file, which is not an input.
145
+ - The pnpm executable and the pnpm launchers' helpers are trusted as
146
+ toolchain: what they read is not checked.
147
+ - Code that copies the environment and reads the copy, and native code that
148
+ reads files or variables (native addons such as Rust-based resolvers), is
149
+ not seen.
150
+ - A project whose `.npmrc` sets `node-options` stays UNVERIFIED: npm replaces
151
+ `NODE_OPTIONS` with it, which drops REDUE's recorder.
152
+ - A script that runs a build first (`npm run build && tsc --noEmit`) usually
153
+ starts esbuild or another native bundler and stays UNVERIFIED.
154
+ - Windows is not supported; the check stays recording-only there.
@@ -0,0 +1,42 @@
1
+ # npm launcher applicability correction — alpha.4
2
+
3
+ Alpha.4 withdraws automatic applicability for npm-backed TypeScript checks using
4
+ `typescript-noemit-v1`. Alpha.3 was demonstrated to admit an npm invocation whose
5
+ update notification consumes unobserved remote responses, cache and time. Those
6
+ inputs can change the outer npm result without changing the tracked project or
7
+ caller identity. The controlled reproduction does not establish a public registry
8
+ malfunction, affected-user count, or compiler completion in the failing npm case.
9
+
10
+ Npm commands still execute exactly as configured through `redue run CHECK`.
11
+ They still record PASS/FAIL. Previous PASS remains historical PASS; applicability
12
+ is now UNVERIFIED, including after synchronization or a successful new npm run.
13
+ This is a correction to the claimed boundary, not evidence that the code failed.
14
+ Supported Yarn/pnpm and independently reviewed explicit contracts are unchanged.
15
+
16
+ Until upgraded, do not rely solely on automatic npm CURRENT to skip the npm
17
+ invocation. A different verification recipe requires explicit selection and fresh
18
+ evidence; it cannot reinterpret an old npm receipt. Alpha.4 does not ship the
19
+ experimental npm direct-compiler recipe or agent activation.
20
+
21
+ ## Upgrade without losing history
22
+
23
+ First stop **every observer using this installation**, using each original config
24
+ and any explicit state directory. Then upgrade that same installation and restart
25
+ each observer with the same selections:
26
+
27
+ ```sh
28
+ redue --config /path/to/project/redue.config.json stop
29
+ npm install --global @redue/cli@0.1.0-alpha.4
30
+ redue --config /path/to/project/redue.config.json start
31
+ redue --config /path/to/project/redue.config.json status --sync
32
+ ```
33
+
34
+ Repeat the stop/start commands for each project. If you originally used
35
+ `--state-dir`, supply the identical directory on every REDUE command. If installed
36
+ with a custom npm `--prefix`, supply the same prefix during installation. On Windows
37
+ use `npm.cmd`/`redue.cmd` if required by existing PowerShell policy; do not change it.
38
+
39
+ **Do not delete receipts or use `remove-state` as an upgrade step.** An upgraded
40
+ client refuses an obsolete running observer interpretation and asks for stop/start.
41
+ Restart reconciles existing state and preserves immutable historical runs. This
42
+ correction does not silently change your project config or configured command.
@@ -0,0 +1,146 @@
1
+ # Opt-in development pilot: alpha.18
2
+
3
+ Use one fixed build for several ordinary development sessions. The owner will
4
+ invite two or three willing testers and approve their projects; nothing is
5
+ collected or uploaded automatically. External usage has **not started**. Earlier
6
+ controlled compiler/agent trials are acceptance evidence, not independent adoption.
7
+
8
+ ## Check the boundary first
9
+
10
+ Start with a trusted, single-package npm Git checkout, normally installed locked
11
+ dependencies, and an existing standalone `tsc --noEmit` script without pre/post
12
+ scripts. The direct recipe accepts only an official, unchanged TypeScript **5.x or
13
+ 6.0** package (a reviewed release, or one matching the tarball package-lock.json
14
+ names in npm's cache) and Node **22 or 24**. No workspaces, named
15
+ tsconfig selection, emitted/incremental/composite builds, custom loaders or
16
+ outside-root inputs are added by this pilot. Do not downgrade or change your
17
+ project to fit; record an unsupported setup as feedback.
18
+
19
+ Development-mode source-map support, Node preloads/inspectors/compile-cache
20
+ overrides and `TS_ETW_MODULE_PATH` are unsupported. A few inert Node settings
21
+ are accepted; see [Node settings](direct-typescript.md#node-settings). The injected
22
+ `NODE_USE_ENV_PROXY=1` context is reviewed only on Node **22.13.0**; leave the
23
+ environment and security controls intact. See the [full recipe boundary](direct-typescript.md).
24
+
25
+ CLI/package mechanics are tested on macOS/APFS, native Linux and Windows/NTFS;
26
+ Linux also needs Python 3 and [supported native storage](getting-started.md).
27
+ Actual fresh-session behavior was tested with Codex **0.162.0 on macOS**, Node
28
+ 22.13.0 and the approved exact checkout/state/socket access. This is not general
29
+ sandbox compatibility. Claude mechanics are tested, and the
30
+ [Claude Code typecheck hook](agents.md#claude-code-typecheck-hook) was exercised in
31
+ Claude Code 2.1.302 (`claude -p`). Broader Claude behavior and cross-host handoff
32
+ are **NOT RUN**. The recording shortcut is macOS/direct-recipe only.
33
+
34
+ ## Install and connect explicitly
35
+
36
+ For a new default npm installation:
37
+
38
+ ```sh
39
+ npm install --global @redue/cli@0.1.0-alpha.18
40
+ redue --version
41
+ cd my-project
42
+ redue --config redue.config.json init --recipe typescript-direct --check typecheck --dry-run
43
+ redue --config redue.config.json init --recipe typescript-direct --check typecheck
44
+ redue --config redue.config.json agent setup codex --dry-run
45
+ redue --config redue.config.json agent setup codex --apply
46
+ redue --config redue.config.json start
47
+ redue --config redue.config.json agent doctor codex --json
48
+ ```
49
+
50
+ Use the discovered check name. Review both previews before applying. The recorded
51
+ invocation is the resolved local Node/compiler with `--noEmit`, **not npm or its
52
+ lifecycle scripts**. Neither init nor setup executes verification. An existing
53
+ config is never overwritten; use the [deliberate check replacement procedure](direct-typescript.md#existing-npm-configuration-deliberate-replacement)
54
+ without losing other checks or changing config/state identity. For an owned custom
55
+ installation prefix or Windows command shims, use the [installation instructions](getting-started.md).
56
+
57
+ Run doctor inside the agent's actual command boundary too. A healthy outside-shell
58
+ result does not prove access. The owner must grant only the intended checkout,
59
+ owned state directory and exact observer endpoint through supported host controls.
60
+ The observer can write its index, run trusted configured probes in its own process
61
+ context, reload receipts and stop; endpoint access is more than a file read.
62
+ Do not disable sandboxing, bypass the proxy, or allow all sockets. If access is
63
+ unavailable, record the limitation; an installed skill is not behavioral acceptance.
64
+ See [access and diagnostics](agents.md). Doctor does not run verification or sync.
65
+
66
+ Complete setup before the first real verification obligation. When that obligation
67
+ arises, record the configured compiler check in the intended execution context:
68
+
69
+ ```sh
70
+ redue --config redue.config.json run typecheck
71
+ redue --config redue.config.json status --sync --json
72
+ ```
73
+
74
+ A successful run may remain **UNVERIFIED**. Reuse needs understood schema, healthy
75
+ observation, CURRENT, PASS and `reuse_eligible: true`; a receipt or exit code alone
76
+ is insufficient. Recovery cannot repair a run's missing start observation. A later
77
+ fully observed run can create new eligible evidence. Do not erase receipts to get green.
78
+
79
+ Start a fresh agent session after connecting, then work normally. Keep ordinary
80
+ task requests and explicit fresh-verification obligations; an explicit npm-script
81
+ request still requires npm. Let later sessions occur naturally—do not manufacture
82
+ edits, restarts or repeated queries to inflate reuse. Configured compiler evidence
83
+ does not verify tests, npm lifecycle behavior or application correctness.
84
+
85
+ ## Update or leave without losing evidence
86
+
87
+ Keep the pilot version fixed unless a real defect requires a reviewed update.
88
+ Record any build boundary. Before replacing an installation, stop every observer
89
+ using it; keep the same npm prefix, config and any explicit state selection.
90
+
91
+ ```sh
92
+ redue --config redue.config.json stop
93
+ npm install --global @redue/cli@0.1.0-alpha.18
94
+ redue --config redue.config.json agent setup codex --dry-run
95
+ redue --config redue.config.json agent setup codex --apply
96
+ redue --config redue.config.json start
97
+ redue --config redue.config.json status --sync --json
98
+ ```
99
+
100
+ Package installation alone does not update project instructions. Setup updates
101
+ intact owned content, preserves unrelated text and refuses edited managed content.
102
+ Review conflicts; do not delete ownership records to force an update. Setup metadata
103
+ can invalidate prior applicability; reassess normally and start a fresh agent session.
104
+ Historical receipts remain. See [the complete update procedure](agents.md#update-an-existing-connection).
105
+
106
+ To disconnect and uninstall, using the same installation prefix when applicable:
107
+
108
+ ```sh
109
+ redue --config redue.config.json agent remove codex --dry-run
110
+ redue --config redue.config.json agent remove codex --apply
111
+ redue --config redue.config.json stop
112
+ npm uninstall --global @redue/cli
113
+ ```
114
+
115
+ This preserves project config and receipts. Do not use `remove-state` for an upgrade
116
+ or pilot exit. Edited managed content is left intact with a conflict for review.
117
+
118
+ ## Voluntary notes, reviewed before sharing
119
+
120
+ Keep a small local note per real verification obligation, including setup failures
121
+ and abandoned attempts. Use existing local receipts/tool history only when you
122
+ choose; do not enable automatic transcript capture. Suggested fields:
123
+
124
+ | Field | Record |
125
+ | --- | --- |
126
+ | Context | Build, OS/Node/compiler/host versions, anonymous project/session label |
127
+ | Obligation | What actually needed verification; explicit fresh execution if requested |
128
+ | Decision | Consulted? Agent-visible CURRENT/PASS/eligible, STALE, unsupported or unavailable? |
129
+ | Outcome | Actual reuse with no equivalent execution; wrapped run; execution outside REDUE; or unresolved |
130
+ | Evidence | Local before/after receipt IDs and actual commands, reviewed locally |
131
+ | Cost | Measured setup/restart/query/recording/execution durations, if available |
132
+ | Friction | What failed or confused; why you continued or stopped using REDUE |
133
+
134
+ A CURRENT query is not automatically a saved run; an inspection needing no
135
+ verification is not an avoided check. Do not infer a counterfactual from a receipt.
136
+ Label estimated avoided compiler cost and its assumptions separately from measured
137
+ time. Include recording, restarts and required reruns. Whole-task time alone does
138
+ not establish causal speedup. The question is whether natural later reuse repays
139
+ the cost of creating and maintaining evidence.
140
+
141
+ Share only a voluntary owner-reviewed, redacted summary through the
142
+ [existing issue tracker](https://github.com/msiric/redue/issues). Keep private source,
143
+ paths, configuration bodies, credentials, receipts and raw transcripts out of public
144
+ issues. Sensitive correctness/security reports use the repository's private security
145
+ reporting channel. Maintain one short friction list, prioritize correctness first,
146
+ and choose the next development task from observed usage rather than a benchmark target.
@@ -0,0 +1,60 @@
1
+ # Receipt selection correction in alpha.3
2
+
3
+ **Known affected release: `@redue/cli@0.1.0-alpha.2`. Fixed in
4
+ `@redue/cli@0.1.0-alpha.3`.** No exploit or affected-user count is established.
5
+ REDUE remains an alpha.
6
+
7
+ If a wrapper persisted a newer outcome but its observer reload notification
8
+ failed, the observer could continue selecting an older passing receipt. Cached
9
+ and synchronized status could then present that older result as CURRENT and
10
+ eligible for reuse. A separate reproduced write-failure window left a new
11
+ immutable outcome recorded while replacing the selected receipt failed.
12
+
13
+ Until upgraded, do not rely solely on affected CURRENT output to skip checks.
14
+ Run verification when needed and inspect its actual outcome; a successful status
15
+ command is not proof that the latest execution passed.
16
+
17
+ ## What changes
18
+
19
+ The observer refreshes REDUE-owned receipt selection before publishing status.
20
+ Clients check that a response represents the current readable selection. Missing,
21
+ unreadable, malformed, incompatible, or replaced selection cannot authorize
22
+ reuse. A new client connecting to an alpha.2 observer returns UNVERIFIED and
23
+ asks for a restart, even when the old response says CURRENT.
24
+
25
+ A small owned pending-update record also withholds applicability after an
26
+ incomplete receipt write. Successful recording clears only that check's pending
27
+ entry. It does not discard another check's incomplete outcome or automatically
28
+ promote an orphaned journal entry. Inspect the failed run and storage error, fix
29
+ the storage problem, then record that check again. An unreadable/malformed pending
30
+ record requires operator inspection; do not delete receipts to obtain green status.
31
+
32
+ Historical PASS/FAIL and immutable `runs-v1` files remain intact. Stable applicable
33
+ FAIL remains a failure and is never reuse-eligible. This guard tracks owned
34
+ receipt replacement; it does not replace input-content validation or promise an
35
+ atomic decision against concurrent edits/writes.
36
+
37
+ ## Upgrade and restart
38
+
39
+ From each initialized project, stop its observer before replacing the installation.
40
+ After all observers for that installation have stopped, upgrade it once, then
41
+ restart each observer with its existing configuration and state selection:
42
+
43
+ ```sh
44
+ redue stop
45
+ npm install --global @redue/cli@0.1.0-alpha.3
46
+ redue --version
47
+ redue start
48
+ redue status --sync --json
49
+ ```
50
+
51
+ Use the same npm `--prefix`, project `--config`, and `--state-dir` you already use,
52
+ if customized. On Windows use `npm.cmd`/`redue.cmd` where required by the existing
53
+ shell policy. Stop all observers belonging to the installation before replacing
54
+ it, then start each with the upgraded binary. For an independently installed
55
+ candidate, its `stop` command can stop the older observer in the same explicitly
56
+ selected owned state. Do not run `remove-state`; receipts need not be deleted.
57
+
58
+ Restart preserves historical receipts and reconciles inputs. It does not promise
59
+ CURRENT: a newer failure, unstable execution, incomplete contract, changed input,
60
+ or observation uncertainty remains visible under the normal rules.
@@ -12,18 +12,30 @@ is known. When npm is not pinned in `package.json`,
12
12
  leaves the version unknown rather than guessing if that inspection fails.
13
13
 
14
14
  At the repository root, `init` looks for existing typecheck, test, lint, and
15
- build scripts. It prefers familiar script names, then unique recognizable
15
+ build scripts. It prefers familiar script names (for typecheck: `typecheck`,
16
+ `type-check`, `check-types`, `check:types`, `types:check`, `tsc`, `types`,
17
+ `lint:types`), then for typecheck one script named under such a name
18
+ (`typecheck:test`, `tsc:build`; not a watch script), then unique recognizable
16
19
  `tsc`, Vitest/Jest, ESLint, or Vite commands under other names. Multiple
17
20
  matches are reported as ambiguous rather than guessed. It never creates a new
18
- project script. It recognizes canonical
19
- `tsc --noEmit` on local npm installations as a candidate for automatic
20
- qualification. Before CURRENT is possible, a read-only probe checks the
21
- TypeScript configuration and effective file list, local compiler, installed
22
- contents, npm/Node identity, and relevant caller context. Unsupported plugins,
23
- preloads, config graphs, or outside-root inputs leave the check UNVERIFIED with
24
- an explanation. A discovered script can still be run and recorded in that
25
- state. Root and package-level npm/Yarn workspace scripts are discovered from
26
- `package.json` workspace declarations. Pinned Yarn 4 `node-modules`/classic
21
+ project script. Without `--recipe`, `init` tries the strict recipes below first,
22
+ then the [general recipe](node-script.md) for a root typecheck or lint
23
+ script: the script run through the package manager with every Node process
24
+ recorded and the whole checkout as its input. `--recipe typescript-direct`,
25
+ `tests-direct` or `node-script` selects one recipe alone. The direct TypeScript recipe supports
26
+ a typecheck script of `tsc --noEmit` runs in any argument order, chained runs,
27
+ or no typecheck script at all (a proposed `tsc --noEmit`; see
28
+ [script forms](direct-typescript.md#script-forms)), in a single-package npm installation using
29
+ TypeScript 5.x or 6.0 (a reviewed release, or one whose installed files match the
30
+ tarball package-lock.json names in npm's cache; see
31
+ [compiler identity](direct-typescript.md#compiler-identity)) and Node 22/24. It executes the local compiler,
32
+ not npm. Full installed compiler identity, config/membership, installed inputs
33
+ and relevant Node environment are checked before reuse. See the
34
+ [exact direct boundary](direct-typescript.md). Unsupported plugins, preloads,
35
+ configuration or outside-root inputs remain UNVERIFIED. Root and package-level npm/Yarn workspace scripts are discovered from
36
+ `package.json` workspace declarations. In a workspace of more than 50
37
+ packages, `init` proposes the root's checks only and names what it skipped;
38
+ add package checks to the config yourself. Up to 1000 packages are read. Pinned Yarn 4 `node-modules`/classic
27
39
  workspaces with a standalone `tsc -p .` script, local TypeScript, a conventional
28
40
  `src` include, and Node module resolution can qualify automatically. For one
29
41
  workspace, use `redue init --workspace NAME --check typecheck`; it writes only
@@ -36,21 +48,42 @@ When an internal dependency is consumed through built declarations, changing
36
48
  only its source does not stale the selected check until the consumed output
37
49
  changes. Unsupported custom resolution, project references, outside-root
38
50
  source links, and installation modes remain recording-only. Other Yarn
39
- typechecks, Vitest/Jest, ESLint, and builds start recording-only unless a
40
- separately justified explicit contract is provided.
51
+ typechecks, Vitest/Jest, ESLint, and builds in workspace packages start
52
+ recording-only unless a separately justified explicit contract is provided.
53
+ For a single-package project whose test script runs Vitest 4/5 or Jest 29
54
+ directly, `init` uses the direct test recipe, which records the run and guards
55
+ its inputs; see [direct tests](tests-direct.md).
41
56
  In particular, test caches and build-generated inputs are not excluded to
42
57
  make evidence green.
43
58
 
44
- Pinned pnpm 12 projects are read from `packageManager`, `pnpm-workspace.yaml`,
45
- the lockfile, and the installed `node_modules/.modules.yaml` metadata. The
59
+ pnpm 9-12 projects are read from `packageManager`, `pnpm-workspace.yaml`,
60
+ `.npmrc`, the lockfile, and the installed `node_modules/.modules.yaml`
61
+ metadata. The installed metadata names the pnpm version; a `packageManager`
62
+ pin is optional, and when present (with or without Corepack's `+sha512` hash)
63
+ it must name the installed version. Settings that only change what gets
64
+ installed are accepted, because the installed files are what REDUE observes.
65
+ Settings that move or copy installed files, or run code during install, are
66
+ not: custom `modulesDir`, `virtualStoreDir` or `lockfileDir`, the global
67
+ virtual store, injected workspace packages, separate workspace lockfiles,
68
+ pnpmfile hooks and `configDependencies`. They are refused when either
69
+ `pnpm-workspace.yaml` or `.npmrc` sets them. The linker is read from the file
70
+ the installed major reads (`.npmrc` for pnpm 9, `pnpm-workspace.yaml` then
71
+ `.npmrc` for pnpm 10, `pnpm-workspace.yaml` for pnpm 11 and 12) and must match
72
+ the installed one. Only the project's files are read: settings from a user or
73
+ global rc file, or `npm_config_*` variables during install, are not. The
46
74
  supported local layouts are pnpm's standard isolated and hoisted
47
75
  `node_modules` with a project-local `.pnpm` virtual store. `init` can discover
48
- root and workspace scripts. For a standalone `tsc --noEmit` or `tsc -p ...`
49
- script in the narrow TypeScript contract, it records the exact installed
50
- compiler invocation directly. This avoids pnpm 12's task-run bookkeeping
76
+ root and workspace scripts. For a typecheck script of `tsc` runs
77
+ ([script forms](direct-typescript.md#script-forms); `tsc -p .` without
78
+ `--noEmit` is also accepted), or a package with a `tsconfig.json` and no
79
+ typecheck script, it records the exact installed compiler invocation directly.
80
+ The source inputs are the directories the config's `include` covers, as
81
+ TypeScript itself computes them for new files, minus `node_modules` (`exclude`
82
+ entries stay inputs: an import can still reach an excluded file); a file the
83
+ program reads outside them keeps the check recording-only. This avoids pnpm's task-run bookkeeping
51
84
  writes during `pnpm run`; it does not alter the project's script. Test, lint,
52
- build, and ambiguous TypeScript scripts still execute through `pnpm run` and
53
- start recording-only. The generated `@typescript-bin:WORKSPACE` token resolves
85
+ build, and ambiguous TypeScript scripts still execute through `pnpm run`; root
86
+ typecheck and lint ones can use the [general recipe](node-script.md). The generated `@typescript-bin:WORKSPACE` token resolves
54
87
  the local compiler when the check runs; it is not an absolute machine path.
55
88
 
56
89
  The pnpm contract indexes consumed installed package contents inside the
@@ -61,10 +94,10 @@ global virtual stores, injected workspace dependencies, and external links
61
94
  without an observed boundary remain recording-only with a reason. Installed
62
95
  files with hard-linked aliases outside the checkout remain recording-only on
63
96
  macOS: writes through an unseen alias cannot support CURRENT there. On Linux,
64
- redue watches project-installed file inodes and rehashes the selected
65
- installed inputs on every synchronized read. A cached read remains UNVERIFIED
66
- for these pnpm checks; a synchronized read can establish CURRENT after the
67
- rehash. If an inode watch cannot be installed, observation becomes unavailable
97
+ redue watches project-installed file inodes and re-reads the identity of the
98
+ selected installed inputs on every synchronized read. A cached read remains UNVERIFIED
99
+ for these pnpm checks; a synchronized read can establish CURRENT after that
100
+ re-read. If an inode watch cannot be installed, observation becomes unavailable
68
101
  instead of dropping that file. The global store is not watched. An absent
69
102
  optional package remains unqualified when Node could later
70
103
  resolve it from an unobserved ancestor `node_modules`; project-local candidate
@@ -0,0 +1,132 @@
1
+ # Direct test recipe (Vitest and Jest)
2
+
3
+ `redue init` (or `redue init --recipe tests-direct --check test` for this recipe
4
+ alone) records a project's unit test run and reports later whether that result
5
+ still applies to the files you have.
6
+ It supports a single-package project installed by npm, pnpm 9-12 (isolated or
7
+ hoisted `node_modules`) or Yarn with the `node-modules` linker, whose test
8
+ script runs Vitest 4 or 5, or Jest 29, directly. It runs on macOS and Linux with
9
+ Node 22 or 24. The project has one lockfile kind, matching any
10
+ `packageManager` declaration; Yarn needs that declaration. With pnpm, the
11
+ runner is the package `node_modules/RUNNER` links to, inside the checkout.
12
+ Jest also needs jest-util 29 at `node_modules/jest-util`, which pnpm does not
13
+ place there unless it hoists.
14
+
15
+ In a workspace (npm or Yarn `workspaces`, or `packages` in
16
+ `pnpm-workspace.yaml`), `init` gives each package whose test script runs
17
+ Vitest directly its own check, `PACKAGE:test`, run in the package's directory
18
+ with the Vitest that Node finds from there. Its inputs are still the whole
19
+ checkout, so an edit in any package makes it STALE. The root's and every
20
+ package's `node_modules` are observed as installed files, without Vite's
21
+ caches in them. Jest is not supported in workspace packages, and neither is a
22
+ test script at the workspace root. A package added later needs `init` again:
23
+ its manifest appearing makes the workspace's checks UNVERIFIED until then.
24
+
25
+ ## Setup
26
+
27
+ ```sh
28
+ redue init --recipe tests-direct --check test
29
+ redue start
30
+ redue run test
31
+ redue status --sync
32
+ ```
33
+
34
+ `init` picks the `test`, `test:unit` or `test:check` script when it runs a
35
+ runner directly, otherwise the only script that does. The script must be one
36
+ runner invocation: no `&&`, quotes, globs or variable assignments. Watch mode,
37
+ `--changed`, `related`, snapshot updates (`-u`), shuffling, sharding and
38
+ Jest's `--onlyChanged` are refused. Vitest needs `run`, `--run` or
39
+ `--watch=false`. Other test scripts stay recording-only: the
40
+ [general recipe](node-script.md) covers typecheck and lint scripts, not tests.
41
+
42
+ REDUE runs the local runner with Node directly. The package manager, its
43
+ lifecycle scripts and `package.json` are not changed or executed.
44
+
45
+ ## What REDUE claims
46
+
47
+ CURRENT means the last run's inputs are unchanged: every file in the checkout
48
+ outside `node_modules`, the installed files in `node_modules`, the Node binary,
49
+ the exact command, and the environment. Every environment variable the run's
50
+ JavaScript read directly must have the same value, or still be unset. Node and
51
+ libc read some variables natively, and Vite lists the environment for its own,
52
+ so these are always compared: `TZ`, `LANG` and every `NODE_*`, `LC_*`,
53
+ `VITE_*`, `DYLD_*` and `LD_*` variable. Display-only variables are not
54
+ compared: `_`, `TERM`, `TERM_PROGRAM`, `COLORTERM`, `FORCE_COLOR`, `NO_COLOR`,
55
+ `FORCE_TTY`, `COLUMNS`, `LINES` and `AI_AGENT`.
56
+ It does not mean a rerun would give the same result. A test that depends on the
57
+ time of day or on randomness can pass now and fail later on the same inputs.
58
+
59
+ The default config excludes `**/*.md`, `**/*.mdx` and, with `--coverage`,
60
+ `coverage/**`. Editing those files keeps a result CURRENT unless the run read
61
+ them. A run may create git-ignored files (for example under `test/tmp`) and
62
+ read back what it wrote, in the same process or inside a directory it created:
63
+ when every change during the run is such an output, the result keeps the files
64
+ as the run left them.
65
+
66
+ ## The guard
67
+
68
+ Every Node process and worker thread of the run loads a recorder
69
+ (`src/test-recorder.cjs`). After the run REDUE checks the record. The result is
70
+ kept but marked UNVERIFIED, with the reason, when the run:
71
+
72
+ - read a file that the config excludes, or a git-ignored file REDUE does not
73
+ observe, that the run did not create first;
74
+ - changed an input during the run, or read a git-ignored output an earlier
75
+ run left before writing it again: the result depends on that leftover;
76
+ - listed a directory, or read a file over 1 MB, outside the checkout, other
77
+ than files and directories the run created, Jest's cache directory and the
78
+ runners' data directories (Vitest's `vitest` folder in the user data
79
+ directory);
80
+ - connected to anything other than loopback or a local socket, or asked DNS
81
+ about a name other than localhost;
82
+ - started a process that did not load the recorder: a shell, a binary, or a
83
+ Node process with its own environment;
84
+ - had an installed file with another hard link, other than a link into the
85
+ pnpm store the install recorded in `node_modules/.modules.yaml` (pnpm's
86
+ default on Linux). A change through a link elsewhere, such as Yarn's
87
+ hardlinks modes, is not seen.
88
+
89
+ Lookups that can be asked again become run facts, checked before every CURRENT
90
+ answer: whether a path outside the checkout exists, where a link resolves, the
91
+ content of each smaller file read outside it (Linux native bindings read
92
+ `/proc/version` and `/usr/bin/ldd` to pick their C library), and
93
+ the existence and listings of excluded or ignored files in the checkout.
94
+
95
+ When two runs on the same inputs and environment give different results, the
96
+ check is UNVERIFIED until the inputs change.
97
+
98
+ A `--coverage` run may read back the default `coverage/` report: Jest reads
99
+ every JavaScript file under its roots, an earlier report's included, only to
100
+ map dependencies, and that does not decide pass or fail. This is not allowed
101
+ when the configuration names coverage thresholds, which make the report decide
102
+ the result, or another report directory. Configuration can be code, so REDUE
103
+ looks for `coverageThreshold`, `thresholds`, `coverageDirectory` and
104
+ `reportsDirectory` in `package.json`'s `jest` field and the runner's config
105
+ files; when one appears, reading an old report is UNVERIFIED.
106
+
107
+ ## Known causes of UNVERIFIED
108
+
109
+ Tests that write files git does not ignore change the run's inputs.
110
+
111
+ ## Limits
112
+
113
+ - Runner caches and data are trusted: `node_modules/.vite`,
114
+ `node_modules/.vite-temp`, `node_modules/.vitest`, Vitest's typecheck build
115
+ info in `node_modules/vitest/dist`, Jest's cache directory and Vitest's user
116
+ data directory are not inputs.
117
+ - Code that copies the environment and reads the copy, and native code that
118
+ reads files, environment variables or the network, is not seen.
119
+ - Metadata of excluded files (size, times) is not compared, only their
120
+ existence.
121
+ - A file the run creates and another process reads is the run's output only
122
+ when it is inside a directory the run created.
123
+ - Outputs a run keeps are part of the result's inputs afterwards. Deleting
124
+ them, or an observer restart that reindexes without them (git-ignored files
125
+ are not indexed at start), makes the result STALE.
126
+ - A config written by an earlier build of this recipe is UNVERIFIED with "run
127
+ redue init again"; move `redue.config.json` aside and run init.
128
+ - pnpm's store is trusted not to change a file in place, as pnpm never does.
129
+ Editing an installed file in another project that shares the same store
130
+ file changes it here too, unseen. Use `pnpm patch`, or a copying install
131
+ (`package-import-method=copy`), if you edit installed files.
132
+ - Windows is not supported; the check stays recording-only there.