Skip to content

Launch skill: attach the repo's automation library to a running instance - #332487

Draft
Joaquín Ruales (jruales) wants to merge 62 commits into
mainfrom
joruales/launch-skill-automation-attach
Draft

Joaquín Ruales (jruales) wants to merge 62 commits into
mainfrom
joruales/launch-skill-automation-attach

Conversation

@jruales

@jruales Joaquín Ruales (jruales) commented Aug 25, 2026 •

Copy link
Copy Markdown
Contributor

Why

The launch skill drove Code OSS exclusively through raw @playwright/cli, so every agent hand-writes selectors for surfaces that test/automation already models.

That library is what the smoke tests use, and it encodes the knowledge these surfaces actually need. For example, Chat.selectModel already documents and retries the exact failure mode where a row click is "absorbed by the action-widget's animating context-view-pointerBlock overlay" — a failure I spent a long time rediscovering by hand.

What

test/automation normally spawns Electron via playwright._electron.launch(). But PlaywrightDriver only uses that object for windows() / close() and already branches on 'windows' in application, so a Browser from chromium.connectOverCDP() works in its place — letting the page objects attach to the instance launch.sh already started.

  • scripts/attach.ts — attach(cdpPort, { window }) returns { workbench, code, page, browser, detach }
  • automation-library.md — the companion doc: window choice, controls with no page object, the integrated browser, and the gotchas
  • SKILL.md — a "Drive the UI" section pointing at both

Why a module rather than a documented snippet

Three steps are invisible from the type definitions, and each fails in a way that does not point at its own cause:

  1. test/automation/out is CommonJS, so import { Code } throws SyntaxError: Named export not found
  2. Quality is a const enum, erased at compile time, so it has no runtime value; this surfaces later as Cannot read properties of undefined
  3. window.driver only exists with --enable-smoke-test-driver; without it, page objects hang or throw obscure errors

Each is checked up front and reported with the fix. This matches the precedent set by monaco-paste.sh in the same skill.

Validation

Verified live against a real instance:

Check Result
Chat round-trip (authenticated, real response) PONG in 4.6s
workbench.extensions.searchForExtension returned publisher + install count in one call
Session-target picker to Local switched, telemetry confirmed explicitOverride
agentsWindow.isSessionTypeAvailable correctly reports Local is not offered there
Missing --enable-smoke-test-driver / unbuilt out/ names the exact command to run
Bad port / missing port / bad window kind / missing window all actionable messages

What the user studies changed

I ran fresh agents against the docs and measured. The headline result was that the companion doc alone did not work: in the first two studies no agent ever opened it, and one spent 7m40s and 40 raw CLI calls hand-rolling DOM queries for a task workbench.extensions.searchForExtension() answers in one line.

Two follow-up runs isolated why, and each finding became a doc change:

Run Time Raw CLI calls What it exposed
Baseline 7m40s 40 Never opened the companion doc → list the page objects in SKILL.md itself
+ inventory 3m42s 20 Reached for new Application(...) under ts-node (the spawn API) → name attach() as the entry point
+ entry point 4m33s 7 Launched without --enable-smoke-test-driver → show the flag at launch time, where it must be passed

Dogfooding the condensed docs also caught two things worth having: Set Session Target is gated on chatSessionIsEmpty so it vanishes once a session has a turn, and searchForExtension takes an extension id — passing a display name fails with Extension ... is not found, which reads like the surface is unsupported.

automation-library.md was condensed from 267 to ~210 lines in the process, keeping the gotchas that cost multiple runs to discover and cutting the ones that were self-inflicted or instantly obvious.

Launcher changes that came out of review

Copilot reviewed this a number of times, and the launcher-side findings turned out to be the substantive ones. Each was reproduced before being changed.

The editor.editContext problem. The page objects pick .native-edit-context vs textarea from Code.editContextEnabled, which is unconditionally true for a dev build. A cloned profile that had set editor.editContext: false rendered a textarea while every text-input helper waited on the wrong selector and timed out 20s later, pointing at nothing. Both launchers now normalize that key alongside files.simpleDialog.enable.

