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 test — install.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 success — install.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 text — install.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 evidence — install.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-dependent — install.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 registered — install.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.
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
mcpargsAcceptance
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 withcommand: "/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 test —
install.sh:836,install.ps1:987. Acodex mcp getresult readingcommand: <dest>-old, orcommand: <dest>with wrong or absentmcpargs, 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 success —
install.sh:650,install.ps1:788, 1157. A preserved dead target, a failedcodex 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 withexit 0.4. Presence is inferred from directories and free text —
install.sh:804, 904,install.ps1:935, 815. An orphan.cursor/.geminidirectory triggers config creation with no executable present. Anyclaude plugin listoutput containingcommitloresuppresses installation — including an unrelated plugin whose name contains it. Shared.5. Wrapper ownership is still inferred from forgeable evidence —
install.sh:457, 460,install.ps1:560, 565. A foreign wrapper containing the marker is overwritten. An unmarked foreign executable that prints0.8.1is overwritten whenever a matching checkout exists — which proves the checkout exists, not that it wrote the wrapper. Shared.Divergences between the two installers
install.shemits a broken wrapper for a legal POSIX path containing"—install.sh:608. SetHOMEorXDG_DATA_HOMEto such a path: checkout and smoke verification succeed, the generatedexec … "$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-dependent —install.sh:698, 722. Withjq,{"metadata": {"commitlore": {}}}anywhere in the file suppresses wiring. Withoutjq, command extraction is disabled entirely, so a real entry pointing at a deleted/tmp/…/commitloregets only "already mentions". PowerShell limits detection tomcpServers/mcp/serversand does not share either gap.PowerShell can make an interrupted write permanently look registered —
install.ps1:892, 923, 832.Set-Contentwrites 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.