Skip to content

fix(install): correct OpenCode global install path (#3398) - #3459

Open
nikhilsaxena04 wants to merge 1 commit into
Graphify-Labs:v8from
nikhilsaxena04:fix-opencode-global-install
Open

fix(install): correct OpenCode global install path (#3398)#3459
nikhilsaxena04 wants to merge 1 commit into
Graphify-Labs:v8from
nikhilsaxena04:fix-opencode-global-install

Conversation

@nikhilsaxena04

Copy link
Copy Markdown

The bug:
A non-project (global) OpenCode install wrote the plugin and registered it in opencode.json at the CWD's .opencode/ instead of the documented ~/.config/opencode/. The two helper functions shared a single code path that joined _OPENCODE_PLUGIN_PATH (which starts with .opencode/) onto whatever project_dir they received — `Path(".")\ for the global case — so the global install was effectively the same as a project install at CWD.

The fix:

  • Replaced the two static constants _OPENCODE_PLUGIN_PATH / _OPENCODE_CONFIG_PATH with a helper _opencode_base(project, project_dir) that returns the correct (base_dir, plugin_entry_string) for the scope.
  • Updated _install_opencode_plugin and _uninstall_opencode_plugin to accept project: bool and project_dir instead of a single path, so global vs. project routing is explicit at every call site.
  • All call sites updated: global calls now pass project=False; project-scoped calls pass project=True, project_dir=....

Tests:
tests/test_opencode_install.py — three cases:

  • global install lands in ~/.config/opencode/ with no .opencode/ nesting
  • project install lands in <project_dir>/.opencode/ as before
  • global uninstall cleans up ~/.config/opencode/ correctly

The bug:
A non-project (global) OpenCode install wrote the plugin and registered
it in opencode.json at the CWD's `.opencode/` instead of the documented
`~/.config/opencode/`.  The two helper functions shared a single code
path that joined `_OPENCODE_PLUGIN_PATH` (which starts with `.opencode/`)
onto whatever `project_dir` they received — `Path(".")\ for the global
case — so the global install was effectively the same as a project install
at CWD.

The fix:
- Replaced the two static constants `_OPENCODE_PLUGIN_PATH` /
  `_OPENCODE_CONFIG_PATH` with a helper `_opencode_base(project, project_dir)`
  that returns the correct `(base_dir, plugin_entry_string)` for the scope.
- Updated `_install_opencode_plugin` and `_uninstall_opencode_plugin` to
  accept `project: bool` and `project_dir` instead of a single path, so
  global vs. project routing is explicit at every call site.
- All call sites updated: global calls now pass `project=False`;
  project-scoped calls pass `project=True, project_dir=...`.

Tests:
tests/test_opencode_install.py — three cases:
  - global install lands in `~/.config/opencode/` with no `.opencode/` nesting
  - project install lands in `<project_dir>/.opencode/` as before
  - global uninstall cleans up `~/.config/opencode/` correctly

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Graphify reviewed this change.

Worth a look — the grounded gate found no coupling regressions or blocking issues, but 5 advisory finding(s) below merit a look before merge.

Formal verification. 2 change(s) tested, no difference found (not proven).


Graphify review — findings

Fixes global opencode installs to write the plugin and register it under ~/.config/opencode/ instead of erroneously nesting it in ./.opencode/, while project installs keep writing to <project_dir>/.opencode/ with a root-relative plugin entry (#3398). Reworks _install_opencode_plugin and _uninstall_opencode_plugin to take project/project_dir and resolve their base path and entry string through the new _opencode_base helper, threading the correct scope through the opencode install/uninstall and uninstall_all paths. Adds tests covering the XDG global target, the project target, and global uninstall cleanup.

Worth a look

  • _agents_uninstall opencode branch changed to project=True but skill is user-scopegraphify/install.py:1725 · Escalate · high
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • Opencode plugin install/uninstall scope mismatch in _agents_uninstallgraphify/install.py:1725 · Escalate · high
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • Opencode install/uninstall scopes are inconsistent through agents* — graphify/install.py:1748 · Escalate · high
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • _install_opencode_plugin no longer honors legacy positional project_dirgraphify/install.py:1410 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
  • Global opencode install now writes plugin under a different key/entry than before, breaking existing project registrationsgraphify/install.py:1571 · Escalate · medium
    • agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 414 functions depend on the 148 functions this change touches.

Health — this change adds coupling hotspots:

  • new: dispatch_command() — 2 callers, 124 callees
  • new: codebuddy_install() — 20 callers, 5 callees
  • new: claude_install() — 19 callers, 4 callees
  • new: gemini_install() — 10 callers, 7 callees
  • new: claude_uninstall() — 17 callers, 4 callees
  • new: _project_uninstall() — 5 callers, 13 callees
  • new: dispatch_install_cli() — 2 callers, 31 callees
  • new: _copy_skill_file() — 12 callers, 5 callees
  • …and 13 more — each is listed as a finding

Verification — 414 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 270 function(s) in the blast radius were not formally verified this run

Test selection

Test selection

31 of 263 test file(s) selected (12%) via static blast radius.

  • tests/test_affected_cli.py — impact
  • tests/test_agents_platform.py — impact
  • tests/test_atomic_version_stamp.py — impact
  • tests/test_claude_md.py — impact
  • tests/test_codebuddy.py — impact
  • tests/test_devin.py — impact
  • tests/test_explain_cli.py — impact
  • tests/test_extract_cli.py — impact
  • tests/test_gemini_hook.py — impact
  • tests/test_god_nodes_cli.py — impact
  • tests/test_hollow_chunks_arm_shrink_guard.py — impact
  • tests/test_home_sandbox.py — impact
  • tests/test_hook_strict.py — impact
  • tests/test_incomplete_build_guard.py — impact
  • tests/test_install.py — impact
  • tests/test_install_references.py — impact
  • tests/test_install_strings.py — impact
  • tests/test_install_version_warning.py — impact
  • tests/test_merge_chunks_validation.py — impact
  • tests/test_multigraph_diagnostics.py — impact
  • tests/test_no_dedup_flag.py — impact
  • tests/test_opencode_install.py — impact, changed-test
  • tests/test_path_cli.py — impact
  • tests/test_query_cli.py — impact
  • tests/test_query_induced_edges.py — impact
  • tests/test_read_hook.py — impact
  • tests/test_replace_or_append_section.py — impact
  • tests/test_search_hook.py — impact
  • tests/test_skill_version_warning.py — impact
  • tests/test_uninstall_scope.py — impact
  • tests/test_unverified_semantic_shrink.py — impact

Selection is safe under the controlled-regression assumption; always-run tests + a periodic full run are the backstops. Advisory — it never changes the check verdict.

Formal verification

Could not verify: Could not verify \_agents\_install.

The verifier did not have enough to check \_agents\_install, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 200 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly TypeError — names the real obstacle, not a sampling gap)

No difference found (not proven): No behavior difference found in \_agents\_uninstall (not a proof).

The verifier ran both versions of \_agents\_uninstall on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

Could not verify: Could not verify install.

The verifier did not have enough to check install, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: not verifiable: all 23 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly SystemExit — names the real obstacle, not a sampling gap)

Could not verify: Could not verify \_install\_opencode\_plugin.

The verifier did not have enough to check \_install\_opencode\_plugin, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `project_dir` is annotated `Path | None` — outside the synthesizable primitive/collection set

No difference found (not proven): No behavior difference found in uninstall\_all (not a proof).

The verifier ran both versions of uninstall\_all on many inputs and saw identical behavior every time. Strong evidence the change is safe, but evidence, not a proof.

Guarantee: Empirical: differential testing (both versions run on many generated inputs). A divergence on an untested input remains possible, so this is 'no counterexample found', not 'proven equivalent'.

Note: An input the sampler did not try could still differ.

Could not verify: Could not verify \_uninstall\_opencode\_plugin.

The verifier did not have enough to check \_uninstall\_opencode\_plugin, so it is saying so rather than guessing. No false assurance is the whole point.

Guarantee: No guarantee either way, this is an honest abstention, not a pass.

Note: Reason: parameter `project_dir` is annotated `Path | None` — outside the synthesizable primitive/collection set

· 21 more finding(s) on lines outside this diff (see the check run).

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant