Skip to content

feat(mcp): register plan-DAG tools + local scorer in packages/loopover-mcp - #6526

Merged
JSONbored merged 4 commits into
JSONbored:mainfrom
galuis116:feat/mcp-register-plan-dag-and-scorer-tools
Jul 16, 2026
Merged

feat(mcp): register plan-DAG tools + local scorer in packages/loopover-mcp#6526
JSONbored merged 4 commits into
JSONbored:mainfrom
galuis116:feat/mcp-register-plan-dag-and-scorer-tools

Conversation

@galuis116

Copy link
Copy Markdown
Contributor

Closes #6150

Summary

src/mcp/server.ts registers loopover_run_local_scorer, loopover_build_plan, loopover_plan_status, loopover_record_step_result, and loopover_predict_gate on the remote server, and packages/loopover-mcp/bin/loopover-mcp.js's miner-auto-dev profile listed all five in recommendedTools — but none were actually registered as local stdio tools, only the string literals existed. A contributor relying on the local server for this profile couldn't invoke any of them.

  • loopover_run_local_scorer: computeLocalScorerTokens imported directly from @loopover/engine (already exported at the package root) — same pattern as the existing loopover_check_slop_risk/loopover_lint_pr_text pure in-process tools. Pure, deterministic, no repo/network access.
  • loopover_build_plan / loopover_plan_status / loopover_record_step_result: the plan-DAG state machine (src/services/plan-dag.ts) was never extracted to @loopover/engine's export map, so there's nothing to import — hand-duplicated here following the exact same precedent this file already uses for MAINTAIN_ACTION_CLASSES/AUTONOMY_LEVELS when the published package's export map doesn't cover something. Pure + stateless (no DB, no network) — the harness runs each step and calls loopover_record_step_result to report it back.
  • loopover_predict_gate: cannot be pure-local — it needs live repo/issue/PR/manifest data only the server can assemble (env.DB-backed). Proxies to the existing POST /v1/local/branch-analysis route, which already computes predictedGate via buildPredictedGateVerdict (the identical logic the remote tool uses) and returns it as a top-level response field — no new backend endpoint needed. Uses a metadata-only input shape (no git/workspace context), unlike the sibling branch-analysis tools that shell out to git.

