@redue/cli 0.1.0-alpha.3 → 0.1.0-alpha.4

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.0-alpha.4 — 2026-10-07
4
+
5
+ - Withdraw automatic npm-launcher applicability because update notifications consume
6
+ unobserved remote/cache/time state. Npm scripts still run and record outcomes.
7
+ - Preserve historical PASS/FAIL and require stop/start when upgrading an observer.
8
+ Do not delete receipts. See the [maintainer notice](docs/user/npm-launcher-notice.md).
9
+ - Supported Yarn/pnpm and explicit reviewed contracts are unchanged. No agent
10
+ activation or npm direct-compiler recipe is included in this correction.
11
+
3
12
  ## 0.1.0-alpha.3 — 2026-10-06
4
13
 
5
14
  Install: `npm install --global @redue/cli@alpha`.
package/README.md CHANGED
@@ -1,7 +1,9 @@
1
1
  # REDUE
2
2
 
3
- Maintainer notice: alpha.2 users should review the
4
- [receipt-selection correction and interim guidance](docs/user/receipt-selection-notice.md).
3
+ Maintainer notice: alpha.4 withdraws automatic npm-launcher applicability.
4
+ Npm commands still execute and record historical outcomes, but remain UNVERIFIED.
5
+ [Why this changed and how to upgrade without deleting receipts](docs/user/npm-launcher-notice.md).
6
+ Alpha.2 users should also review the [receipt correction](docs/user/receipt-selection-notice.md).
5
7
 
6
8
  **Know what still holds. Redo what's due.**
7
9
 
@@ -12,6 +14,8 @@ REDUE keeps persistent verification state for your codebase so developers and
12
14
  coding agents can see which previous checks still apply to the code they have now.
13
15
  It records evidence, not proofs of correctness.
14
16
 
