Skip to content

fix(miner): status.ts's two remaining monorepo-sibling path lookups still use the pre-dist-migration hardcoded depth #8630

Description

@JSONbored

⚠️ Definition of Done: this issue must be completed in full, in a single PR. Do not split this
work across multiple PRs, and do not defer any Deliverable below to a follow-up issue. A PR that
satisfies only some of the Deliverables, stubs a required test, or leaves a checkbox
partially-done does NOT resolve this issue and will be closed.

Context

packages/loopover-miner/lib/status.ts resolves the monorepo-sibling packages/loopover-engine
path relative to its own on-disk location, because this file runs from two different physical
directories depending on context: packages/loopover-miner/lib/status.ts (source, used by
in-process test imports) or packages/loopover-miner/dist/lib/status.js (the real compiled CLI,
since the 2026-07-24 out-of-place dist/ emit migration, commit c89679761, PR #8590).

That same commit introduced a depth-flexible helper specifically to handle this:

// lib/status.ts, lines 62-66
function resolveMonorepoSiblingPath(...segments: string[]): string {
  const fromLib = join(moduleDir(), "..", ...segments);
  if (existsSync(fromLib)) return fromLib;
  return join(moduleDir(), "..", "..", ...segments);
}

c89679761's commit message states: "Two production-source bugs found and fixed properly, not
patched around: bin/loopover-mcp.ts and lib/status.ts both compute paths relative to their own
file location ... Both now try the source-relative depth first, falling back to the dist-relative
depth, so they work correctly however they're currently loaded."

That fix was applied to the happy path of readInstalledEnginePackageVersion (line 180) and to
the pin-file argument of readExpectedEnginePackageVersion (line 223), but two sibling
call sites that compute the exact same monorepo-sibling path were left on the old
single-hardcoded-depth calculation, which is only correct from the source (lib/) directory, not
from the compiled (dist/lib/) one:

  1. readInstalledEnginePackageVersion's catch-all fallback (lib/status.ts:184), reached when
    requireFromHere().resolve(ENGINE_PACKAGE) throws (Node cannot resolve @loopover/engine at
    all):

    // lib/status.ts, lines 176-193
    export function readInstalledEnginePackageVersion(): string | null {
      try {
        return readInstalledEnginePackageVersionFromPaths(
          requireFromHere().resolve(ENGINE_PACKAGE),
          resolveMonorepoSiblingPath("..", "loopover-engine", "package.json"), // fixed
        );
      } catch {
        /* v8 ignore next 9 -- only reaches when Node cannot resolve the installed package at all */
        const workspacePkg = join(moduleDir(), "../../loopover-engine/package.json"); // NOT fixed
        if (existsSync(workspacePkg)) { ... }
        return null;
      }
    }
  2. readExpectedEnginePackageVersion's first argument (monorepoEnginePkg, lib/status.ts:222):

    // lib/status.ts, lines 220-224
    export function readExpectedEnginePackageVersion(): string | null {
      return readExpectedEnginePackageVersionFromPaths(
        join(moduleDir(), "../../loopover-engine/package.json"), // NOT fixed
        resolveMonorepoSiblingPath("expected-engine.version"),   // fixed
      );
    }

Empirically verified against the built package (npm --workspace @loopover/engine run build && npm --workspace @loopover/miner run build:tsc, then evaluating both path expressions for
moduleDir() = packages/loopover-miner/lib vs packages/loopover-miner/dist/lib):

Expression from lib/ (source) from dist/lib/ (compiled, real CLI)
join(moduleDir(), "../../loopover-engine/package.json") packages/loopover-engine/package.json (exists) packages/loopover-miner/loopover-engine/package.json (does not exist)
resolveMonorepoSiblingPath("..", "loopover-engine", "package.json") packages/loopover-engine/package.json (exists) packages/loopover-engine/package.json (exists)

So when the real compiled CLI (dist/bin/loopover-miner.js, what doctor/status actually spawn
in production and in the CLI-harness e2e tests) runs inside a monorepo checkout:

  • readExpectedEnginePackageVersion()'s monorepoEnginePkg check for the live
    packages/loopover-engine/package.json always silently fails (wrong path, existsSync
    false), so it always falls through to the static expected-engine.version pin file instead
    — even when a live, newer monorepo packages/loopover-engine/package.json is present. This
    contradicts the function's own doc comment: "Expected minimum engine semver: monorepo engine
    package.json when present, else the shipped pin file."
    The "when present" branch is dead code
    from the compiled CLI's perspective.
  • readInstalledEnginePackageVersion()'s catch-all fallback (the last-resort path used when
    @loopover/engine cannot be resolved via require.resolve at all) computes the wrong monorepo
    sibling path from dist/lib/, so it returns null instead of finding the real workspace
    version — again, only from the compiled CLI's execution context.

This is a genuine regression introduced by c89679761 (verified: before that migration, source
and compiled output shared the same directory — packages/loopover-miner/lib/status.ts next to
its in-place-emitted status.js — so join(moduleDir(), "../../loopover-engine/package.json")
was correct from either execution mode; the dist/ split broke that assumption for these two
call sites only). Neither is covered by the existing test suite:
test/unit/miner-status.test.ts only calls readInstalledEnginePackageVersionFromPaths /
readExpectedEnginePackageVersionFromPaths directly with injected paths, or calls the
zero-arg wrapper functions from the .ts source context (where moduleDir() always resolves to
lib/, never dist/lib/) — so the compiled-CLI code path these two spots are meant to serve is
completely unexercised, matching the /* v8 ignore next 9 */ annotation already sitting on the
catch block.

