Skip to content

star self uninstall deletes the whole share/star/extensions directory, including extensions it never installed #917

Closed

Description

@David-Noble-at-work

star self uninstall promises manifest-driven removal. Its help: "Reads the installation manifest and removes only
files that have not been modified since installation. Modified files are skipped and reported."

Its extensions hook does not read the manifest. cmd/star/star/root.go:331-338:

func uninstallStarExtensions(prefix string) error {
    targetExtDir := filepath.Join(prefix, "share", "star", "extensions")
    if err := os.RemoveAll(targetExtDir); err != nil && !os.IsNotExist(err) {
        return fmt.Errorf("failed to remove extensions: %w", err)
    }
    return nil
}

RemoveAll takes the whole directory: every extension, whoever put it there, modified or not.

Why that matters

<prefix>/share/star/extensions is shared, and the loader is what makes it so: it probes
xdg.DataPath("star", "extensions") (cmd/star/extension/loader.go:240), so any extension anyone installs lives in
one directory.

On DANOBLE-WD11-3, ~/.local/share/star/extensions holds:

Extension Who put it there
com.noblefactor.devlore.{Actions,Knowledge,Model,Package,Test} star self install, 23 files, from the archive's share/ (#903)
com.noblefactor.ops.GitHub writ, deploying the base layer (NobleFactor/noblefactor-ops)

star self uninstall would delete com.noblefactor.ops.GitHub too. writ would still hold it as deployed, so its
state and the filesystem would disagree, and star gh issues report — the report agent rule 3 requires — would stop
resolving until a redeploy.

Found by reading the code on 2026-09-22, during #903's verification. Not observed: nothing has run
star self uninstall here.

Shape of a fix

installStarExtensions already returns the files it copied (cli.CollectFiles), and they are recorded in the
manifest. The uninstall hook can remove those entries by the same rule the rest of uninstall follows — unmodified,
recorded by this app — and then remove the directory only if it is empty. The same hook exists for every product that
gains extensions.

Neighbour

#780 is the same family from the other side: self install over-claims in a shared directory, recording every page
in share/man/man1 in its own manifest, so an uninstall that does obey the manifest still deletes other apps' files.
This one bypasses the manifest. Both end in one app deleting another's files, and a fix for either should be checked
against the other.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Area:CLICommand-line interfaceEpic:ReleaseDistributionRelease, distribution, and self-installThread:WritSelfInstalla fresh machine installs writ and can use itbugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions