Skip to content

Both installers still skip a host whose registration cannot run, and exit 0 #595

Description

@MongLong0214

SSOT execution contract

Outcome

Installer exit 0 means every requested host is live-verified healthy or explicitly preserved as a healthy custom CommitLore registration.

Scope

  • one shared TypeScript host-inspection/apply command used by shell and PowerShell
  • exact owned wrapper + mcp args
  • live initialize/serverInfo probe for custom registrations
  • atomic JSON/TOML write
  • non-zero on any requested host failure

Acceptance

  • dead command, directory command, wrong args, malformed config, failed host CLI, interrupted write: non-zero
  • healthy owned/custom registration: preserved and reported distinctly
  • shell and PowerShell return the same summary schema and exit meaning

Non-goals

Do not rewrite runtime checkout/manifest activation and do not add hosts.


The pattern, still here after v0.8.1

An existing MCP entry is treated as working when its command merely exists as a string. The repository's own ownership predicate requires this wrapper and exactly mcp; the installers do not apply it to what they skip.

1. A config that cannot run an MCP server is skipped as "already mentions commitlore"install.sh:718, install.ps1:851. A Cursor entry with command: "/bin/sh", args: ["mcp"], or a directory, or a non-executable file. The installer exits 0. Shared by both.

2. Codex's "already points at this install" is a substring testinstall.sh:836, install.ps1:987. A codex mcp get result reading command: <dest>-old, or command: <dest> with wrong or absent mcp args, passes. When the CLI is absent, the fallback checks only for the TOML table header — not its command, not its args (install.sh:864, install.ps1:1020). Shared.

3. A host setup failure is a printed line, then overwritten by successinstall.sh:650, install.ps1:788, 1157. A preserved dead target, a failed codex mcp add, malformed config, a failed plugin install, or a Hermes failure leaves that host broken and the process exits 0. Shared; PowerShell makes it explicit with exit 0.

4. Presence is inferred from directories and free textinstall.sh:804, 904, install.ps1:935, 815. An orphan .cursor/.gemini directory triggers config creation with no executable present. Any claude plugin list output containing commitlore suppresses installation — including an unrelated plugin whose name contains it. Shared.

5. Wrapper ownership is still inferred from forgeable evidenceinstall.sh:457, 460, install.ps1:560, 565. A foreign wrapper containing the marker is overwritten. An unmarked foreign executable that prints 0.8.1 is overwritten whenever a matching checkout exists — which proves the checkout exists, not that it wrote the wrapper. Shared.

Divergences between the two installers

install.sh emits a broken wrapper for a legal POSIX path containing "install.sh:608. Set HOME or XDG_DATA_HOME to such a path: checkout and smoke verification succeed, the generated exec … "$candidate/dist/…" has unescaped quotes and points somewhere else, and the installer reports success. Fresh JSON configs are unescaped the same way (install.sh:749). PowerShell does not share the practical case — Windows paths cannot contain " — and its JSON writer serializes values properly.

Shell's JSON detection is recursive and jq-dependentinstall.sh:698, 722. With jq, {"metadata": {"commitlore": {}}} anywhere in the file suppresses wiring. Without jq, command extraction is disabled entirely, so a real entry pointing at a deleted /tmp/…/commitlore gets only "already mentions". PowerShell limits detection to mcpServers/mcp/servers and does not share either gap.

PowerShell can make an interrupted write permanently look registeredinstall.ps1:892, 923, 832. Set-Content writes in place; killed after "commitlore": but before the JSON closes, the next run's parse failure falls to a regex that says it is registered, and exits 0.

Smoke-exit classification differs — shell treats 126/127 as verification-unavailable; PowerShell's version and validate paths treat them as unusable.

What is genuinely clean

Both enforce Node 22.13+, run git before any write, bind the checkout to the requested tag's HEAD, verify tracked runtime assets, smoke-test the runtime before activation, retain old checkouts on upgrade, and write new MCP entries using the wrapper plus mcp. Same-version reruns reverify and reactivate; upgrades add a pinned checkout and repoint the stable wrapper. No persistent write was found outside the checkout, the wrapper, supported host config locations, and temp directories. The risk is preserved pre-existing entries, not newly generated wrong commands.

What closes it

Apply the repository's own ownership predicate to what is skipped, not only to what is written: the command must be this wrapper and the args must be exactly mcp. A host that cannot be made to work should make the installer exit non-zero, or at minimum be reported in a summary the caller can act on — CI and configuration management read the exit code, and today it says everything worked.

Follows #541, which fixed the same class for the runtime by requiring it to answer. Host wiring never got that treatment.

Found by an audit of both installers.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions