Skip to content

fix: carry the IDE entry's env when wiring the datamate stdio MCP server - #1081

Open
ralphstodomingo wants to merge 5 commits into
mainfrom
fix/datamate-stdio-env
Open

fix: carry the IDE entry's env when wiring the datamate stdio MCP server#1081
ralphstodomingo wants to merge 5 commits into
mainfrom
fix/datamate-stdio-env

Conversation

@ralphstodomingo

@ralphstodomingo ralphstodomingo commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Issue for this PR

Closes #1082

Type of change

  • Bug fix

What does this PR do?

Fixes the bug where datamate-cli.js suddenly opens as an editor tab when launching sessions, and the datamate MCP server dies with -32000 Connection closed.

On desktop editors the extension-written .vscode/mcp.json datamate stdio entry has command = the editor's Electron binary and env: {"ELECTRON_RUN_AS_NODE": "1"} (Electron only runs the script as Node with that flag; without it, the editor GUI boots and opens the script as a document). datamate_manager add reused the entry's command + args but dropped the env block, both in the immediate spawn and in the entry persisted to .altimate-code/altimate-code.json — so the file popped on add and again on every later session launch, with no self-repair in TUI/run (the healing sync only ran on serve boot).

Changes:

  • readDatamateTransportFromIde now returns the IDE entry's env (minus ALTIMATE_EXTENSION_RPC, mirroring the sync path) and updatedAt; handleAdd carries the env into the runtime MCP config and persists it as environment, plus updatedAt on disk so the sync recognizes the entry as current.
  • The sync path's inline env-strip is extracted into a shared extractSpawnEnvironment helper so the two paths stay in lockstep.
  • The TUI worker and run now run syncDatamateUrlFromVscodeMcp before the first session, as serve already did — entries already persisted broken in the field self-heal on the next launch. The heal is scoped to the containing git project root (resolveDatamateSyncRoot), and in the worker it is sequenced strictly before config load, the first in-process request, and Server.listen, so the first session connects with the healed entry rather than a stale cached one. datamate_manager add on an existing-but-disconnected entry likewise refreshes it from the current IDE transport before connecting.
  • Scope note: everything above is datamate-specific except one known side effect of the wider sync trigger — syncDatamateUrlFromVscodeMcp has a second pass that refreshes the URL (and updatedAt) of other remote MCP entries mirrored from the IDE config (name match, URL differs). That pass is not new behavior — serve boot has always run it — TUI/run now just apply the same refresh consistently. Spawn/env behavior for non-datamate servers is unchanged.

How did you verify your code works?

E2E in the docker code-server harness against a desktop-shaped mcp.json entry (command = an Electron-contract shim that opens its args as documents unless ELECTRON_RUN_AS_NODE=1), driven through real run sessions:

Scenario Published 0.8.10 This branch
datamate_manager add file pops, -32000 Connection closed, env-less entry persisted no pop, connected as 'datamate', entry persists environment + updatedAt
Plain session launch with the 0.8.10-written (env-less) entry file pops on every launch entry healed before MCP connect, no pop

Unit tests: test/release-validation/mcp-datamate-stdio-env.test.ts covers the env carry (strip rule, omission when empty, back-compat bare shape, non-string filtering) and sync parity. Existing mcp-datamate-893 suite unchanged and green; tsgo --noEmit clean.

Screenshots / recordings

Before — datamate_manager add pops the file open:

before

After — same broken persisted entry, next session heals it and nothing pops:

after

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Summary by CodeRabbit

  • New Features

    • Datamate connections now preserve update timestamps and relevant local environment settings.
    • Project synchronization automatically detects the project root, including nested Git directories.
    • Existing IDE connection settings are retained while connection details refresh automatically.
  • Bug Fixes

    • Internal extension RPC variables are excluded from synchronized environments.
    • Reconnection reliability has improved, while startup and requests remain non-blocking if synchronization fails.
  • Tests

    • Added coverage for environment filtering, timestamp preservation, project-root detection, and configuration synchronization.