Scope turned out to be the hard part, in three layers:

  • editor.* settings are LANGUAGE_OVERRIDABLE, so a "[typescript]" block outranks the root value — nested overrides are rewritten too, but only in real override blocks (OVERRIDE_PROPERTY_REGEX), not in unrelated nested data.
  • Named profiles read User/profiles/<id>/settings.json, and the clone preserves the workspace→profile associations that resolveProfileForBrowserWindow acts on. Every existing profile settings file is normalized; only profiles whose stored metadata sets useDefaultFlags.settings genuinely inherit, and only those are left alone. A symlinked User/profiles or profile directory aborts the launch, since rsync -a preserves it and the clone would no longer be self-contained.
  • Workspace and folder settings merge after user configuration, so no amount of profile rewriting can guarantee the effective value. attach() detects that case during preflight — after whenWorkbenchRestored(), once there are editors to inspect — and says where to look instead of letting the first helper time out.

The settings merge itself. It has to be data-preserving (comments and user values survive), which rules out reparse-and-rewrite. It now lives in one scripts/normalize-automation-settings.ts used by both launchers so the platforms cannot drift, and it fails closed: the file is parsed the way VS Code parses it before anything is written, so a malformed settings.json is never clobbered. Along the way review caught symlinked settings files being written through (rsync -a preserves links, so this escaped the throwaway profile), "\u005btypescript\u005d" not being recognized as an override, and --sync=off being defeated by a forwarded --sync=on because VS Code keeps the last value of a string option.

Parsing it the way VS Code does. Fail-closed validation only helps if it agrees with the real parser, and several rounds of review were spent closing that gap: VS Code's JSONC scanner ends a line comment at a bare \r and at U+2028/U+2029, accepts the full Unicode 3.0 space set as whitespace, and treats an unterminated block comment as an error rather than as trivia running to EOF. Each mismatch either rejected a profile VS Code reads happily (aborting the launch) or accepted one it rejects (rewriting a broken file).

scripts/normalize-automation-settings.test.ts covers this with 68 node --test cases. Every guard was mutation-checked: removing it fails at least one test.

Review also hardened the surrounding recipes — the cleanup snippets in SKILL.md now verify that the directory they are about to rm -rf really is a launcher run directory under the launcher's own temp root, and a PowerShell equivalent was added for Windows (where $pid is read-only and -like treats a temp path containing [1] as a wildcard pattern).

Copilot AI balanced review requested due to automatic review settings August 25, 2026 06:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds support for attaching VS Code’s smoke-test automation page objects to an existing Code OSS instance over CDP.

Changes:

  • Adds the attach() automation helper.
  • Documents automation-library usage and raw Playwright interoperability.
  • Updates the launch skill to recommend repository page objects.
Show a summary per file
File Description
.agents/skills/launch/SKILL.md Introduces the automation-library workflow.
.agents/skills/launch/scripts/attach.ts Implements CDP attachment and session creation.
.agents/skills/launch/automation-library.md Documents setup, APIs, and usage guidance.

Review details

💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

  • Files reviewed: 3/3 changed files
  • Comments generated: 2
  • Review effort level: Balanced

Comment thread .agents/skills/launch/scripts/attach.ts
Comment thread .agents/skills/launch/scripts/attach.ts Outdated
@jruales

Copy link
Copy Markdown
Contributor Author

Copilot review

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review details

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

.agents/skills/launch/scripts/attach.ts:57

  • These aliases make every object returned by attach() an any, so callers lose type checking and completion for the exact page-object API this wrapper is meant to expose. Runtime require does not prevent using type-only import(...) aliases: Node erases them while the editor retains the Page, Browser, BrowserContext, Code, and Workbench contracts.
type PlaywrightPage = any;
type PlaywrightBrowser = any;
type PlaywrightContext = any;
type AutomationCode = any;
type AutomationWorkbench = any;
  • Files reviewed: 3/3 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread .agents/skills/launch/scripts/attach.ts Outdated
@jruales

Copy link
Copy Markdown
Contributor Author

Copilot review

Copilot AI commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Copilot review

Re-reviewed attach.ts against current test/automation sources — Code/PlaywrightDriver constructor signatures and the Quality const enum values still match, and the three previously flagged issues (unpollable driver check, Code.exit() rejecting obscurely, untyped globalThis cast) remain fixed as of 70a3a92/d1463e3. No further changes needed.

@jruales

Copy link
Copy Markdown
Contributor Author

Agent user-study results

Seven subagent runs (models: claude-haiku-4.5, gpt-5-mini) were given real tasks against a launched Code OSS and observed without intervention. Docs were iterated between runs.

# Model Task Outcome Time
1 haiku Chat round-trip ✅ used attach.ts ~2m
2 gpt-5-mini Chat round-trip ✅ (wrongly concluded --clone-extensions was required — verified false, providers register async; now documented) ~3m
3 haiku Chat round-trip ⚠️ never opened automation-library.md, fell back to raw CLI 4m49s
3-rerun haiku same ✅ used attach.ts after SKILL.md pointer gained a code sample 1m38s
4 gpt-5-mini Harness picker ❌ never found the Set Session Target control —
6 gpt-5-mini Harness picker ❌ same —
7 gpt-5-mini Harness picker ⚠️ used attach.ts, but launched --agents for a workbench task 12m50s

Changes driven by the studies

  1. SKILL.md pointer → code sample. Study 3 skipped the companion doc entirely. Leading with a concrete sample cut the same task from 4m49s → 1m38s.
  2. Control-discovery section (studies 4/6) — how to find a control with no page object.
  3. Window-choice rule (study 7) — “Agent”/“harness” appear in both products, so name-matching the task picks the wrong window. Anything involving Local is a regular-workbench task; the Agents window has no Local session type, so the failure surfaces as Available: and reads like a broken selector.
  4. Cleanup verification. All 7 runs reported successful cleanup; 9 instances (13.7 GB RSS) and 341 MB of temp profiles were still alive. Killing the code.sh pid does not reliably reap the Electron process group. Added a runDir-scoped pgrep verification step + machine-wide audit — scoped by runDir so concurrent agents are unaffected.

Note on CI