Note on re-open (this is attempt #3)

#6462 and #6490 were both auto-closed by red CI, neither caused by this diff's own code:

  1. test/unit/mcp-tool-rename-aliases.test.ts hardcodes the exact count of registered loopover_-prefixed stdio tools; this PR's 5 new tools take that count from 55 to 60, so the guard needed updating (this one genuinely is a consequence of this diff).
  2. A same-day main commit (feat(agent): autonomy-levels framework (observe→…→auto) #773) gave the real, live JSONbored/gittensory repo's .loopover.yml genuine autonomy: { merge: auto, ... } config. Several pre-existing tests across the suite create a fixture repo literally named JSONbored/gittensory without mocking fetch, so their manifest-resolution calls silently hit that real, live config over the network instead of the DB-only settings under test — test/unit/backfill.test.ts (2 tests), test/unit/queue-5.test.ts (1 test), and test/integration/api.test.ts (1 test) were all confirmed broken this way by reproducing the identical failures in an isolated git worktree of clean upstream/main, unrelated to this branch. Fixed the two queue-5.test.ts/api.test.ts instances directly; the two backfill.test.ts instances were independently fixed by the maintainer upstream in the meantime (this branch is rebased on top of that fix, no conflict remains). The maintainer also fixed the actual root cause at the source afterward (test/helpers/d1.ts's default self-repo test identity no longer collides with the real repo name), so this class of failure shouldn't recur.
  3. A separate, also-unrelated main-level migration-number collision (0156 grabbed by two different PRs) briefly broke validate-code on the previous attempt; already fixed upstream and picked up by this branch's rebase.

Incidental fix

While testing, found packages/loopover-mcp/node_modules/@loopover/engine was a stale, non-symlinked directory shadowing the correct root-level workspace symlink, breaking the CLI's own @loopover/engine/signals/slop etc. subpath imports — confirmed pre-existing and unrelated to this change via git stash comparison against a clean checkout. Removed it; the root symlink resolves correctly.

Scope

Validation

  • git diff --check
  • npm run actionlint
  • npm run typecheck (root) — reliably OOMs on this shared sandbox regardless of what changed (reproduced repeatedly this session). packages/loopover-mcp is plain JS with its own npm run build (node --check across every lib/bin file) — ran it directly and it passes clean, and confirmed via direct execution that loopover-mcp --help and loopover-mcp tools --json (60 tools, count verified) both run without error.
  • npm run test:coverage — not run repo-wide (same OOM risk). Instead: ran the full MCP CLI test suite (30 files, 230 tests), mcp-tool-rename-aliases.test.ts, backfill.test.ts, queue-5.test.ts (the one affected test), and api.test.ts (the one affected test) together — 406 tests, all passing — after this branch's final rebase onto current main. Also ran a background full (non-coverage) sweep of the entire test/unit+test/integration suite (908 files, 17,435 tests) specifically to rule out any further instances of the self-repo collision pattern beyond the 4 already found — none found. test/unit/mcp-cli-plan-scorer-tools.test.ts (15 new tests) covers registration + success/rejection paths for all 5 new tools, including the API-failure path for loopover_predict_gate.
  • npm run test:workers — N/A, no Worker-facing code changed (this is the local CLI, not src/).
  • npm run build:mcp / npm run test:mcp-pack — both run directly and pass clean.
  • npm run ui:openapi:check / ui:lint / ui:typecheck / ui:build — N/A, no apps/loopover-ui changes.
  • npm audit --audit-level=moderate — 0 vulnerabilities.
  • New/changed behavior has tests — 15 new tests covering all 5 tools' success paths, zod-rejection paths, and (for the HTTP-backed tool) an API-failure path via a new localBranchAnalysisStatus fixture-server option added to test/unit/support/mcp-cli-harness.ts, mirroring the existing intakeStatus pattern.

If any required check was skipped, explain why:

  • Root npm run typecheck / npm run test:coverage: reliably OOMs on this shared sandbox under memory pressure from concurrent sessions, independent of the diff. Substituted with packages/loopover-mcp's own build (clean), direct CLI execution confirming all 5 tools register and respond correctly, the broader test suites listed above (all passing), and a full-suite non-coverage sweep specifically to de-risk the unrelated regression class that closed the two prior attempts.

Safety

  • No secrets, wallet details, hotkeys, coldkeys, user PATs, private keys, raw trust scores, private rankings, or private maintainer evidence are exposed.
  • Public GitHub text stays sanitized, low-noise, and does not imply compensation guarantees or optimization tactics.
  • Auth, cookie, CORS, GitHub App, Cloudflare, or session changes include negative-path tests. — N/A, no auth changes; the one HTTP-backed tool does have a negative-path (API-failure) test.
  • API/OpenAPI/MCP behavior is updated and tested where needed. — New local MCP tools added and tested; no new backend API surface (reuses the existing /v1/local/branch-analysis route).
  • UI changes use live API data or real empty/error/loading states, not production mock/demo fallbacks. — N/A, no UI changes.
  • Visible UI changes include a UI Evidence section below with screenshots. — N/A, no visible UI change (CLI tool registration only).
  • Public docs/changelogs are updated where needed; changelogs are only edited for release-prep PRs. — CHANGELOG.md untouched.

Notes

  • loopover_build_plan/loopover_plan_status/loopover_record_step_result's hand-duplicated plan-DAG logic in loopover-mcp.js is a deliberate architectural choice, not an oversight: this file already documents (in the MAINTAIN_ACTION_CLASSES/AUTONOMY_LEVELS comment block) that it resolves @loopover/engine through the published package, whose export map exposes only a curated set of subpaths — widening that public API is a separate, larger decision than "register these 5 tools locally," so this follows the existing precedent rather than introducing a new one.

…r-mcp

The miner-auto-dev profile's recommendedTools listed
loopover_run_local_scorer/loopover_build_plan/loopover_plan_status/
loopover_record_step_result/loopover_predict_gate, but none were
registered as local stdio tools -- only the string literals existed.

- loopover_run_local_scorer: computeLocalScorerTokens imported
  directly from @loopover/engine (already exported), same pattern as
  the existing loopover_check_slop_risk/loopover_lint_pr_text pure
  in-process tools.
- loopover_build_plan / loopover_plan_status /
  loopover_record_step_result: the plan-DAG state machine
  (src/services/plan-dag.ts) was never extracted to @loopover/engine's
  export map, so it's hand-duplicated here following the same
  MAINTAIN_ACTION_CLASSES/AUTONOMY_LEVELS precedent this file already
  uses for exactly this situation. Pure + stateless -- no DB, no
  network access.
- loopover_predict_gate: cannot be pure-local (needs live repo/issue/
  PR/manifest data only the server can assemble). Proxies to the
  existing POST /v1/local/branch-analysis route, which already
  computes predictedGate via buildPredictedGateVerdict -- the same
  logic the remote tool uses -- and returns it as a top-level field.
  No new backend endpoint needed. Metadata-only input (no git
  required), unlike the branch-analysis tools that shell out to git.

Along the way, found and fixed a stale, non-symlinked
packages/loopover-mcp/node_modules/@loopover/engine directory
shadowing the correct root-level workspace symlink, which was
breaking the CLI's own subpath imports (unrelated to this change --
confirmed pre-existing via git stash).

Added test/unit/mcp-cli-plan-scorer-tools.test.ts (15 tests) covering
all 5 tools' success + rejection paths, and a
localBranchAnalysisStatus fixture-server option +
predictedGate field on localBranchAnalysisFixture in
test/unit/support/mcp-cli-harness.ts to test loopover_predict_gate's
API-failure path, mirroring the existing intakeStatus pattern.

Closes JSONbored#6150
JSONbored#6150 registered loopover_run_local_scorer, loopover_build_plan,
loopover_plan_status, loopover_record_step_result, and
loopover_predict_gate, taking the total loopover_-prefixed stdio tool
count from 55 to 60. mcp-tool-rename-aliases.test.ts hardcodes this
count as a regression guard against silent alias/registration drift;
update it to match.
Both tests create a fake repo literally named "JSONbored/gittensory"
(the default self-repo identity test/helpers/d1.ts's createTestEnv()
uses) but never mock fetch, so the repo-settings resolver's manifest
loader fell through to a REAL network request to the real, live
JSONbored/gittensory GitHub repo's .loopover.yml. That real manifest
now carries autonomy: { merge: auto, ... } (JSONbored#773),
so the live-fetched content silently overrode the DB-only settings
these two tests exist to verify -- unrelated to and unaffected by this
branch's own diff, confirmed by reproducing the identical failure via
`git stash` and again in a clean upstream/main worktree.

Mock fetch to 404 (matching this file's established pattern for
network-adjacent tests) and keep the LOOPOVER_DRIFT_ISSUE_REPO
override the sibling tests in this file already use, so the fixture
repo name no longer collides with the real self-repo's live config or
its bundled fallback.
…etch

Same root cause as the earlier backfill.test.ts fix: both tests create
a fixture repo literally named "JSONbored/gittensory" without
overriding LOOPOVER_DRIFT_ISSUE_REPO away from createTestEnv()'s
default, so the repo-settings resolver's un-mocked (or 404-catch-all)
manifest fetch falls through to the bundled self-repo manifest, which
now carries real autonomy: auto config (JSONbored#773) and
silently overrides these tests' DB-only settings.

- test/integration/api.test.ts: "serves installation repair
  diagnostics and refreshes installation health" had no fetch mock at
  all for its first two /repair calls, so pull_requests came back
  "write" instead of the expected baseline "read".
- test/unit/queue-5.test.ts: "the live slop gate fetches the PR's own
  commit messages" already mocked fetch with a 404 catch-all, but
  still matched the self-repo fallback via the env default, silently
  routing the PR through the self-repo's manifest instead of the DB
  settings under test -- surfaced as a null slopRisk instead of 15.

Confirmed unrelated to this branch's own diff by reproducing both
failures in a clean upstream/main checkout before fixing.
@galuis116
galuis116 requested a review from JSONbored as a code owner July 16, 2026 11:48
@superagent-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@codecov

codecov Bot commented Jul 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 95.56%. Comparing base (c76ece3) to head (0009f0a).
⚠️ Report is 3 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #6526   +/-   ##
=======================================
  Coverage   95.56%   95.56%           
=======================================
  Files         589      589           
  Lines       47121    47121           
  Branches    14989    14989           
=======================================
  Hits        45032    45032           
  Misses       1297     1297           
  Partials      792      792           
Flag Coverage Δ
shard-1 43.96% <ø> (-0.11%) ⬇️
shard-2 36.90% <ø> (+0.19%) ⬆️
shard-3 32.36% <ø> (+0.01%) ⬆️
shard-4 35.24% <ø> (ø)
shard-5 30.98% <ø> (ø)
shard-6 45.38% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

@loopover-orb loopover-orb Bot added the gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier. label Jul 16, 2026
@loopover-orb

loopover-orb Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Tip

✅ LoopOver review result - approve/merge recommended

Review updated: 2026-07-16 12:07:50 UTC

7 files · 1 AI reviewer · no blockers · readiness 100/100 · CI green · clean

✅ Suggested Action - Approve/Merge

  • safe to merge

Review summary
This PR wires up five previously-advertised-but-unregistered MCP tools (`loopover_run_local_scorer`, `loopover_build_plan`, `loopover_plan_status`, `loopover_record_step_result`, `loopover_predict_gate`) on the local stdio server, closing the gap called out in #6150. The plan-DAG logic is a faithful hand-port of `src/services/plan-dag.ts` (buildPlanDag/validatePlanDag/nextReadySteps/applyStepResult/planProgress all line up with the TS source), `loopover_run_local_scorer` correctly imports `computeLocalScorerTokens` directly from `@​loopover/engine`, and `loopover_predict_gate` proxies to the existing `/v1/local/branch-analysis` route and extracts `predictedGate` rather than duplicating gate-verdict logic. Tests drive the real stdio server end-to-end (not mocked) and cover the zod-rejection and HTTP-failure paths, and the tool-count assertions (55→60) are updated consistently across `mcp-tool-rename-aliases.test.ts`.

Nits — 5 non-blocking
  • The plan-DAG state machine (buildPlanDag/validatePlanDag/nextReadySteps/applyStepResult/planProgress, ~90 lines) is hand-duplicated from src/services/plan-dag.ts rather than imported; any future change to the state machine's semantics in the source file will silently drift from this copy since there's no shared type or test asserting parity between the two implementations.
  • packages/loopover-mcp/bin/loopover-mcp.js:537-603 introduces many unexplained magic length limits (400, 2000, 500, 50, 300, 60, 40000) for the new zod shapes — worth a short comment or shared constant given how many show up in one block.
  • validatePlanDag (loopover-mcp.js:134) mirrors the same ~16-branch cyclomatic complexity as its TS source counterpart; not a new regression, but worth flagging if the hand-duplication convention continues to grow this file (already 1425 lines).
  • Consider extracting src/services/plan-dag.ts's pure functions into @​loopover/engine's export map (as the PR description notes is future work) to eliminate the duplicate-and-drift risk long-term, following the same path already taken for computeLocalScorerTokens.
  • Add a lightweight test or lint rule that fails if the hand-duplicated plan-DAG functions in loopover-mcp.js diverge from src/services/plan-dag.ts's behavior for a shared set of fixtures.

Decision drivers

  • ✅ Code review — No blockers (1 reviewer)
  • ✅ Gate result — Passing (No configured blocker found.)
Context & advisory signals — never blocks the verdict
Signal Result Evidence
Linked issue ✅ Linked #6150
Related work ✅ No active overlap found No same-issue or scoped active PR overlap found.
Change scope ✅ 20/20 Low review scope from cached public metadata (1 linked issue).
Validation posture ✅ 25/25 PR body includes validation/test evidence.
Contributor workload ✅ 10/10 Author activity: 1937 registered-repo PR(s), 1276 merged, 54 issue(s).
Contributor context ✅ Confirmed Gittensor contributor galuis116; Gittensor profile; 1937 PR(s), 54 issue(s).
Improvement ✅ Minor risk: clean · value: minor
Review context
  • Author: galuis116
  • Role context: outside_contributor
  • Public audience mode: oss maintainer
  • Lane context: Repository is configured for direct PR review.
  • Public profile languages: JavaScript, Python, Dart, TypeScript, HTML, MDX, Rust, C++
  • Official Gittensor activity: 1937 PR(s), 54 issue(s).
  • PR-specific overlap: none found.
Contributor next steps
  • Keep the PR focused and include validation evidence before maintainer review.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.
🧪 Chat with LoopOver

Ask LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.

  • @loopover ask &lt;question&gt; answers contribution-quality Q&A with source citations and freshness.
  • @loopover chat &lt;question&gt; answers in natural prose from cached decision-pack facts via local inference (maintainer/collaborator; read-only).
  • A plain-language @loopover mention with a real question is routed to the closest matching read-only command automatically — no exact syntax required.

Full command reference: https://loopover.ai/docs/loopover-commands

🧪 Experimental — new and may change.

🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed


💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →.

Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.

  • Re-run LoopOver review

@loopover-orb loopover-orb Bot added the manual-review Gittensor contributor context label Jul 16, 2026
@JSONbored
JSONbored merged commit 1aa6603 into JSONbored:main Jul 16, 2026
16 checks passed
@loopover-orb loopover-orb Bot added gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. and removed gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier. labels Jul 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. manual-review Gittensor contributor context

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(mcp): register plan-DAG tools + local scorer (build_plan/plan_status/record_step_result/run_local_scorer/predict_gate) in packages/loopover-mcp

2 participants