17
+ For a qualified supported Yarn/pnpm check (ordinary npm scripts are recording-only):
18
+
15
19
  ```text
16
20
  $ redue status --sync
17
21
  ✓ typecheck CURRENT / PASS
@@ -85,7 +89,8 @@ has been verified.
85
89
  - macOS, Linux, and Windows on supported local filesystems; Node 22+.
86
90
  - Linux also requires Python 3 for observation. No WSL or Git Bash needed on Windows.
87
91
  - npm, pinned Yarn `node-modules`, and pinned pnpm 12 isolated/hoisted layouts.
88
- - Automatic qualification for supported TypeScript invocations; workspace
92
+ - Automatic qualification for the supported Yarn/pnpm TypeScript invocations; npm
93
+ launcher checks are recording-only in alpha.4. Workspace
89
94
  boundaries follow the files actually consumed, including built declarations.
90
95
  - Existing test/lint/build and arbitrary commands can record results while
91
96
  their applicability remains unqualified.
package/bin/redue.mjs CHANGED
@@ -266,6 +266,7 @@ const checks=publicConfig.checks.map(check=>{
266
266
  coverageReasons:check.coverageReasons||Object.fromEntries(
267
267
  categories.map(category=>[category,reason])),
268
268
  qualification:check.qualification||null,script:check.script||null,
269
+ ...(npmAutomatic?{qualificationInterpretation:'npm-launcher-withheld@1'}:{}),
269
270
  workspace:check.workspace||null,kind:check.kind||null};
270
271
  });
271
272
  const runtime={schema:1,provider:'declared-project@1',root,state,checks};
@@ -57,6 +57,12 @@ prefix. Uninstalling the package does not remove local verification evidence;
57
57
  `redue remove-state` is a separate, explicit action. Alpha.1 remains available as
58
58
  historical software and is superseded for package identity, not a runtime defect.
59
59
 
60
+ ## Upgrading to alpha.4
61
+
62
+ Stop every observer using the installation before upgrading, then restart each
63
+ with the same config/state selection. See [the npm launcher notice](npm-launcher-notice.md)
64
+ for exact commands. Do not remove state or delete receipts.
65
+
60
66
  ## First project
61
67
 
62
68
  From the project root:
@@ -74,7 +80,9 @@ Use a check actually printed by `init`. For a workspace:
74
80
  `redue init --workspace @example/package --check typecheck`.
75
81
  Initialization previews detected commands and qualification confidence before writing
76
82
  `redue.config.json`. An existing config is never overwritten. No checks are run and
77
- no dependencies installed. A missing/unsupported boundary is explained.
83
+ no dependencies installed. A missing/unsupported boundary is explained. Npm-launcher typechecks are
84
+ recording-only in alpha.4; a passed execution is historical PASS with UNVERIFIED
85
+ applicability, not reusable CURRENT.
78
86
 
79
87
  “Ready to qualify” means REDUE has a supported recipe whose prerequisites will be
80
88
  validated. It is not a receipt. “Recording-only” can record PASS/FAIL without claiming
@@ -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.
@@ -15,14 +15,13 @@ At the repository root, `init` looks for existing typecheck, test, lint, and
15
15
  build scripts. It prefers familiar script names, then unique recognizable
16
16
  `tsc`, Vitest/Jest, ESLint, or Vite commands under other names. Multiple
17
17
  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
18
+ project script. Canonical `tsc --noEmit` npm scripts are discovered and recorded, but their
19
+ automatic applicability is withdrawn in alpha.4: npm's update notification uses
20
+ remote/cache/time inputs outside the observed contract. Even a successful run
21
+ remains UNVERIFIED/PASS. Historical receipts are retained. See the
22
+ [maintainer notice](npm-launcher-notice.md). A separately selected verification
23
+ obligation would require an explicit configuration and fresh evidence; alpha.4
24
+ does not provide the unreleased npm direct-compiler recipe. Root and package-level npm/Yarn workspace scripts are discovered from
26
25
  `package.json` workspace declarations. Pinned Yarn 4 `node-modules`/classic
27
26
  workspaces with a standalone `tsc -p .` script, local TypeScript, a conventional
28
27
  `src` include, and Node module resolution can qualify automatically. For one
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@redue/cli",
3
3
  "private": false,
4
- "version": "0.1.0-alpha.3",
4
+ "version": "0.1.0-alpha.4",
5
5
  "publishConfig": {
6
6
  "access": "public",
7
7
  "tag": "alpha"
@@ -116,7 +116,7 @@ function classify(kind,scriptName,script,pkg,managerInfo,root) {
116
116
  managerInfo.name==='npm'&&
117
117
  exists(path.join(root,'tsconfig.json'))&&
118
118
  exists(path.join(root,'node_modules/typescript/package.json')))
119
- return {level:'ready',reason:'canonical local tsc --noEmit; checked again before reuse',
119
+ return {level:'recording',reason:'npm launcher uses unobserved remote/cache/time inputs; execution can be recorded, but cannot be reused',
120
120
  qualification:'typescript-noemit-v1'};
121
121
  if(kind==='typecheck')return {level:'recording',reason:
122
122
  'TypeScript invocation, installation, or configuration is outside the automatic noEmit contract'};
@@ -183,7 +183,7 @@ export function discoverNodeProject(root) {
183
183
  if(!script)continue;
184
184
  let classification=observationIssue?{level:'unsupported',reason:observationIssue}:
185
185
  classify(kind,script,scripts[script],pkg,pm,root);
186
- if(workspaces&&classification.level==='ready')classification={level:'recording',
186
+ if(workspaces&&classification.qualification==='typescript-noemit-v1')classification={level:'recording',
187
187
  reason:'workspace task and installed-input closure need explicit qualification'};
188
188
  const check={name:kind,script,kind,inputs:sourceInputs(root,kind,pm.lockfiles||[])};
189
189
  if(pm.name==='pnpm'&&kind==='typecheck'&&!workspaces&&!pm.issue&&
@@ -1,188 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // Read-only applicability probe for the deliberately narrow npm/tsc --noEmit
3
- // contract. It emits one digest; failures emit only a fixed diagnostic code.
4
- import {compilerFiles} from './typescript-list.mjs';
5
- import {contextOnly,probeResult} from './probe-result.mjs';
6
- import fs from 'node:fs';
7
- import path from 'node:path';
8
- import os from 'node:os';
9
- import {createHash} from 'node:crypto';
10
- import {createRequire} from 'node:module';
11
- import {spawnSync} from 'node:child_process';
12
- import {withinPath as within,realObservedPath} from './path-identity.mjs';
13
- import {timing} from './decision-profile.mjs';
14
-
15
- const [rootArg,scriptName,npmArg]=process.argv.slice(2);
16
- const hash=value=>createHash('sha256').update(value).digest('hex');
17
- const fail=code=>{throw Object.assign(Error(code),{code});};
18
- const digestFile=file=>hash(fs.readFileSync(file));
19
- const require=createRequire(import.meta.url);
20
- function treeHash(dir){
21
- const digest=createHash('sha256');let count=0;
22
- const visit=(folder,rel='')=>{
23
- for(const entry of fs.readdirSync(folder,{withFileTypes:true}).sort((a,b)=>
24
- a.name.localeCompare(b.name))){
25
- const name=rel?rel+'/'+entry.name:entry.name,full=path.join(folder,entry.name);
26
- if(entry.isDirectory())visit(full,name);
27
- else if(entry.isFile()){digest.update(name+'\0'+digestFile(full)+'\0');count++;}
28
- else fail('npm-installation-unobservable');
29
- }
30
- };
31
- visit(dir);return [digest.digest('hex'),count];
32
- }
33
- function npmRcKeys(file){
34
- if(!fs.existsSync(file))return null;
35
- const keys=[];
36
- for(const line of fs.readFileSync(file,'utf8').split(/\r?\n/)){
37
- const trimmed=line.trim();if(!trimmed||/^[#;]/.test(trimmed))continue;
38
- const at=trimmed.indexOf('=');if(at<0)fail('npm-configuration-unsupported');
39
- const key=trimmed.slice(0,at).trim();
40
- if(key!=='registry'&&!/^\/\/[^\s]+\/:_authToken$/.test(key))
41
- fail('npm-configuration-unsupported');
42
- keys.push(key);
43
- }
44
- return keys.sort();
45
- }
46
- try{
47
- if(!rootArg||!scriptName||!npmArg)fail('probe-arguments');
48
- const root=realObservedPath(rootArg),npm=realObservedPath(npmArg);
49
- const pkg=JSON.parse(fs.readFileSync(path.join(root,'package.json'),'utf8'));
50
- if(pkg.scripts?.[scriptName]?.trim()!=='tsc --noEmit'||
51
- pkg.scripts?.['pre'+scriptName]||pkg.scripts?.['post'+scriptName])
52
- fail('typecheck-script-changed');
53
- if(process.env.NODE_OPTIONS||process.env.NODE_PATH||process.env.BASH_ENV||
54
- process.env.ENV||Object.entries(process.env).some(([key,value])=>{
55
- if(/^npm_config_prefix$/i.test(key)&&process.platform==='win32'){
56
- // The Windows runner supplies a global npm prefix independently of
57
- // the selected local compiler. Its value is part of caller context.
58
- return !path.isAbsolute(value);
59
- }
60
- return /^(npm_config_|DYLD_|TSGO_)/i.test(key);
61
- }))fail('execution-environment-unsupported');
62
- let npmRoot;
63
- if(process.platform==='win32'){
64
- if(path.basename(npm).toLowerCase()!=='npm.cmd')
65
- fail('npm-installation-layout-unsupported');
66
- npmRoot=path.join(path.dirname(npm),'node_modules','npm');
67
- const body=fs.readFileSync(npm,'utf8');
68
- if(!body.includes('node_modules\\npm\\bin\\npm-cli.js')||
69
- !body.includes('node_modules\\npm\\bin\\npm-prefix.js')||
70
- !body.includes('"%NODE_EXE%" "%NPM_CLI_JS%" %*'))
71
- fail('npm-installation-layout-unsupported');
72
- const prefix=spawnSync(process.execPath,[path.join(npmRoot,'bin','npm-prefix.js')],
73
- {cwd:root,env:process.env,encoding:'utf8',timeout:4000,stdio:['ignore','pipe','ignore']});
74
- const selectedPrefix=prefix.stdout?.trim();
75
- if(prefix.error||prefix.status!==0||!selectedPrefix||
76
- !path.isAbsolute(selectedPrefix))fail('npm-installation-layout-unsupported');
77
- // npm.cmd selects the prefix's CLI only when that file exists. Otherwise
78
- // it runs the bundled CLI beside npm.cmd; observe the same branch here.
79
- const alternate=path.join(selectedPrefix,'node_modules','npm','bin','npm-cli.js');
80
- if(fs.existsSync(alternate)&&
81
- realObservedPath(alternate)!==realObservedPath(path.join(npmRoot,'bin','npm-cli.js')))
82
- fail('npm-installation-layout-unsupported');
83
- }else npmRoot=path.dirname(path.dirname(npm));
84
- if(path.basename(npmRoot)!=='npm'||path.basename(path.dirname(npmRoot))!=='node_modules')
85
- fail('npm-installation-layout-unsupported');
86
- const home=process.platform==='win32'?os.homedir():process.env.HOME;
87
- if(!home||!path.isAbsolute(home))fail('home-context-unavailable');
88
- const npmConfig=[path.join(root,'.npmrc'),path.join(home,'.npmrc')]
89
- .map(file=>[hash(file),npmRcKeys(file)]);
90
- const contextFacts={
91
- npmTree:treeHash(npmRoot),npmConfig,
92
- node:hash(JSON.stringify([process.version,realObservedPath(process.execPath),
93
- fs.statSync(process.execPath).size])),
94
- shell:process.platform==='win32'?hash(JSON.stringify([
95
- realObservedPath(process.env.ComSpec||process.env.COMSPEC||''),
96
- digestFile(process.env.ComSpec||process.env.COMSPEC||''),
97
- process.env.PATHEXT||'',digestFile(npm),
98
- digestFile(path.join(root,'node_modules','.bin','tsc.cmd'))])):
99
- digestFile('/bin/sh')};
100
- const contextHash=hash(JSON.stringify(contextFacts));
101
- if(contextOnly(contextHash))process.exit(0);
102
- const tsRoot=path.join(root,'node_modules','typescript');
103
- let tsc;
104
- if(process.platform==='win32'){
105
- const shim=path.join(root,'node_modules','.bin','tsc.cmd');
106
- const body=fs.readFileSync(shim,'utf8').replace(/\r\n/g,'\n').trim();
107
- const expected=`@ECHO off
108
- GOTO start
109
- :find_dp0
110
- SET dp0=%~dp0
111
- EXIT /b
112
- :start
113
- SETLOCAL
114
- CALL :find_dp0
115
-
116
- IF EXIST "%dp0%\\node.exe" (
117
- SET "_prog=%dp0%\\node.exe"
118
- ) ELSE (
119
- SET "_prog=node"
120
- SET PATHEXT=%PATHEXT:;.JS;=;%
121
- )
122
-
123
- endLocal & goto #_undefined_# 2>NUL || title %COMSPEC% & "%_prog%" "%dp0%\\..\\typescript\\bin\\tsc" %*`;
124
- if(body!==expected)fail('typescript-executable-changed');
125
- tsc=realObservedPath(path.join(tsRoot,'bin','tsc'));
126
- }else{
127
- tsc=realObservedPath(path.join(root,'node_modules','.bin','tsc'));
128
- if(tsc!==path.join(tsRoot,'bin','tsc'))fail('typescript-executable-changed');
129
- }
130
- const configReads=new Set(),visited=new Set();
131
- let ts;try{ts=require(tsRoot);}catch{}
132
- if(typeof ts?.getParsedCommandLineOfConfigFile==='function'){
133
- const host={...ts.sys,readFile(file){configReads.add(path.resolve(file));
134
- return ts.sys.readFile(file);},onUnRecoverableConfigFileDiagnostic(){}};
135
- const parseConfig=file=>{
136
- const absolute=path.resolve(file);
137
- if(!within(absolute,root))fail('typescript-config-outside-repository');
138
- if(visited.has(absolute))return;visited.add(absolute);
139
- if(visited.size>64)fail('typescript-config-graph-too-large');
140
- const parsed=ts.getParsedCommandLineOfConfigFile(absolute,{},host);
141
- if(!parsed||parsed.errors?.length)fail('typescript-config-unavailable');
142
- if(parsed.options?.plugins?.length)fail('typescript-plugin-unsupported');
143
- if(parsed.options?.incremental||parsed.options?.composite)
144
- fail('typescript-incremental-unsupported');
145
- for(const reference of parsed.projectReferences||[])
146
- parseConfig(reference.path.endsWith('.json')?reference.path:
147
- path.join(reference.path,'tsconfig.json'));
148
- };
149
- parseConfig(path.join(root,'tsconfig.json'));
150
- }else{
151
- // TypeScript 7's native compiler does not expose the previous config API.
152
- // Its CLI reports the effective config; restrict this path to one direct
153
- // config rather than guessing about extends/reference provenance.
154
- const config=path.join(root,'tsconfig.json'),raw=fs.readFileSync(config,'utf8');
155
- if(/"(?:extends|references|plugins|incremental|composite)"\s*:/.test(raw))
156
- fail('typescript-config-graph-unsupported');
157
- const shown=spawnSync(process.execPath,[tsc,'--showConfig'],{cwd:root,
158
- env:process.env,encoding:'utf8',timeout:4000,maxBuffer:16*1024*1024,
159
- stdio:['ignore','pipe','pipe']});
160
- if(shown.error||shown.signal||shown.status!==0)fail('typescript-config-unavailable');
161
- const effective=JSON.parse(shown.stdout);
162
- if(effective.compilerOptions?.plugins?.length||
163
- effective.compilerOptions?.incremental||effective.compilerOptions?.composite)
164
- fail('typescript-config-graph-unsupported');
165
- configReads.add(config);
166
- }
167
- for(const file of configReads)if(!within(realObservedPath(file),root))
168
- fail('typescript-config-outside-repository');
169
- const listingStarted=performance.now();
170
- const captured=process.argv.includes('--redue-checkpoint')?compilerFiles(tsRoot,root):null;
171
- const listed=captured?{status:0,stdout:captured.files.join('\n')}:spawnSync(process.execPath,[tsc,'--noEmit','--listFilesOnly'],
172
- {cwd:root,env:process.env,encoding:'utf8',timeout:process.platform==='win32'?10000:4000,
173
- maxBuffer:16*1024*1024,
174
- stdio:['ignore','pipe','pipe']});
175
- timing('typescript_contract.list_files',listingStarted,{status:listed.status??null,
176
- error:listed.error?.code||null});
177
- if(listed.error||listed.signal||listed.status!==0)fail('typescript-input-list-unavailable');
178
- const files=listed.stdout.trim().split(/\r?\n/).filter(Boolean)
179
- .map(file=>path.resolve(file));
180
- if(!files.length||files.some(file=>!within(realObservedPath(file),root)))
181
- fail('typescript-input-outside-repository');
182
- const facts={schema:1,root:hash(root),script:scriptName,
183
- configs:[...configReads].sort().map(file=>[hash(file),digestFile(file)]),
184
- files:[...new Set(files)].sort().map(file=>[hash(file),digestFile(file)]),
185
- ...contextFacts};
186
- probeResult(hash(JSON.stringify(facts)),contextHash,captured?.queries);
187
- }catch(e){console.error(`VSTATE_REASON:${e.code&&/^[a-z-]+$/.test(e.code)?e.code:
188
- 'typescript-contract-unavailable'}`);process.exitCode=2;}
2
+ // Retired npm-launcher interpretation. Do not return even a context-only digest:
3
+ // old receipts and cached compiler certificates cannot qualify this obligation.
4
+ console.error('VSTATE_REASON:npm-launcher-unobserved-inputs');
5
+ process.exitCode=2;
@@ -50,47 +50,7 @@ export function qualifyTypeScript(root,selected,discovered={}) {
50
50
  return {qualified:issues.length===0,issues};
51
51
  }