The Compile & Hygiene failure is not from this PR — it is pre-existing on base eff5556afcf (electron@42.9.3 dropped the dep “only needed for types”, so electron-main/ files hit TS7006). PRs branched before that commit pass; after, they fail. This PR touches only .agents/skills/launch/** (docs + one standalone script).

@jruales
Joaquín Ruales (jruales) force-pushed the joruales/launch-skill-automation-attach branch from f6dce94 to d6fcd48 Compare August 25, 2026 06:55
@jruales

Copy link
Copy Markdown
Contributor Author

Update on the Compile & Hygiene failure: it is not from this PR, and #332489 is the fix.

Root cause. #331833 (chore: bump electron@42.9.3) removed the electron devDependency and replaced it with a postinstall download of electron.d.ts (build/npm/electronTypes.ts). CI restores node_modules from a cache, and on a cache hit npm install — and therefore that download — is skipped, so electron.d.ts is absent and every electron-main/ file fails with TS2307: Cannot find module 'electron'.

That explains why the failure looked arbitrary rather than universal: PRs landing on a fresh cache pass, PRs landing on a stale cache fail. Rebasing onto latest main did not help, because the cache key was unchanged.

#332489 fixes exactly this by calling ensureElectronTypes() before the cache archive is built, adding .build/typings/electron.d.ts to it, and hashing listNodeModules.ts into the cache key so existing stale caches are invalidated.

Evidence this PR is not implicated:

  • Every reported error is in one unrelated file, src/vs/platform/browserView/electron-main/browserSessionPermissions.ts; zero errors reference files from this PR.
  • This PR changes only .agents/skills/launch/{SKILL.md,automation-library.md,scripts/attach.ts} — no source files, and no overlap with build: include electron types in node modules cache #332489.
  • Running the download locally (node build/npm/electronTypes.ts) produces electron.d.ts matching the expected checksum 63bf27ed…, after which tsc --noEmit -p src/tsconfig.json reports 0 errors.

No action needed here; this should go green once #332489 merges and the cache is rebuilt.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review details

  • Files reviewed: 3/3 changed files
  • Comments generated: 4
  • Review effort level: Balanced

Comment thread .agents/skills/launch/scripts/attach.ts Outdated
Comment thread .agents/skills/launch/scripts/attach.ts
Comment thread .agents/skills/launch/SKILL.md Outdated
Comment thread .agents/skills/launch/automation-library.md Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review details

  • Files reviewed: 3/3 changed files
  • Comments generated: 3
  • Review effort level: Balanced

Comment thread .agents/skills/launch/scripts/attach.ts Outdated
Comment thread .agents/skills/launch/SKILL.md
Comment thread .agents/skills/launch/SKILL.md Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review details

Suppressed comments (1)

Previously missed (1) — in code that hasn't changed since the last review.

.agents/skills/launch/SKILL.md:158

  • The PR description says SKILL.md is 85 lines shorter, but these hunks make it 94 lines longer (+46 lines in the UI-driving hunk and +48 in cleanup). Please update that scope/size claim so it matches the submitted diff.
## Drive the UI
  • Files reviewed: 3/3 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment thread .agents/skills/launch/SKILL.md Outdated
…d tests

Three findings, all reproduced first:

editor.* settings are LANGUAGE_OVERRIDABLE, so normalizing only the root
key does not guarantee the effective value. Verified live: a profile with
"[typescript]": { "editor.editContext": false } rendered a TypeScript
editor as {nec:0, ta:1} - a textarea - while the page objects wait on
.native-edit-context. The scanner now reports occurrences at any depth and
rewrites nested overrides too; the same profile now renders {nec:1, ta:0}.

Neither launcher passed --sync=off. Sync enablement lives in
StorageScope.APPLICATION, which the clone preserves, so a source profile
with sync on could upload these automation-only values to the user's real
settings. Both launchers now force it off.

The JSONC rewriter had no checked-in coverage. Added a node --test suite
of 30 valid and 5 malformed fixtures covering comments, duplicate and
effective keys, nested overrides, escaped strings, exponents, trailing
commas and malformed roots. Each result is re-parsed to assert both keys
resolve to true and that no override at any depth contradicts them;
malformed input must exit non-zero and leave the file byte-identical.
Mutation-checked: disabling nested rewriting fails 6 of the 35.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…follow symlinks

Four findings, each reproduced first:

R20's nested rewrite was too broad. VS Code only treats direct children of
a top-level [language] key as overrides (OVERRIDE_PROPERTY_REGEX in
ConfigurationModelParser.toOverrides), so "some.extension": { ... } is
ordinary data owned by another consumer - and it was being rewritten.
Nested rewrites are now limited to direct properties of a top-level
override block; verified that [typescript] is normalized while
some.extension is left untouched.

The structural check ran only when a key had to be appended, so a
malformed file that already contained both keys took the rewrite path
twice and was written back out. It now runs once before any rewrite.

findRootObject counted depth without tracking delimiter types, so
parsed as a balanced root object and was rewritten. It now matches {} and
[] and rejects a mismatch.

rsync -a preserves symlinks, so a settings.json linked to a dotfiles
checkout was written through to the user's real file - reproduced, the
real file was edited. The link is now materialized as a regular file
first, keeping the throwaway profile actually throwaway.

Tests grow to 43 (from 35) covering all four, and each new guard is
mutation-checked: removing any one of them fails at least one test.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…overrides

Six findings, each reproduced first:

Forwarded args were appended after --sync=off, and VS Code keeps the last
value for string options, so `-- --sync=on` defeated the guarantee. Both
launchers now append the enforced value last.

Override keys were compared in their raw source spelling. VS Code parses
"\u005btypescript\u005d" as [typescript], so that override was not
recognized and stayed false. Keys are now decoded before comparison.

findRootObject took the first opening brace without checking the prefix,
so leading junk was rewritten and reported as success. Everything before
the root object (BOM aside) must now be trivia.

A dangling symlink took the ENOENT path with the link still in place, so
the write created its target outside the throwaway profile. A dangling
link is now replaced too.

editor.editContext is also valid at workspace and folder scope, and those
models merge after user configuration, so the profile rewrite cannot
guarantee the effective value. Reproduced: a workspace setting it to false
made a page object time out after 20s on .native-edit-context. attach()
now detects the textarea/native-edit-context mismatch during preflight and
explains where to look, and the healthy path is unaffected.

The 'missing file' fixture wrote an empty file, so the ENOENT branch was
never exercised. The helper now distinguishes an absent file from an empty
one, and both are covered.

Tests grow to 49; removing any new guard fails at least one of them.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… check

Three findings, both reproduced before changing anything.

The launchers normalized only the default profile. The clone preserves
userDataProfiles and profileAssociations in application state, and
resolveProfileForBrowserWindow hands an associated workspace its named
profile, which reads User/profiles/<id>/settings.json. Reproduced by
seeding a profile associated with the launched workspace: the cloned
settings kept editor.editContext false and all three editors rendered a
textarea. Both launchers now normalize every existing profile settings
file. Files that do not exist are left absent on purpose - a profile
inheriting settings via useDefaultFlags points back at the default
resource, so creating one would invent an override.

The preflight treated any textarea-backed editor as evidence of a
disabled setting, but inlineEditsSideBySideView and
longDistancePreviewEditor pass editContext: false themselves for speed,
so a visible inline-edit preview would have failed a healthy window.
Those opt-outs are always a subset while a disabling setting applies to
every editor, so the check now requires all of them to be textarea-backed.
The fixed run above shows why this matters: one editor of three used
native-edit-context and attach() correctly succeeded.

Tests grow to 51; dropping the profile discovery fails the new one.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…restore

Two findings, both reproduced first.

Validation checked delimiter balance but not syntax, so balanced-yet-invalid
input passed. Reproduced: `{ "a": 1 "b": 2 }` was rewritten and reported
success, exactly the fail-closed contract this script claims to keep. The
file is now parsed the way VS Code parses it (strip comments, then tolerate
trailing commas, per src/vs/base/common/jsonc.ts) before anything is written,
with fixtures for a missing comma, a missing colon, an invalid escape and an
unquoted key. A leading BOM is still accepted, since JSON.parse rejects it
but VS Code does not.

The editContext preflight ran right after the smoke-test driver appeared,
which is before the workbench restores, so it usually inspected zero editors
and proved nothing. It now runs after whenWorkbenchRestored().

That move exposed the check itself being wrong: requiring every editor to be
textarea-backed never fired, because a restored window had four .monaco-editor
elements of which only two own an input. The signal that actually separates
the two cases is whether anything still uses EditContext - the editors that
opt out for speed are always a subset, while a disabling setting leaves none.
Verified live both ways: a workspace setting it to false now fails with the
actionable message, and the same workspace without it attaches with two of
four editors on native-edit-context.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… preservation

Six findings.

Profile discovery was written twice in shell and a third time inside the
test, so the test could stay green while either launcher regressed - and it
needed bash, which a Windows user running the documented `node --test`
command does not have. Discovery now lives in the shared script behind
`--user-data-dir`, both launchers call that, and the test drives the same
entry point.

The symlink guard only inspected the final settings.json component, but
`rsync -a` preserves linked directories too: a symlinked `User` or
`User/profiles/<id>` arrived here as an ordinary file path that still
resolved outside the throwaway profile. Reproduced, and writing there
edited the real file. Linked ancestors are now rejected. The walk is
bounded by the profile root because on macOS /tmp is itself a symlink,
which an unbounded walk would reject on every run.

The fixture loop asserted only the two injected keys, so an implementation
that discarded the file and wrote just those keys would have passed - the
one thing a data-preserving merge must never do. Fixtures now also assert
that every unrelated value and every real comment survives.

The two symlink tests are skipped on Windows, where creating file symlinks
needs elevation, matching what the repository's watcher tests do.

`logsPath` defaulted to a POSIX path even though attach() is documented for
launch.ps1; it now derives from the platform temp dir.

Tests grow to 55; the new directory guard and the discovery are both
mutation-checked. Verified live: launch plus a chat round-trip still works.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…iscovery

Three findings.

The cleanup guard in SKILL.md checked RUN_DIR with a shell glob, but a glob
matches `/` too, so `/tmp/code-oss-dev-x/../../etc` passed the pattern and
would have reached `rm -rf`. The path is canonicalized first and only a
resolved `code-oss-dev-*` basename is accepted; the traversal above now
resolves to /private/etc and is rejected.

The editContext preflight required *no* editor in the window to use
EditContext, which only caught a window-wide disable. The setting is
LANGUAGE_OVERRIDABLE and valid per folder, so a `[typescript]` override can
leave one file's editor textarea-backed while Chat stays native-backed - and
that file's page object is exactly the one that hangs. The known opt-outs
(the inline-edit previews, which pass editContext: false for speed) all live
under an inline-edits view, so they are excluded by ancestor and any other
textarea-backed editor is reported. Verified live both ways: the folder
override now fails with the actionable message, and the same workspace
without it attaches.

Profile discovery used existsSync, which follows links, so a *dangling*
settings symlink looked absent and was skipped as an inheriting profile -
leaving the link in place for VS Code to write through later. Reproduced:
one file discovered instead of two, link untouched. lstatSync is used
instead, and the materialization already in place handles the rest.

SKILL.md said attach() detects workspace overrides up front, which overstates
a check that can only inspect editors the window actually restored. It now
says what the check does and what to look at if a helper still times out.

Tests grow to 56; the new discovery case is mutation-checked.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…eanup

Four findings.

A bare carriage return ends a line comment for VS Code's scanner, but the
comment mask only reset on a newline, so a valid CR-only settings.json had
everything after its first `//` blanked and was rejected as malformed.
Reproduced, fixed, and covered by a fixture. The test's own JSONC parser had
the same bug.

The symlink materialization treated every read failure as a dangling link,
so an unreadable target (EACCES, EIO, a directory) meant the link was
unlinked, replaced with an empty file, and rewritten with only the automation
keys - silently discarding that profile's settings. Only ENOENT proves a
link is dangling; anything else now propagates to the fail-closed path.
Verified: a directory target aborts with exit 1 and is left alone.

The preservation assertion stripped the automation keys recursively from
both sides, which also hid corruption of a *deeply* nested occurrence - the
very thing it was added to catch. Stripping is now limited to direct children
of a recognized top-level override block. Mutation-checked: widening the
normalizer to rewrite any nested occurrence now fails three tests, where
before it failed none.

Cleanup was documented only as Bash, using jq, pgrep, sed and xargs, in a
skill that otherwise supports launch.ps1 and states Windows needs neither
Bash nor jq - so Windows users could not run the survivor check that section
exists to enforce. Added a PowerShell equivalent with the same run-dir
canonicalization, verified to accept a real run directory and reject both a
traversal and a non-run path. Two bugs surfaced writing it: `$pid` is
read-only in PowerShell, so the existing `$pid = $info.pid` line silently
left the shell's own PID in place, and `?.` requires PowerShell 7 while this
skill supports 5.1. Both fixed.

Tests grow to 58.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…cipe

Three findings, two of them bugs in the PowerShell recipe added last round.

Discovery treated an absent named-profile settings.json as "this profile
inherits". It does not: createProfile makes only the directory, so an
independent profile that has nothing saved yet was skipped, and an associated
workspace then opened with the default editor.editContext and
files.simpleDialog.enable - the exact failure this script prevents. Only
useDefaultFlags.settings redirects settingsResource to the default profile,
so the cloned userDataProfiles metadata now decides, and unreadable or absent
metadata skips nothing, which is the safe direction. The fixture models real
metadata and covers all three cases.

The Windows cleanup recipe validated the run directory by its leaf name, but
launch.ps1 names it <temp>\\code-oss-dev\\<timestamp-pid>, so it rejected every
genuine Windows launch and left the profile behind. It now requires a direct
child of the launcher's base directory; verified that a real run is accepted
while a traversal and an unrelated path are rejected.

The survivor query used -like, which is a wildcard match: a temp path
containing [ or ] is read as a character class. Verified that such a path
fails -like but matches a literal comparison, so the query could have deleted
a live instance's profile or killed unrelated processes. It now compares
case-insensitively as a literal substring.

Tests stay at 58; both directions of the discovery change are mutation-checked,
and a launch plus chat round-trip still works.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
VS Code's JSONC scanner treats U+2028/U+2029 as line terminators and the full
Unicode 3.0 space set as whitespace. codeMask only ended line comments at CR/LF
and left the extra trivia in place, so JSON.parse rejected profiles that VS Code
reads happily and every launch aborted. Fold scanner-only trivia to a plain
space (1:1, offsets preserved) and end line comments at any scanner line break.

attach() defaulted logsPath to a fixed shared directory, but PlaywrightDriver
names traces and screenshots from per-process counters that both start at 1, so
concurrent agents overwrote each other's artifacts. Use mkdtempSync instead.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
codeMask blanked an unterminated block comment through EOF, so the remaining
text parsed cleanly and a broken settings.json was rewritten despite the
fail-closed contract. Reject unterminated comments and strings from the scanner
state instead.

The cleanup recipe's basename check let a real directory such as
~/code-oss-dev-important reach `rm -rf`. Also require the canonical parent to be
the launcher's temp root ($TMPDIR, or /tmp when launch.sh fell back to it).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reject a symlinked User/profiles or profile child before the inheritance
skip. An empty or dangling link yields no entries to walk, and a linked
child marked as inheriting is skipped, so in both cases the clone kept a
directory resolving outside the throwaway profile. Non-directory entries
are now ignored rather than turned into a settings.json path.

Broaden the preservation assertion to collect block comments as well as
line comments; dropping /* */ was previously invisible to the suite.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add a find-widget recipe to automation-library.md: the visible-widget
selector, the fact that fill works because the find input is a plain
textarea, aria-label prefixes for the option toggles, and the need to read
aria-checked before clicking one. Verified live against a running instance.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Dogfooding showed keyboard.type lands text in both a Monaco editor and the
chat input, contradicting the blanket "fill and type silently fail" claim.
Verified against a live instance: fill times out on native-edit-context,
while keyboard.type and locator.type dispatch real key events and work.

