Separate bugs reports for sure, Eli, but I first thought it useful to discuss holistically to illustrate. Let's consider this bug report to be solely about project.
Dmitry, I think the following patch to project--write-project-list might suffice to avoid this in the future:
diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el
index f2a27ff91dd..f9b3b8891bc 100644
--- a/lisp/progmodes/project.el
+++ b/lisp/progmodes/project.el
@@ -1824,7 +1824,7 @@ project--read-project-list
(lambda (elem)
(let ((name (car elem)))
(list (if (file-remote-p name) name
- (abbreviate-file-name name)))))
+ (file-name-as-directory (abbreviate-file-name name))))))
(condition-case nil
(read (current-buffer))
(end-of-file
This causes /Users/xxx to appear as /Users/xxx/ in the project file and then project--read-project-list produces ~/ rather than ~ for which file-name-directory produces nil.
(defun project--read-project-list ()
...
(file-name-as-directory (abbreviate-file-name name))))))
Note: I did not consider the tramp/remote file cases in this analysis.