`datamate_manager add` reused the command + args from the IDE's `mcp.json`
`datamate` entry but dropped its `env` block, both in the immediate spawn and
in the entry persisted to `.altimate-code/altimate-code.json`. On desktop
editors the command is the editor's Electron binary and `env` carries
`ELECTRON_RUN_AS_NODE=1` — spawned without it, the editor GUI boots and opens
`datamate-cli.js` as a document, the MCP client reports `-32000 Connection
closed`, and the broken persisted entry re-pops the file on every subsequent
session launch.

- `readDatamateTransportFromIde` now returns the entry's env (minus
  `ALTIMATE_EXTENSION_RPC`, mirroring the sync path) and `updatedAt`;
  `handleAdd` carries the env into the runtime config and persists it as
  `environment`, plus `updatedAt` on disk so the sync recognizes the entry
  as current.
- The sync path's inline env-strip is extracted into the shared
  `extractSpawnEnvironment` helper so both paths stay in lockstep.
- The TUI worker and `run` now run `syncDatamateUrlFromVscodeMcp` before the
  first session (as `serve` already did), so entries already persisted
  without `environment` self-heal on the next launch.
@ralphstodomingo ralphstodomingo self-assigned this Aug 7, 2026
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 07085664-3db6-447f-97f8-07a96842cbce

📥 Commits

Reviewing files that changed from the base of the PR and between 7bcc9b6 and 37c3d2c.

📒 Files selected for processing (1)
  • packages/opencode/src/altimate/tools/datamate.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/opencode/src/altimate/tools/datamate.ts

📝 Walkthrough

Walkthrough

Datamate local transports now preserve filtered environment variables and updatedAt. Run and TUI startup paths synchronize VS Code MCP configuration before use. Regression tests cover environment filtering, Git-root resolution, and persisted transport metadata.

Changes

Datamate synchronization

Layer / File(s) Summary
Transport metadata and discovery
packages/opencode/src/altimate/datamate-transport.ts
Local and remote transports support updatedAt. Local transports support filtered environment values. Discovery and synchronization preserve valid values and resolve the Git project root when available.
Configuration synchronization and persistence
packages/opencode/src/altimate/datamate-transport.ts, packages/opencode/src/altimate/tools/datamate.ts
Synchronization preserves filtered environment values and timestamps. Existing entries retain non-transport fields, set enabled: true, write refreshed configuration, and reconnect with MCP.add().
Startup synchronization and validation
packages/opencode/src/cli/cmd/run.ts, packages/opencode/src/cli/tui/worker.ts, packages/opencode/test/release-validation/mcp-datamate-stdio-env.test.ts
Run and TUI paths perform best-effort synchronization before startup, RPC fetches, and external server startup. Tests cover environment conversion, root resolution, and persisted metadata.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant RunCommand
  participant DatamateTransport
  participant MCPConfig
  participant DatamateGateway
  RunCommand->>DatamateTransport: resolve project root
  RunCommand->>MCPConfig: synchronize Datamate entry
  MCPConfig->>DatamateTransport: read command, environment, and updatedAt
  DatamateTransport-->>MCPConfig: return filtered transport metadata
  MCPConfig->>DatamateGateway: persist refreshed entry
  DatamateGateway-->>RunCommand: complete or suppress synchronization error
  RunCommand->>DatamateGateway: start local session
Loading

Poem

A rabbit keeps the Node flag bright,
Filters stray variables from sight.
Timestamps hop into the stream,
Startup entries heal the scheme.
MCP runs without surprise.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the primary fix: preserving the IDE environment for the Datamate stdio MCP server.
Description check ✅ Passed The description completes all required sections and explains the cause, implementation, verification, screenshots, and checklist status.
Linked Issues check ✅ Passed The changes satisfy issue [#1082] by preserving the IDE environment and enabling self-healing before TUI and run sessions.
Out of Scope Changes check ✅ Passed The changes remain within the stated Datamate synchronization objectives, including consistent existing remote MCP URL refresh behavior.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/datamate-stdio-env

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

Thanks for updating your PR! It now meets our contributing guidelines. 👍

@AltimateAI AltimateAI deleted a comment from github-actions Bot Aug 7, 2026
@ralphstodomingo
ralphstodomingo marked this pull request as ready for review August 7, 2026 04:26
Copilot AI review requested due to automatic review settings August 7, 2026 04:26

@claude claude 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.

Claude Code Review

This repository is configured for manual code reviews. Comment @claude review for a one-time review, or @claude review always to subscribe this PR to a review on every future push.

Tip: disable this comment in your organization's Code Review settings.

Copilot AI 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.

Pull request overview

This PR fixes a desktop-editor regression where the IDE-provided datamate stdio MCP entry’s env (notably ELECTRON_RUN_AS_NODE=1) was dropped when wiring/persisting the server, causing Electron to boot the editor UI and open datamate-cli.js as a tab, and leading to -32000 Connection closed. It also expands the “heal from .vscode/mcp.json” sync behavior so terminal entrypoints (run/TUI worker) self-repair already-persisted broken entries, matching serve startup behavior.

Changes:

  • Carry the IDE env (minus ALTIMATE_EXTENSION_RPC) and updatedAt through readDatamateTransportFromIde, datamate_manager add runtime wiring, and persisted config.
  • Deduplicate env-stripping logic into a shared extractSpawnEnvironment() helper to keep add and sync paths aligned.
  • Trigger syncDatamateUrlFromVscodeMcp earlier for run and the TUI worker so previously-broken persisted entries self-heal on next launch.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/opencode/src/altimate/datamate-transport.ts Adds env + updatedAt propagation for IDE datamate stdio entries; factors env normalization into extractSpawnEnvironment; updates sync to use shared env extraction.
packages/opencode/src/altimate/tools/datamate.ts Ensures datamate_manager add carries environment into runtime MCP config and persists environment + updatedAt to disk.
packages/opencode/src/cli/tui/worker.ts Adds a boot-time datamate sync gate so the worker doesn’t serve requests / start external server mode until the heal attempt finishes.
packages/opencode/src/cli/cmd/run.ts Runs the same datamate sync before bootstrapping a session to self-heal env-less persisted entries.
packages/opencode/test/release-validation/mcp-datamate-stdio-env.test.ts Adds regression coverage for env carry-through, stripping rules, back-compat, and sync parity.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/opencode/src/cli/tui/worker.ts Outdated
Comment on lines +44 to +48
// altimate_change start — datamate entry heal, awaited before the first in-process
// request (session start connects MCP servers from the config this sync repairs).
// Errors are swallowed: a failed sync must never block the TUI.
const datamateSyncReady: Promise<unknown> = syncDatamateUrlFromVscodeMcp(process.cwd()).catch(() => {})
// altimate_change end

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 80d4ad4 — trace init now awaits the heal (traceReady starts with await datamateSyncReady), so InstanceRuntime.load/Config.get() can no longer read concurrently with the non-atomic write.

@kilo-code-bot

kilo-code-bot Bot commented Aug 7, 2026

Copy link
Copy Markdown

Code Review Summary

Status: No Issues Found | Recommendation: Merge

The incremental commit (37c3d2c) hoists the duplicated updatedAt conditional spread from both handleAdd branches into a single shared updatedAtField constant computed once at the top of the IDE/extension-mode block. This is a clean, behavior-preserving refactor that resolves the prior DRY suggestion. transport.updatedAt (non-null inside the transport !== null branch) replaces the now-redundant transport?.updatedAt optional chaining, and the explanatory comment was consolidated at the declaration site.

Files Reviewed (1 file)
  • packages/opencode/src/altimate/tools/datamate.ts
Previous Review Summaries (4 snapshots, latest commit 7bcc9b6)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 7bcc9b6)

Status: 1 Issue Found | Recommendation: Merge (non-blocking suggestion)

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 1
Issue Details (click to expand)

SUGGESTION

File Line Issue
packages/opencode/src/altimate/tools/datamate.ts 284 Duplicated updatedAt conditional spread in both handleAdd branches (also at L303)

The incremental commit (7bcc9b6) correctly extends the datamate env/transport fix to remote transports and fixes a real inconsistency where the live MCP.add client dropped preserved auth/connection settings that the disk write kept. Verified sound:

  • DatamateTransport's remote variant now carries updatedAt?, and readDatamateTransportFromIde returns it for remote entries — parity with the local branch.
  • Both handleAdd updatedAt conditions generalize from transport?.type === "local" && … to transport?.updatedAt, matching the type change.
  • The refresh path's MCP.add now receives the merged refreshed entry instead of the bare mcpConfig. create() only short-circuits on enabled === false, so enabled: true connects exactly as before, and updatedAt/enabled are harmless extra keys in the in-memory s.config (not schema-validated at add, and the disk write is already handled separately by addMcpToConfig).

Only a minor DRY suggestion remains.

Fix these issues in Kilo Cloud

Files Reviewed (3 files)
  • packages/opencode/src/altimate/datamate-transport.ts
  • packages/opencode/src/altimate/tools/datamate.ts
  • packages/opencode/test/release-validation/mcp-datamate-stdio-env.test.ts

Previous review (commit 1cb8fad)

Status: No Issues Found | Recommendation: Merge

The incremental commit (1cb8fad) is a focused refactor that extracts the previously-duplicated TRANSPORT_FIELDS set into a single shared, exported TRANSPORT_IDENTITY_FIELDS constant in datamate-transport.ts, consumed by both syncDatamateUrlFromVscodeMcp and datamate_manager add's refresh path. This directly resolves the prior review's only SUGGESTION (drift risk between the two local sets).

Behavior is verified identical at both call sites:

  • Sync path: old set {type, command, args, environment, url, updatedAt}TRANSPORT_IDENTITY_FIELDS (same 6 fields).
  • handleAdd refresh: old set {…6 fields…, enabled}new Set([...TRANSPORT_IDENTITY_FIELDS, "enabled"]) (same 7 fields).

No new issues introduced; the enabled-added-locally rationale is documented inline.

Files Reviewed (2 files)
  • packages/opencode/src/altimate/datamate-transport.ts
  • packages/opencode/src/altimate/tools/datamate.ts

Previous review (commit 80d4ad4)

Status: 1 Issue Found | Recommendation: Merge (non-blocking)

Overview

Severity Count
CRITICAL 0
WARNING 0
SUGGESTION 1

The incremental changes (commit 80d4ad4c) correctly resolve the prior review's concerns: the heal is now scoped to the git project root (resolveDatamateSyncRoot) so subdirectory launches find the IDE config + persisted entry, the TUI worker sequences the heal strictly before InstanceRuntime.load/Config.get() (removing the concurrent read/write window), and the in-config-but-not-connected branch now refreshes the persisted entry from the current IDE transport via the established readMcpEntryFromDisk + MCP.add pattern (matching the reload-datamate endpoint) before reconnecting. The primary local-stdio ELECTRON_RUN_AS_NODE fix is sound. Only one minor maintainability nit below.

Issue Details (click to expand)

SUGGESTION

File Line Issue
packages/opencode/src/altimate/tools/datamate.ts 273 TRANSPORT_FIELDS duplicates the set in datamate-transport.ts:258; drift risk
Files Reviewed (5 files)
  • packages/opencode/src/altimate/datamate-transport.ts
  • packages/opencode/src/altimate/tools/datamate.ts
  • packages/opencode/src/cli/cmd/run.ts
  • packages/opencode/src/cli/tui/worker.ts
  • packages/opencode/test/release-validation/mcp-datamate-stdio-env.test.ts

Fix these issues in Kilo Cloud

Previous review (commit cbf4f65)

Status: No Issues Found | Recommendation: Merge

The fix correctly carries the IDE mcp.json env block (notably ELECTRON_RUN_AS_NODE) through both the datamate_manager add path and the mcp.json sync path. The refactor extracts a shared extractSpawnEnvironment helper that is behaviorally equivalent to the prior inline strip for normal cases while additionally filtering non-string values and validating the object shape — a strict, non-regressing improvement. updatedAt is persisted disk-only in handleAdd, matching how syncDatamateUrlFromVscodeMcp already records it, and the new TUI/run heal is awaited before the first session/connect in the correct order using process.cwd() consistently. Fork-only files need no altimate_change markers, and the run.ts/worker.ts additions are correctly wrapped. The new test uses await using tmpdir() (proper disposal) and covers the strip rule, empty-env omission, back-compat bare shape, non-string filtering, and sync parity.