Also record that focusing chat by clicking the input times out - use the
command instead, since a failed focus makes typing look broken when it isn't.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remap legacy URI profile locations into the cloned user-data directory,
preserve comment-only JSONC settings, reject workspace simple-dialog
overrides, and keep run directories when process enumeration fails.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Recognize the nested builtin/agents profile without treating its namespace
as a profile, and inspect code-workspace files opened through --file-uri.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Forwarded profile flags create profiles after automation settings have been
normalized, so fail before launch instead of allowing native dialogs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Materialize a linked storage.json in the clone and reject linked ancestors
before rewriting legacy profile URIs, preserving the source profile.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Set Match Case only when needed and require the launcher timestamp leaf
before recursively deleting a Windows run directory.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reject forwarded options that can override isolation, preserve inert window
settings in language blocks, and reference the actual VS Code JSON parser.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reject inspect-brk variants, remove every test fixture in an after hook,
and avoid double-counting profiles when TMPDIR resolves to /tmp.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Recognize native CLI option values, force no-path launches into an empty
window, and retain launcher isolation while accepting valid forwarded args.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Accept trivia-only workspace settings, reject transient profiles, and avoid
letting a missing string-option value swallow the next protected option.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Match minimist value-token behavior for URI and ordinary string options,
and reject a second delimiter that would hide appended safety flags.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Classify positional arguments after parsing so code-workspace files opened as
diff or merge inputs are not mistaken for active workspace configuration.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Mirror VS Code value-tree ordering for workspace checks and rewrite nested
editor settings at root and language-override scope without touching unrelated data.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove owned attach log directories, preserve caller paths, and cover the
deprecated debugger plus short diff and merge option spellings.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

This branch has not been deployed

No deployments
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.

3 participants