52
52
  if(selected.qualification!=='typescript-noemit-v1')return null;
53
- const issues=[];
54
- let pkg;
55
- try{pkg=JSON.parse(fs.readFileSync(path.join(root,'package.json'),'utf8'));}
56
- catch{issues.push('package manifest unavailable');}
57
- if(!selected.script||pkg?.scripts?.[selected.script]?.trim()!=='tsc --noEmit'||
58
- pkg?.scripts?.['pre'+selected.script]||pkg?.scripts?.['post'+selected.script])
59
- issues.push('typecheck script is not the supported standalone tsc --noEmit invocation');
60
- if(selected.command?.length!==3||selected.command[1]!=='run'||
61
- selected.command[2]!==selected.script)
62
- issues.push('package-manager invocation differs from the discovered script');
63
- try{const npm=fs.realpathSync(selected.command[0]);
64
- const supported=process.platform==='win32'?path.basename(npm).toLowerCase()==='npm.cmd'&&
65
- fs.existsSync(path.join(path.dirname(npm),'node_modules','npm','bin','npm-cli.js')):
66
- path.basename(npm)==='npm-cli.js'&&
67
- path.basename(path.dirname(path.dirname(npm)))==='npm';
68
- if(!supported)
69
- issues.push('npm executable layout is not supported');}
70
- catch{issues.push('npm executable unavailable');}
71
- if(!fs.existsSync(path.join(root,'tsconfig.json')))
72
- issues.push('tsconfig.json is missing');
73
- if(!fs.existsSync(path.join(root,'node_modules','typescript','package.json'))||
74
- !fs.existsSync(path.join(root,'node_modules','.bin',
75
- process.platform==='win32'?'tsc.cmd':'tsc')))
76
- issues.push('local TypeScript installation is missing');
77
- if(!selected.inputs?.includes('**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs,json,jsonc}')||
78
- !selected.inputs?.includes('package.json')||
79
- !selected.inputs?.includes('tsconfig.json'))
80
- issues.push('TypeScript source/config file-set declaration is incomplete');
81
- if(!selected.installedInputs?.includes('node_modules/**'))
82
- issues.push('installed package contents and membership are not declared');
83
- if(!selected.environment?.executableIdentity||
84
- !['node','npm'].every(name=>selected.environment?.pathExecutables?.includes(name))||
85
- !['NODE_OPTIONS','NODE_PATH','HOME','BASH_ENV','ENV'].every(name=>
86
- selected.environment?.variables?.includes(name))||
87
- !['npm_config_','DYLD_','TSGO_'].every(name=>
88
- selected.environment?.prefixes?.includes(name)))
89
- issues.push('caller toolchain or execution context declaration is incomplete');
90
- if(!selected.probes?.some(argv=>path.basename(argv[1]||'')==='typescript-contract-probe.mjs'&&
91
- argv[2]&&fs.existsSync(argv[2])&&
92
- realObservedPath(argv[2])===root&&argv[3]===selected.script&&
93
- argv[4]===selected.command[0]))
94
- issues.push('TypeScript input and toolchain probe is missing');
95
- return {qualified:issues.length===0,issues};
53
+ // Retired automatic launcher contract: npm's notifier consumes unobserved
54
+ // remote/cache/time state. A successful execution is still an outcome.
55
+ return {qualified:false,issues:['npm launcher applicability is unqualified: update notification uses unobserved remote/cache/time inputs; run the npm script when needed']};
96
56
  }