Files Reviewed (5 files)
  • packages/opencode/src/altimate/datamate-transport.ts
  • packages/opencode/src/altimate/tools/datamate.ts
  • packages/opencode/src/cli/cmd/run.ts
  • packages/opencode/src/cli/tui/worker.ts
  • packages/opencode/test/release-validation/mcp-datamate-stdio-env.test.ts

Reviewed by glm-5.2 · Input: 22.6K · Output: 3.4K · Cached: 197.2K

Review guidance: REVIEW.md from base branch main

@cubic-dev-ai cubic-dev-ai 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.

3 issues found across 5 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/opencode/src/altimate/datamate-transport.ts">

<violation number="1" location="packages/opencode/src/altimate/datamate-transport.ts:137">
P2: Datamate entries using supported MCP env references are launched with the literal `${VAR}` value instead of the resolved environment value. The IDE-specific read and sync paths should apply the shared `ConfigPaths.resolveEnvVarsInString` handling before returning or persisting the environment, while preserving the existing single-pass escape semantics.</violation>
</file>

<file name="packages/opencode/src/cli/cmd/run.ts">

<violation number="1" location="packages/opencode/src/cli/cmd/run.ts:951">
P2: This adds a full-project `**/mcp.json` glob scan to the synchronous startup path of every local `run` invocation, since the sync is awaited before `bootstrap`. For a one-shot CLI this is per-invocation latency even when the user has no datamate entry — the scan runs before the function discovers there is nothing to heal. Consider guarding this so it only runs when a datamate IDE entry is actually present (or launching it concurrently with bootstrap rather than awaiting a blocking scan), so ordinary `run` invocations don't regress in startup time.</violation>
</file>

<file name="packages/opencode/src/cli/tui/worker.ts">