Requirements

  • Replace the two remaining hardcoded-depth path computations in
    packages/loopover-miner/lib/status.ts with the already-existing resolveMonorepoSiblingPath
    helper, exactly the same way the other two call sites in the same file were already fixed:
    • readInstalledEnginePackageVersion's catch-all fallback (the workspacePkg local computed at
      line 184) must use resolveMonorepoSiblingPath("..", "loopover-engine", "package.json")
      instead of join(moduleDir(), "../../loopover-engine/package.json").
    • readExpectedEnginePackageVersion's first argument (line 222) must use
      resolveMonorepoSiblingPath("..", "loopover-engine", "package.json") instead of
      join(moduleDir(), "../../loopover-engine/package.json").
  • Do not change the behavior of either function for the currently-tested source (lib/)
    execution context — both must continue to resolve the identical file they already resolve today
    when moduleDir() is packages/loopover-miner/lib.
  • Add tests that exercise both fixed call sites from a simulated dist/lib/ directory depth
    (i.e. construct a temporary directory tree with <tmp>/packages/loopover-miner/dist/lib/ as the
    "module dir" and <tmp>/packages/loopover-engine/package.json as the sibling, mirroring the
    existing injectable-path test pattern already used elsewhere in test/unit/miner-status.test.ts
    for readInstalledEnginePackageVersionFromPaths / readExpectedEnginePackageVersionFromPaths),
    proving:
    • readInstalledEnginePackageVersion's catch-all fallback resolves the live monorepo
      packages/loopover-engine/package.json version correctly when moduleDir() is a dist/lib
      style path two levels deeper than packages/loopover-miner/.
    • readExpectedEnginePackageVersion resolves the live monorepo packages/loopover-engine/package.json
      version (not the expected-engine.version pin file) when moduleDir() is that same dist/lib
      style path and the live monorepo package.json is present.
  • Remove the /* v8 ignore next 9 */ annotation on the now-testable catch block once it is
    actually covered (it must not remain uncovered-and-ignored after this issue's tests land).

Deliverables

  • packages/loopover-miner/lib/status.ts's readInstalledEnginePackageVersion catch-all
    fallback (currently line 184) computes its workspace-sibling path via
    resolveMonorepoSiblingPath("..", "loopover-engine", "package.json"), not a hardcoded
    join(moduleDir(), "../../loopover-engine/package.json").
  • packages/loopover-miner/lib/status.ts's readExpectedEnginePackageVersion (currently line
    222) computes its monorepoEnginePkg argument via
    resolveMonorepoSiblingPath("..", "loopover-engine", "package.json"), not a hardcoded
    join(moduleDir(), "../../loopover-engine/package.json").
  • A new regression test in test/unit/miner-status.test.ts that proves
    readInstalledEnginePackageVersion's catch-all fallback resolves the correct file from a
    simulated dist/lib-depth moduleDir(), named for this issue.
  • A new regression test in test/unit/miner-status.test.ts that proves
    readExpectedEnginePackageVersion resolves the live monorepo packages/loopover-engine/package.json
    (not the pin file) from a simulated dist/lib-depth moduleDir() when that file is present,
    named for this issue.
  • The /* v8 ignore next 9 */ comment on readInstalledEnginePackageVersion's catch block is
    removed once the new test gives it real coverage.

All four Deliverables above are required in the same PR — this is not splittable across follow-ups.
A PR that fixes only one of the two call sites, or adds tests without the corresponding source fix
(or vice versa), does not resolve this issue.

Test Coverage Requirements

packages/loopover-miner/** is measured by codecov/patch (99% target, branch-counted — see
packages/loopover-miner/lib/status.ts itself, already covered by test/unit/miner-status.test.ts).
The new tests must exercise both the "found" and "not found" branches of each fixed call site
(mirroring the existing readInstalledEnginePackageVersionFromPaths /
readExpectedEnginePackageVersionFromPaths tests' fixture pattern in the same file) so the fix
itself is provably exercised, not just present in source.

Expected Outcome

doctor's engine-version-skew check and status's engine-version display, when run from the
real compiled CLI (dist/bin/loopover-miner.js, what actually ships and what the CLI-harness
e2e tests spawn) inside a monorepo checkout, correctly detect the live
packages/loopover-engine/package.json version instead of silently falling back to (for
readExpectedEnginePackageVersion) the static expected-engine.version pin file or (for
readInstalledEnginePackageVersion's catch-all path) null, matching the behavior already correct
today when the same functions run against the .ts source in tests.

Links & Resources

  • packages/loopover-miner/lib/status.ts (the file to fix — see resolveMonorepoSiblingPath at
    lines 62-66 for the already-correct pattern to reuse).
  • test/unit/miner-status.test.ts (existing test file to extend — see its
    readInstalledEnginePackageVersionFromPaths / readExpectedEnginePackageVersionFromPaths tests
    for the injectable-path fixture pattern to mirror).
  • Introduced by commit c89679761 ("fix(build): migrate loopover-miner/loopover-mcp to
    out-of-place dist/ emit", PR fix(build): migrate loopover-miner/loopover-mcp to out-of-place dist/ emit #8590), which fixed two of four equivalent call sites in the same
    file but left these two on the pre-migration hardcoded-depth calculation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    gittensor:bugGittensor-scored bug fix — scores a 0.05x multiplier.help wantedExtra attention is needed

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions