CtrlK
BlogDocsLog inGet started
Tessl Logo

merge-upstream-vscode

This skill should be used when merging upstream changes from vscode-server into the Positron IDE repository. Invoke it when the git repository contains the conflicted changes from an upstream merge.

68

Quality

83%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Passed

No findings from the security scan

SKILL.md
Quality
Evals
Security

Merge Upstream VSCode

Background

Positron is a fork of a project called 'vscode-server' (https://github.com/rstudio/vscode-server), which is itself a fork of VS Code, aka Code OSS (https://github.com/microsoft/vscode). vscode-server is a version of Code OSS that has been customized to run in Posit Workbench (annotated in the source as PWB). Positron, this repository, is a fork of vscode-server, and runs in both Posit Workbench and as a standalone desktop app for macOS, Windows, and Linux.

On a regular basis, changes are merged from Code OSS into vscode-server, and then from vscode-server into Positron.

The skill is typically invoked in the middle of a cherry-pick merge from vscode-server into Positron. The working tree is dirty and contains conflicted changes from the upstream merge.

Your goal is to resolve the conflicts that arise in this second phase until the code compiles cleanly and passes tests.

General Principles

Extension

Positron's main goal is to extend Code OSS with addtional functionality; only in a few places do we actually need to override or modify the upstream code. So where genuine conflicts are encountered, your goal should generally be to resolve them in a way that preserves Positron's changes while still integrating with the upstream code.

Here is an example:

// extensions/copilot has its own code style
	'!extensions/copilot/**',

	'!src/vs/base/browser/dompurify/**',
	'!src/vs/workbench/services/keybinding/browser/keyboardLayouts/**',
	'!src/vs/workbench/contrib/terminal/common/scripts/psreadline/**',

<<<<<<< HEAD
	// --- Start Positron ---
	'!scripts/positron/**/*',
	// --- End Positron ---
=======
	// Files with licences
	'!src/vs/platform/endpoint/common/licenseAgreement.ts',
>>>>>>> 83301f3f7d9 (Upstream Code OSS changes from 1.124.0 to 1.130.0)

In this case, Positron has added an entry to an array in the same place as the upstream change, so the conflict can be resolved creating a change that preserves both the upstream and Positron changes.

Modification

In other places, Positron needs to modify the upstream code to add or change functionality. In these cases, the conflict can be resolved by accepting the upstream changes and then reapplying the Positron changes. For example:

<<<<<<< HEAD
			// --- Start Positron ---
			.pipe(jsonEditor({ commit, date: readISODate(sourceFolderName), version, positronVersion, positronBuildNumber, quality: releaseChannel }))
			// --- End Positron ---
=======
			.pipe(jsonEditor((json: Record<string, unknown>) => {
				json.commit = commit;
				json.date = readISODate(sourceFolderName);
				json.version = version;
				// Stamp agentSdks from the per-platform results file produced
				// by `build/agent-sdk/produce.ts`. REH-only: REH-web is
				// browser-served and the agent host is node-only, so the
				// SDK config has no consumer there.
				if (type === 'reh') {
					const agentSdks = readAgentSdkResults();
					if (Object.keys(agentSdks).length > 0) {
						json.agentSdks = agentSdks;
					}
				}
				return json;
			}))
>>>>>>> 83301f3f7d9 (Upstream Code OSS changes from 1.124.0 to 1.130.0)

Here, Positron has added several fields to the JSON object that are not present in the upstream code. The correct resolution to this class of conflict is to accept the upstream change and then reapply the Positron change by adding the missing fields, matching the new formatting and adding appropriate change markers..

Copilot AI commands and menus

Don't add chatAiFeaturesEnabled to Copilot's AI commands or menu items by hand. Positron already gates every command and menu item from GitHub.copilot-chat where the extension points read them, via gateCopilotContribution in src/vs/workbench/services/actions/common/menusExtensionPoint.ts, so anything upstream adds is covered.

  • Check that the two Positron-marked call sites in menusExtensionPoint.ts survived the merge, one on precondition: and one on item.when =. Nothing else does the gating, so if they're gone the gate is gone.
  • test/e2e/tests/assistant/chat-command-palette-gating.test.ts checks this end to end.

Upstream Divergence

Because vscode-server and Positron merge upstream release branches, it is frequently the case that merge conflicts arise that are simply conflicts between two upstream release branches. If a merge conflict arises that contains no Positron or PWB (Posit Workbench) change markers, it is often a simple conflict between two upstream release branches that can be resolved by accepting the upstream changes.

This is especially common in chat/AI related files.

Here's an example:

<<<<<<< HEAD
                    "key": "updateModePolicy",
                    "value": "Configure whether you receive automatic updates. Requires a restart after change. The updates are fetched from a Microsoft online service."
=======
                    "key": "updateMode",
                    "value": "Configure whether you receive automatic updates. The updates are fetched from a Microsoft online service."
>>>>>>> 83301f3f7d9 (Upstream Code OSS changes from 1.124.0 to 1.130.0)

This is clearly a conflict between two Microsoft changes. Just accept the incoming (upstream) change.

Note that many .json files don't support comments, and therefore don't contain change markers. In these cases, read the code carefully to determine which changes need to be preserved.

Deleted Files

For files that have been changed upstream but deleted in Positron, resolve the conflict by deleting the file in Positron.

Github Workflows

Positron doesn't use any Github workflows from upstream. You can ignore or delete these changes.

Design Decisions

You will occasionally encounter a merge conflict that requires major, nontrivial design decisions. If this happens, stop and ask the user how to proceed, explaining the problem and suggesting solutions while noting the tradeoffs.

Logs

Write a log file as you work indicating how you resolved each conflict and why, for the user to review. Place it in the root of the repository, named 'merge-log-X-YYY.txt', replacing X-YYY with the upstream version.

Make a special note of any design decisions you made during the merge that did not require you to consult the user.

Committing

Do not make any commits. The user will do so instead.

Step by step

Step 1: Review

Review the conflicts in the working tree as a whole to note patterns and any big picture issues.

Step 2: Resolve

Resolve the conflicts one by one. Once a file is free of conflict markers, stage (don't commit) it and move on to the next file.

Step 3: Install

Once all conflicts are resolved, perform a dependency install (npm install). You may need to nvm install first if the change introduces a new Node version. Stage any lockfile updates. If you see problems, fix them and run npm install again until all lockfile issues are resolved.

Once npm install succeeds, run npm ci until it passes to confirm that the lockfile is complete. Do not skip this: a hand-resolved lockfile can leave an entry that npm install reports as "up to date" (it only reconciles against the node_modules already on disk) while npm ci rejects it with an error like Missing: <pkg>@ from lock file. This bites hardest on overrides (e.g. the sharp stub), where the merge can nest the override under the wrong package instead of at the top level. If npm ci fails, regenerate the lockfile from package.json alone with npm install --package-lock-only rather than editing it further by hand, then confirm with npm ci --dry-run.

Step 4: Compile

Once installation is complete, compile the code to check for compile errors: npm run compile.

Once the basic compilation test is passing, verify that you can run a release build. If you're on macOS: npm run gulp vscode-darwin-arm64

Typecheck the build tooling

npm run compile and the release build both run the gulp pipeline. They do NOT exercise build/next/ (the transpile-based build the dev daemons and launch.json use), and they do not typecheck anything under build/. Those files run via --experimental-strip-types, so a type error there -- for example a call site not updated after the merge changed a function's signature -- has no static check and fails only at runtime. Run the build folder's own typecheck to cover them:

cd build && npm run typecheck && cd ..

This catches the whole class of "the merge added a required parameter and one Positron call site wasn't updated" semantic conflicts (e.g. a 2-vs-3 argument mismatch reported as error TS2554: Expected 3 arguments, but got 2).

This suite is not fully green at baseline -- main carries a few pre-existing errors (e.g. unused-var warnings in the platform gulpfile.vscode.*.ts files). The goal is no new errors in files the merge touched: scan the output for anything under build/next/ or any build file that appears in your merge diff, and ignore the known pre-existing noise elsewhere.

Smoke-launch a dev build

The release build passing does NOT mean a dev build works -- they are different build systems, and Positron code that only the dev path touches (anything in build/next/) can be broken while the release build is green. This is the most common way engineers actually run Positron, so verify it explicitly:

npm run build-stop && npm run build-start && npm run build-check

build-check must report 0 errors from every daemon, including watch-client-transpile. Do not accept a "Finished transpilation with N errors" line: build-check only prints the summary count, not the underlying error. To see what actually failed, attach to the offending daemon directly, e.g. npx deemon --attach -- npm run watch-client-transpile. A transpile error there means a file didn't emit its .js, so the workbench fails to load at runtime with ERR_FILE_NOT_FOUND / "Failed to fetch dynamically imported module" even though every gulp-based check passed.

Step 5: Test

Run the unit tests and the extension host tests. Investigate and fix any failures, and keep running until they pass.

"Unit tests" means BOTH runners, not just one. The Positron vitest suite is fast and needs no build daemons, so it's tempting to run it and assume units are covered -- but it does not execute the core Mocha .test.ts files, which is exactly where upstream's own tests collide with Positron's edits. A green vitest run is NOT evidence that units pass. You must run the core Mocha suite to green before treating unit tests as done or relying on CI:

npm run build-start && npm run build-check   # daemons must be green first
npm run test:core                            # the full core Mocha suite

Do not push the merge with the core Mocha suite unrun. If the log records units as "not run yet," they are not done -- CI will find what you skipped. The test / unit CI job runs this suite, so any red here is a red CI job.

These are the recurring ways an upstream .test.ts collides with Positron's edits. Watch for all of them, not just the first:

  • New constructor dependency. The merge adds a @IService parameter to an upstream class, and its upstream .test.ts doesn't stub the service, so it throws at construction. A variant: the test does stub it but registers the stub AFTER createInstance(...) of the class -- order matters, stub first. Recurring casualties: chatAgents.test.ts, defaultAccount.test.ts, extensionGalleryService.test.ts.
  • Positron flips an upstream default. A test assumes an upstream config default, but Positron changed it (e.g. telemetry.telemetryLevel defaults to off, not all), so the test's expected value no longer holds. Fix the test setup to establish the value it needs explicitly rather than leaning on the default.
  • PWB behavioral patch invalidates a negative assertion. PWB patches isProposedApiEnabled to always return true, so any upstream test asserting that a proposed-API check throws (checkProposedApiEnabled) can never pass. Skip such a test with a // --- Start Positron --- note explaining the patch.
  • Positron edited the production class, not the test. Positron modified an upstream class (new fields it reads, new gates it checks -- e.g. reading positronVersion or gating on update.positron.channel) but the upstream test still exercises the pre-Positron behavior. Update the test to set up the Positron inputs the production code now reads.

Extension host tests

The test / ext-host CI job runs three driver scripts in sequence, matching .github/workflows/test-ext-host.yml: scripts/test-integration-pr.sh (Positron extensions, Electron), scripts/test-remote-integration.sh (upstream API/language suites, Remote), and scripts/test-web-integration.sh (Chromium). A red job can come from any of the three, not just the first.

Read this job's failure carefully: it has a signature that looks green. Every suite can report N passing and Extension host test runner exit code: 0 while the job still ends in ##[error]Process completed with exit code 1. When that happens the failure is in the driver script, not a test:

  • set -e cleanup race. The drivers rm -rf a throwaway user-data temp dir at the end. A builtin extension (notably ms-python) can still be writing a bytecache into it during teardown, so rm fails with rm: cannot remove ... Directory not empty and set -e turns that into exit 1. That one rm: line sits just above the exit code, after the last suite's exited with code: 0. These temp-dir cleanups must be best-effort (|| true); don't chase it as a test failure.
  • Unhandled rejection at shutdown. A rejected promise logged as rejected promise not handled within 1 second can fail the process after tests pass. Note it appears benignly in many suites (e.g. copilot's GitHubLoginFailed); only treat it as the cause if it correlates with the failing process.

So when the ext-host job is red, don't stop at the mocha summary. Scan the tail of the failing suite for a non-test line (rm:, a stack trace, a crash) between the last exited with code: 0 and the final exit code.

Next, install all e2e test dependencies and run the test suite. Investigate any failures and fix them if they are caused by the merge.

Step 6: Check the test tag map

The pr-tags CI job fails if the merge touches a Positron-owned source dir that has no entry in .github/workflows/test-tag-paths-map.json. A merge often pulls Positron edits into upstream dirs that aren't mapped yet (e.g. a change under src/vs/platform/policy/), so check this before pushing. Reproduce the exact CI check locally:

source scripts/lib/pr-tags-lib.sh
find_unmapped_positron_dirs "$(git diff --name-only origin/main...HEAD)" \
  .github/workflows/test-tag-paths-map.json

On a large merge this takes a minute or two (it reads each changed file's copyright header). Add every dir it prints to the map: a feature tag list like ["@:console"] if that dir has e2e coverage, or [] if it doesn't. For an upstream dir where only a Posit-owned file or two live, map the dir to [] and, if a Posit-owned file has coverage, add a longer per-file key for it.

Note this local check is stricter than CI: the pr-tags job only sees the first 3000 changed files (GitHub's API cap), so on a big merge it can miss dirs this command catches. Map them anyway.

Step 7: Document

Summarize all your findings and any design decisions you made during the merge at the end of the log file. Include any manual steps engineers will need to take when pulling down the merged code.

Step 8: Final Tests

Prompt the user to commit the change (a prerequisite to running the CI lab tests). Tell them you're done with the merge and preliminary tests are passing, and you need a commit to run the next phase.

After verifying that the user has commited the changes, run all the tests in the Docker CI lab environment. Again, investigate failures and fix them. Think about whether each failure is a real product bug or needs test code/expectations updated.

Note all test failures and their resolution in the log file, especially if the test needed to be updated to match new or changed behavior.

References

See these references for more information:

  • change-markers.md: how to interpret, create and use change markers (Start Positron, Start PWB)
  • package-json.md: how to handle conflicts in package.json and package-lock.json files
  • codicons.md: how to handle conflicts in codicon.ttf and codiconLibrary.ts files
Repository
posit-dev/positron
Last updated
First committed

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.