<violation number="1" location="packages/opencode/src/cli/tui/worker.ts:83">
P2: The datamate heal sync is now placed on the TUI worker's startup critical path: the first `rpc.fetch` (and `Server.listen` in external-server mode) awaits `datamateSyncReady`, which runs a recursive `**/mcp.json` glob across the whole project before it can short-circuit. Every TUI session pays this scan latency on the first request, even for users with no datamate/IDE entry. Consider not blocking the first request on the scan — run the sync in parallel with bootstrap and let the session's existing self-repair path pick it up, or short-circuit the sync (skip the glob) when no datamate entry/dir is present, so startup latency isn't tied to filesystem traversal.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/opencode/src/cli/tui/worker.ts Outdated
Comment thread packages/opencode/src/altimate/tools/datamate.ts
const args = Array.isArray(entry["args"]) ? (entry["args"] as string[]) : []
if (cmd) {
return { type: "local", command: [cmd, ...args] }
const environment = extractSpawnEnvironment(entry["env"])

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Datamate entries using supported MCP env references are launched with the literal ${VAR} value instead of the resolved environment value. The IDE-specific read and sync paths should apply the shared ConfigPaths.resolveEnvVarsInString handling before returning or persisting the environment, while preserving the existing single-pass escape semantics.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/opencode/src/altimate/datamate-transport.ts, line 137:

<comment>Datamate entries using supported MCP env references are launched with the literal `${VAR}` value instead of the resolved environment value. The IDE-specific read and sync paths should apply the shared `ConfigPaths.resolveEnvVarsInString` handling before returning or persisting the environment, while preserving the existing single-pass escape semantics.</comment>

<file context>
@@ -108,11 +127,21 @@ export async function readDatamateTransportFromIde(
       const args = Array.isArray(entry["args"]) ? (entry["args"] as string[]) : []
       if (cmd) {
-        return { type: "local", command: [cmd, ...args] }
+        const environment = extractSpawnEnvironment(entry["env"])
+        const updatedAt = typeof entry["updatedAt"] === "string" ? entry["updatedAt"] : undefined
+        return {
</file context>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Acknowledged but deliberately not changed here: this is a pre-existing parity gap shared with syncDatamateUrlFromVscodeMcp, which has persisted the env block verbatim since the transport layer landed — this PR's read path just mirrors it (the shared extractSpawnEnvironment keeps them in lockstep). In practice the extension writes only literal values (ELECTRON_RUN_AS_NODE, the RPC socket path), never ${VAR} references, and persisted entries still go through config-load substitution. Unifying with resolveServerEnvVars would change the sync path's semantics too, so it belongs in its own change — parked as a follow-up.

Comment thread packages/opencode/src/cli/cmd/run.ts Outdated
Comment thread packages/opencode/src/cli/cmd/run.ts Outdated
// re-spawned broken on every run invocation with no path to self-repair.
{
const { syncDatamateUrlFromVscodeMcp } = await import("../../altimate/datamate-transport")
await syncDatamateUrlFromVscodeMcp(process.cwd()).catch(() => {})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: This adds a full-project **/mcp.json glob scan to the synchronous startup path of every local run invocation, since the sync is awaited before bootstrap. For a one-shot CLI this is per-invocation latency even when the user has no datamate entry — the scan runs before the function discovers there is nothing to heal. Consider guarding this so it only runs when a datamate IDE entry is actually present (or launching it concurrently with bootstrap rather than awaiting a blocking scan), so ordinary run invocations don't regress in startup time.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/opencode/src/cli/cmd/run.ts, line 951:

<comment>This adds a full-project `**/mcp.json` glob scan to the synchronous startup path of every local `run` invocation, since the sync is awaited before `bootstrap`. For a one-shot CLI this is per-invocation latency even when the user has no datamate entry — the scan runs before the function discovers there is nothing to heal. Consider guarding this so it only runs when a datamate IDE entry is actually present (or launching it concurrently with bootstrap rather than awaiting a blocking scan), so ordinary `run` invocations don't regress in startup time.</comment>

<file context>
@@ -942,6 +942,15 @@ You are speaking to a non-technical business executive. Follow these rules stric
+    // re-spawned broken on every run invocation with no path to self-repair.
+    {
+      const { syncDatamateUrlFromVscodeMcp } = await import("../../altimate/datamate-transport")
+      await syncDatamateUrlFromVscodeMcp(process.cwd()).catch(() => {})
+    }
+    // altimate_change end
</file context>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Deliberate trade-off: the scan must complete before the session's MCP connect or the heal doesn't apply to the launch the user is looking at (see the sibling P1 about exactly that ordering — deferring the sync and strict ordering are mutually exclusive). The cost is identical to what altimate serve boot has paid since the sync was introduced, and the scan is the sync's inherent cost, not something this call site adds. If it shows up in real startup profiles, narrowing the scan itself (known IDE dirs first) would help every caller and is a better follow-up than special-casing this one.

async fetch(input: { url: string; method: string; headers: Record<string, string>; body?: string }) {
// altimate_change start — no request is served until the datamate entry heal
// completes (already-resolved after the first request; effectively free thereafter).
await datamateSyncReady

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: The datamate heal sync is now placed on the TUI worker's startup critical path: the first rpc.fetch (and Server.listen in external-server mode) awaits datamateSyncReady, which runs a recursive **/mcp.json glob across the whole project before it can short-circuit. Every TUI session pays this scan latency on the first request, even for users with no datamate/IDE entry. Consider not blocking the first request on the scan — run the sync in parallel with bootstrap and let the session's existing self-repair path pick it up, or short-circuit the sync (skip the glob) when no datamate entry/dir is present, so startup latency isn't tied to filesystem traversal.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/opencode/src/cli/tui/worker.ts, line 83:

<comment>The datamate heal sync is now placed on the TUI worker's startup critical path: the first `rpc.fetch` (and `Server.listen` in external-server mode) awaits `datamateSyncReady`, which runs a recursive `**/mcp.json` glob across the whole project before it can short-circuit. Every TUI session pays this scan latency on the first request, even for users with no datamate/IDE entry. Consider not blocking the first request on the scan — run the sync in parallel with bootstrap and let the session's existing self-repair path pick it up, or short-circuit the sync (skip the glob) when no datamate entry/dir is present, so startup latency isn't tied to filesystem traversal.</comment>

<file context>
@@ -65,6 +78,10 @@ let server: Awaited<ReturnType<typeof Server.listen>> | undefined
   async fetch(input: { url: string; method: string; headers: Record<string, string>; body?: string }) {
+    // altimate_change start — no request is served until the datamate entry heal
+    // completes (already-resolved after the first request; effectively free thereafter).
+    await datamateSyncReady
+    // altimate_change end
     const headers = { ...input.headers }
</file context>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Same trade-off as the run latency comment: correctness requires heal-before-first-connect (your own P1 on this file asks for strictly tighter ordering, which 80d4ad4 implements), so the scan can't move off the critical path without reintroducing that bug. Cost matches serve's existing boot behavior; narrowing the scan itself is the right follow-up if profiling warrants it.

Comment thread packages/opencode/src/cli/tui/worker.ts Outdated
…root sync scope

- TUI worker: the datamate heal is now sequenced strictly before
  `InstanceRuntime.load`/`Config.get()` (trace init awaits it), so the config
  read can neither race the non-atomic write nor cache the pre-heal entry —
  the first session connects with the healed config.
- `datamate_manager add`: the in-config-but-not-connected branch refreshes the
  persisted entry from the current IDE transport (preserving user-managed
  fields) and connects via `MCP.add`, instead of `MCP.connect` which re-reads
  the stale in-memory entry.
- Boot heals (`run`, TUI worker) scan from the containing git project root via
  the new `resolveDatamateSyncRoot`, not raw cwd — a session launched from a
  subdirectory now finds the root IDE config and persisted entry.
})
await MCP.connect(DATAMATE_KEY)
const existing = await readMcpEntryFromDisk(DATAMATE_KEY, configPath)
const TRANSPORT_FIELDS = new Set(["type", "command", "args", "environment", "url", "updatedAt", "enabled"])

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

SUGGESTION: TRANSPORT_FIELDS duplicates the near-identical set already defined in syncDatamateUrlFromVscodeMcp (packages/opencode/src/altimate/datamate-transport.ts:258), differing only by enabled.

Both encode the same idea — fields re-derived from the transport rather than user-managed. If one list later grows a new transport field (e.g. headers) and the other doesn't, the two paths will silently disagree on what gets preserved. Consider exporting a shared base set (e.g. TRANSPORT_IDENTITY_FIELDS) from datamate-transport.ts and layering the per-site extra (enabled here) on top.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Applied in 1cb8fadTRANSPORT_IDENTITY_FIELDS is now a shared exported set in datamate-transport.ts, used by the sync directly and by the add-refresh path with enabled layered on top (that path re-derives it as true).

…dd refresh

Both paths encode the same idea — entry fields re-derived from the IDE
transport versus user-managed fields carried forward. A single exported set
keeps them from silently diverging when a new transport field is added;
the add-refresh path layers `enabled` on top since it re-derives that too.

@cubic-dev-ai cubic-dev-ai 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.

All reported issues were addressed across 5 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/opencode/src/altimate/tools/datamate.ts Outdated
Comment thread packages/opencode/src/altimate/tools/datamate.ts Outdated
Comment thread packages/opencode/src/altimate/tools/datamate.ts Outdated
…rry updatedAt for remote

- The add-refresh path wrote the merged entry (preserved headers/oauth/timeout
  + fresh transport) to disk but connected the live client with the bare
  transport config, dropping authentication and connection settings for the
  session being connected. MCP.add now receives the same merged entry as the
  disk write, matching the reload-datamate endpoint.
- The remote transport variant now carries updatedAt like the local one, so a
  remote datamate added via datamate_manager is not rewritten once by the next
  boot's sync purely for the missing change signal.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/opencode/src/altimate/datamate-transport.ts (1)

277-284: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Serialize MCP config writes before this sync path.

addMcpToConfig reads mcpConfig, then writes with modify + Filesystem.write without a lock. Concurrent datamate_manager add writes to the same server can overwrite newer fields such as environment, updatedAt, or user-managed headers/oauth/timeout. Add a per-config-path lock or update queue that covers IDE sync and datamate_manager add, and keep the lock shared when resolveConfigPath points to the same file.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/opencode/src/altimate/datamate-transport.ts` around lines 277 - 284,
Serialize the read-modify-write flow in addMcpToConfig with a per-config-path
lock or update queue covering both IDE synchronization and datamate_manager add
operations. Ensure resolveConfigPath results sharing the same file reuse the
same lock, and hold it through mcpConfig reads, modify, and Filesystem.write so
newer environment, updatedAt, headers, oauth, and timeout fields are not
overwritten.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/opencode/src/altimate/datamate-transport.ts`:
- Around line 23-24: Update syncDatamateUrlFromVscodeMcp to compare the datamate
entry’s TRANSPORT_IDENTITY_FIELDS whenever readDatamateTransportFromIde returns
a transport without vscodeUpdatedAt, while preserving timestamp-based
synchronization when the timestamp is present. Add a regression test covering a
timestamp-less IDE transport and verifying that altimate-code.json is
synchronized.

---

Outside diff comments:
In `@packages/opencode/src/altimate/datamate-transport.ts`:
- Around line 277-284: Serialize the read-modify-write flow in addMcpToConfig
with a per-config-path lock or update queue covering both IDE synchronization
and datamate_manager add operations. Ensure resolveConfigPath results sharing
the same file reuse the same lock, and hold it through mcpConfig reads, modify,
and Filesystem.write so newer environment, updatedAt, headers, oauth, and
timeout fields are not overwritten.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5cbe1fe5-948b-4030-829b-bd6729445c96

📥 Commits

Reviewing files that changed from the base of the PR and between 80d4ad4 and 7bcc9b6.

📒 Files selected for processing (3)
  • packages/opencode/src/altimate/datamate-transport.ts
  • packages/opencode/src/altimate/tools/datamate.ts
  • packages/opencode/test/release-validation/mcp-datamate-stdio-env.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/opencode/test/release-validation/mcp-datamate-stdio-env.test.ts
  • packages/opencode/src/altimate/tools/datamate.ts

Comment thread packages/opencode/src/altimate/datamate-transport.ts
@ralphstodomingo

Copy link
Copy Markdown
Contributor Author

Re CodeRabbit's outside-diff finding (serialize addMcpToConfig writes): valid hardening suggestion, but the lock-free read-modify-write predates this PR — sync (serve boot + reload endpoint) and datamate_manager add have always been able to interleave across processes. Within a process this PR makes ordering stricter, not looser: the boot heal is sequenced before the first session, so it cannot run concurrently with a session-invoked add. A per-config-path write queue is parked as a follow-up rather than grown into this fix.

...preserved,
...mcpConfig,
enabled: true,
...(transport?.updatedAt ? { updatedAt: transport.updatedAt } : {}),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

SUGGESTION: transport?.updatedAt ? { updatedAt: transport.updatedAt } : {} is now duplicated at line 303

The identical conditional spread appears in both the refresh branch (here, in refreshed) and the new-entry branch (diskEntry), both derived from the same transport. Hoist a single const updatedAtPart = transport?.updatedAt ? { updatedAt: transport.updatedAt } : {} above the if (existingNames.includes(...)) split and spread ...updatedAtPart in both objects. This mirrors the TRANSPORT_IDENTITY_FIELDS consolidation from the prior commit and keeps the two paths in lockstep if the updatedAt shape ever changes.


Reply with @kilocode-bot fix it to have Kilo Code address this issue.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Applied in 37c3d2c — single updatedAtField hoisted above the branch, documented once.

Both the refresh and new-entry branches persisted the transport's updatedAt
with the same conditional spread; a single `updatedAtField` above the branch
keeps them from drifting, and the disk-only rationale is documented once.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

datamate-cli.js opens as an editor tab when launching sessions (stdio MCP spawn loses ELECTRON_RUN_AS_NODE)

2 participants