Skip to content

[WIP] Feature "DocumentDB Local"#785

Draft
tnaum-ms wants to merge 55 commits into
mainfrom
dev/feature/local-quickstart
Draft

[WIP] Feature "DocumentDB Local"#785
tnaum-ms wants to merge 55 commits into
mainfrom
dev/feature/local-quickstart

Conversation

@tnaum-ms

@tnaum-ms tnaum-ms commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

No description provided.

guanzhousongmicrosoft and others added 30 commits May 18, 2026 22:55
Signed-off-by: Guanzhou Song <guanzhousong@microsoft.com>

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Guanzhou Song <guanzhousong@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Guanzhou Song <guanzhousong@microsoft.com>
- Add local-quickstart-v2.md: simplified design that removes the dedicated
  local connection subtree, uses a webview for container creation, gates
  TLS exceptions in the regular connection wizard, and migrates legacy
  emulator connections on first launch.
- Mark local-quickstart.md as iteration 1 with a forward reference.
…decision note

- terminal-first create progress for v1.0 (staged in-webview card deferred to v1.1)
- adopt @microsoft/vscode-container-client (Docker+Podman) as the runtime layer
- port fallback only for the default port; read the bound port from docker inspect
- distinguish create-vs-start failure; pre-check connection name AND container name
- scope prereq checks v1.0/v1.1; add section 18.1 PostgreSQL source-benchmark learnings
- add docs/ai-and-plans/PRs/653-local-quickstart-design/description.md decision note

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

Adds a demo-focused proof-of-concept plan for Local Quick Start under

docs/ai-and-plans/PRs/local-quickstart-poc/:

- description.md: POC goal, scope (focus vs leave-out), deliberate deviations, reuse strategy, real-world image findings

- poc-implementation-plan.md: process contract, architecture map, 14 decisions, 9 work items (Core WI-0..6 + Stretch WI-7..8), risks, demo script

- review-and-resolutions.md: two rounds of 5-agent review (design/manager/impl/scope/risk) with every finding and resolution; consensus reached at rev. 3

No shipping code; planning only.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add @microsoft/vscode-container-client dependency (validates OPEN-3: post-image args supported via runContainer command[])

- ContainerRuntime: isDockerReady/isPortFree/pullImage/createAndRunContainer/inspect/start/stop/remove/listByLabel/followLogs

- Masked, line-buffered OutputChannel writes so the generated password never leaks (D14), incl. split-chunk safety

- quickStartTypes: image/port/label constants, InstanceState, StageEvent, InstanceMetadata

- Unit tests for masking (8 passing)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Standalone service (documented deviation from D13 Task-composition: Task is single-use + numeric-notification progress, ill-fitting the in-webview stage checklist + Retry; D13 permits standalone)

- provision() async generator yields StageEvents (checking/pulling/creating/starting/waiting/done); per-attempt AbortSignal; cleanup in finally (pull-cancel removes nothing; create/start-cancel removes by id, D12)

- wire-protocol readiness probe via connectToClient (TLS-allow-invalid), 180s + backoff (D7)

- credentials: URL-safe alphabet, percent-encoded conn string (D6); stored in SecretStorage

- activation reconcile(): adopt labelled container if creds stored, else clean-remove

- unit tests for credentials/conn-string (16 passing total)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- localQuickStartController (extends WebviewControllerBase; closePanel via this.panel.dispose, not this.dispose)

- localQuickStartRouter: getDockerStatus/getStatus queries, startQuickStart subscription (mirrors ctx.signal to cancel provisioning + docker), closePanel mutation; imports tRPC primitives from ./trpc

- mounted localQuickStart in appRouter; registered LocalQuickStart in WebviewRegistry (auto-bundled)

- openLocalQuickStart command + registration; attach QuickStartService (reconcile + dispose) at activation

- minimal React entry calling getDockerStatus (full UI in WI-3/WI-4)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Review: 4 metric cards (Docker/Port/Data=Ephemeral(POC)/Security) + What-we-do summary + Start

- Docker-not-ready variant with Retry (opt-in; never auto-starts Docker)

- Progress: lightweight staged checklist (D3) driven by startQuickStart subscription + elapsed timer + spinner; Cancel unsubscribes

- Failure: inline error + Retry; View Docker output reveals the channel

- Success: brief card then auto-close via closePanel (panel.dispose) -> tree handoff

- added showOutput mutation; moved DockerStatusResult to pure types module (no router runtime in webview bundle)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…-5, WI-6)

- LocalQuickStartItem root node: rocket empty-state row (opens webview) when NotInstalled; inline DocumentDBClusterItem when Running/Stopped (expand to browse); Provisioning/Error rows

- QuickStartClusterItem subclass stamps a static 'Running . localhost:port' description (D2 design review)

- QuickStartService pre-populates CredentialCache (setAuthCredentials) on success + reconcile so the inline item browses without a re-prompt

- ConnectionsBranchDataProvider: render the node UNCONDITIONALLY incl. the zero-connections empty state (mandated option A); prepend in normal path; fix savedConnections telemetry count

- refresh the tree on QuickStartService status change

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- ClustersExtension: refresh Connections tree on QuickStartService status change (WI-6 wiring)

- prettier formatting; eslint-disable for initial-load setState-in-effect (matches DocumentView)

- record standalone-service deviation from D13 + -dt/customOptions note in the plan

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Functional/security/tree/webview/design review fixes:

- D14: followLogs now line-buffered via MaskingLineBuffer (split-secret safety)

- stop followLogs on success: cts.cancel() in finally (was leaking docker logs -f)

- orphan sweep if cancelled during create window (createAttempted + label sweep)

- clear stale ClustersClient on fresh provision so re-run browses with new creds

- webview: emit terminal error when busy + onComplete handler (no stuck 'provisioning'); unsubscribe-before-resubscribe + null-on-terminal (no double-click leak); review-screen Cancel button

- readiness probe: direct MongoClient w/ 3s serverSelectionTimeout (Cancel responsive); remove redundant -t (detached already adds --tty)

- optional sample-doc seed so the tree isn't empty to browse

- Learn more... row; savedConnections telemetry counts real connections/folders

Gates: lint, jest 2055, build, webview bundle all pass.

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

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

Root cause: ShellStreamCommandRunnerFactory without a shellProvider drops each arg's quoting metadata and sets windowsVerbatimArguments on Windows, splitting Go-template args like '--format {{json .}}' on the space. This made 'docker info' (and inspect/list) fail, so isDockerReady reported the daemon unreachable even when Docker was running.

Fix: provide a platform shell provider (Cmd on Windows, Bash elsewhere) to every runner; switch makeRunner to strict:false (non-zero exit still rejects; harmless stderr warnings don't). Add @microsoft/vscode-processutils dependency.

Verified live on Windows: info, run (with credential args + labels), inspect (bound port), listByLabel, remove all succeed.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…s shell-provider)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adopt the image's native sample-data mechanism (documentdb-local --init-data true / INIT_DATA=true) instead of a bespoke driver-side seed — matches design 8.4 (use the image's standard init-script convention; portable, identical to running the image by hand).

- pass '--init-data true' as post-image args alongside --username/--password

- loads the rich sampledb (users/products/orders/analytics) vs the prior single doc

- remove seedSampleData() driver hack

- add capped, non-fatal waitForSampleData() so the tree reliably shows data on first expand (sample data loads just after gateway readiness)

Verified live on Windows: --init-data true populates sampledb.

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

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

Turns the provision-and-browse POC into a managed instance (design §6.2/§11).

- States: add Starting/Stopping; Stopped now reachable; Missing badge (§6.1) when metadata exists but Docker has no container

- Service: start/stop/restart/deleteContainer/refreshLiveState; all destructive ops verify the quickstart label before acting (§9/§13.1); lifecycleBusy guard

- Tree: state-aware rows with a Quick-Start-specific contextValue (treeItem_quickStartInstance + state token) so the instance shows Quick Start actions, not generic cluster menus; live-refresh on expand (cheap multi-window/external-change freshness, §12)

- Commands: Start/Stop/Restart/Delete Container/Copy Connection String/Copy Password/View Logs (inline + context menu, gated by state)

- Delete uses a one-line modal confirm (§11); password masked in View Logs (D14)

Verified live on Windows: run/stop(exited)/start(running)/remove + label check. Gates: lint, jest 2055, build, webpack-prod all pass.

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

Baked '--init-data true' re-ran the image's sample-data init on every docker start, hit a duplicate-key error, and crashed the container (set -e); start() also inspected too early and falsely reported Running. Now run with only credentials and seed the built-in sampledb once after readiness via the image's native init script (docker exec), and require the container to stay running (confirmStaysRunning) before declaring Running. Verified live: stop->start stays running and sample data persists.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ased-cryptographic-random)

Use rejection sampling so every URL-safe alphabet character is equally likely; a plain byte % 62 over-represented the first 8 characters. Flagged high-severity by CodeQL on PR #756.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…tance, ownership-bounded)

Production v1 design decision for the multiple-instance / existing-container edge case raised by German Eichberger: Quick Start manages exactly one labelled instance it created; users attach their own containers via the regular wizard; multi-instance/adopt/auto-discovery deferred to v1.2 (labels keep the model forward-compatible). Identifies unlabelled name/port collision safety as the one concrete v1 hardening item. No code changes.

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

P0: persistent named volume at /data (data survives recreate; Missing->recreate reuses stored creds+volume; idempotent sample seeding; explicit Delete drops the volume). Credentials now pass via a temp --env-file (mode 600, deleted in finally) as USERNAME/PASSWORD instead of CLI args (verified the image authenticates via env). Port-conflict fallback band [10260,10360) with bound-port readback. P1: DockerReadiness gains host arch/platformSupported; Docker-not-ready view rebuilt into per-check cards + Start Docker Desktop action + Install/Troubleshooting links; review Data card now 'Persistent volume'. Verified live + gates (lint/jest 2055/build/webpack/l10n).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
P1-3: success card adds Open Connection (focuses Connections view) + Copy Connection String, with a longer auto-close so the buttons are usable. P3-2: start/stop/restart re-check live Docker state via liveStateGuard before acting and inform the user if another window already changed it (design §12). P3-1: documentDB.quickstart.provision telemetry (result/reused/portFallback/provisionMs), getDockerStatus reports dockerReadiness+platformSupported, lifecycle commands tag action; no names/ports/creds sent (§14). Gates: l10n/prettier/lint/jest 2055/build/webpack-prod.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
guanzhousongmicrosoft and others added 12 commits June 26, 2026 13:39
…e coupling)

5-agent rubber-duck review (GPT-5.4/5.5 + Opus 4.8 REJECT; Opus 4.6/4.7 missed it) found a release blocker: emulatorConfiguration.isEmulator is overloaded as both the storage-zone selector and the TLS-allow-invalid trigger, so an emulator connection cannot be moved into the Clusters zone correctly. Reverted the migration cut. Recorded the prerequisite P2-0 (decouple storage-zone from isEmulator, the §7 work) which P2-1 and P2-2 both depend on.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…lator migration (§4, §7)

P2-0: add explicit storageZone to ConnectionClusterModel + resolveStorageZone(cluster) helper (prefers the explicit zone, falls back to the old isEmulator inference). Stamp storageZone at the 3 construction sites and route every zone decision through it (DocumentDBClusterItem x3, removeConnection, moveItems, rename/updateCredentials/updateConnectionString wizards). isEmulator is kept only for behavior (TLS/timeouts/icons). +resolveStorageZone unit tests.

P2-1: one-time migration copies each Emulators-zone connection into a 'Local Connections (Legacy)' folder in the Clusters zone (creds/auth/emulatorConfiguration preserved; isEmulator normalized so TLS still works; routed correctly via the new storageZone). Keeps the Emulators zone as read-only rollback; retires LocalEmulatorsItem once complete; one-time toast. create-if-missing (never overwrites user edits) + a reconciliation re-scan before the completion flag (closes the activation-window race) + isFolder guard + telemetry.

Validated by a 3-round 5-agent rubber-duck review (GPT-5.4/5.5, Opus 4.6/4.7/4.8): the original isEmulator/zone blocker is resolved, the migration is data-safe/idempotent, TLS preserved, no regression. Pre-existing folder-aware deep-link reveal limitation documented as a follow-up. Gates: build/lint/jest 2058/l10n/prettier.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ort 10260 (§13.5)

The manual local-connection wizard defaulted the DocumentDB emulator to 10255; the canonical DocumentDB local port is 10260 (matches Quick Start + the image). The Cosmos DB Mongo (RU) emulator legitimately keeps 10255. PromptPortStep's default/placeholder is now experience-aware.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…wizard (§7)

Replaces the emulator-only TLS handling with a gated TLS-exception step in the
regular new-connection wizard, per design §7.

- Decouple TLS-allow-invalid from `isEmulator`: it now keys off
  `emulatorConfiguration.disableEmulatorSecurity` alone, so a regular local
  connection can accept a self-signed cert without being treated as an emulator
  (all 5 MongoClient option builders).
- Gated wizard step (`PromptTlsExceptionStep`): offered only when every seed
  host is local/private; defaults to "Enable TLS".
- Host classifier (`hostClassification.ts`): loopback/RFC1918/link-local/ULA/
  `.local`/single-word, IDNA-normalized so a Unicode-dot homograph
  (`example。com`) cannot masquerade as a local host.
- Write-time canonicalizer (`tlsException.ts`) is the security boundary: it
  strips every TLS-bypass URL param (cert/hostname/tlsInsecure/rejectUnauthorized)
  from the stored connection string and host-gates the exception, so the wizard,
  deep links and connection-string edits can never create an allow-invalid
  exception for a public host.
- Hybrid runtime policy (`resolveAllowInvalidCertificates`): the stored flag is
  honored only for local/private hosts; on a public host a bare orphaned flag is
  inert (no allow-invalid, fail-fast timeout, local error copy, or tree badge),
  while an explicit URL param the user deliberately set is still honored.
- Shell `isEmulator` now derives from credentials (not the timeout proxy);
  `updateConnectionString` host-gates both flags against the edited hosts.

Verified across 8 five-agent review rounds (GPT-5.4/5.5, Opus 4.6/4.7/4.8) to
unanimous consensus. The §7.3 connection-edit dialog remains a separate issue.

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

Manual-testing UX feedback:

- Success page no longer auto-closes after provisioning. The 4s auto-dismiss is
  removed and `Open Connection` no longer closes the panel — only the new
  explicit `Close` button dismisses it. Added a `Next steps` block (reusing
  the in-view successBox/Card pattern) and wired the shared Announcer for a11y.
- `Delete Container…` is now offered while the instance is Running (menu gate
  += state_running). The confirmation is a stronger, state-aware data-loss
  warning (all data, logs and credentials are permanently lost; the running
  container is stopped first). deleteContainer() already force-removes
  (docker rm -f) and drops the data volume, so no backend change was needed.

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

Adds a collapsible Advanced panel to the Quick Start review screen for a custom
host port, credentials, image tag, and a sample-data toggle (design §5.2), with
security and data-safety hardening from a 4-round 5-agent review.

Security (credentials off the host shell, §8.2):
- Seed sample data by referencing $USERNAME/$PASSWORD from the container's OWN
  environment inside a ShellQuoting.Strong-quoted `sh -c`, so credentials never
  reach the host argv / process list on either bash or cmd. Credentials are now
  validated control-char-only (the env-file newline-injection vector stays blocked
  at zod + writeEnvFile; a `%` password round-trips safely).

Data safety:
- The reuse decision is keyed on stored credentials existing (live SecretStorage),
  not the in-memory Missing flag, so re-running setup can never silently wipe a
  reusable data volume; a fresh, volume-wiping provision is strictly the explicit
  Delete-then-recreate path.

Recreate fidelity:
- Persist the instance's image ref (metadata + a durable globalState record,
  backfilled on reconcile/adopt, cleared on Delete) so a recreate — even across a
  window reload — reuses the original image, not `latest`.
- Surface a `willReuse` flag computed from the same predicate provision() uses; the
  webview hides the credential/image inputs and relabels the summary exactly when
  the service will reuse.

Also: server-side both-or-neither credential refine (parity with the client);
whitespace trim consistent client/zod/service; custom port preserved in the success
message + connection string via a chosenPort inspect fallback; telemetry stays
booleans-only (customPort/customCreds/customImage/sampleData).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… (v1.1)

Makes the provisioning flow usable with a keyboard and screen reader (WCAG 2.4.3,
3.3.1, 1.1.1, 4.1.3), reviewed by a 3-agent cross-model pass.

- Per-field validation: the Advanced inputs now surface errors via FluentUI
  `Field` validationState/validationMessage (programmatically associated,
  aria-invalid + role="alert") instead of one detached bottom message; credential
  and image-tag checks are skipped while reusing an existing instance (those inputs
  are hidden), and the offending field is highlighted.
- Progress announcements: a polite live region streams the current provisioning
  stage; the failed and Docker-not-ready states are announced; the existing
  success announcement is unchanged. The failure announcer is polite so it does not
  clip the focus move, and the progress message is suppressed once a stage errors.
- Stage rows: the list is exposed as role="list"/"listitem" with a natural
  row aria-label ("Pulling official image, done"); decorative icons are hidden. On
  failure the active stage flips to the error state so it no longer reads/looks like
  "in progress".
- Focus management: focus moves to the primary result action (Open Connection /
  Retry) when provisioning ends, so keyboard/screen-reader users are not stranded on
  <body> when the Cancel button unmounts.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…Logs / Start over (v1.1)

Implements §9.1: a wire-protocol readiness timeout no longer tears the container
down. It is kept running (it may just be slow to initialize) and the webview offers
Wait longer, View Docker output, and Start over, reviewed across 3 rounds by 5 agents.

- Wait longer: re-probes the SAME retained container for another readiness window via
  a new `resumeReadiness` generator + `waitLonger` subscription — no re-pull, keeping
  the already-completed stages visible and streaming live logs.
- Start over: `discardTimedOutInstance` removes the container and wipes the volume ONLY
  for a fresh (non-reusing) attempt; a reusing attempt's real data is always kept.
- Reopen-safe: a `canResumeReadiness` status flag (gated on !busy) rehydrates the
  recovery actions when the panel is reopened instead of stranding the container.

Data-safety / correctness:
- Refactored the provision try/catch/finally to extract a shared, non-yielding
  `finalizeReadyInstance`, preserving the setStatus(Running)->success ordering so an
  unsubscribe after the terminal event can never tear down a healthy container.
- Terminal events (timeout and hard error) are buffered and yielded AFTER `finally`,
  so provisioning/lifecycleBusy are clear before the webview shows Wait longer / Retry
  — no fast-click race with the "already in progress" guard.
- `reconcile()` only removes an orphaned container, never its volume (a missing secret
  does not prove the volume is disposable — avoids irreversible data loss).
- A webview stream-generation guard ignores trailing callbacks from a superseded or
  cancelled stream. Resume emits booleans/enum-only telemetry.

Known limitation (v1.2): after a reload, reconcile adopts a reusing timed-out container
as Running without re-probing; recoverable via Restart/Delete, no data loss.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
# Conflicts:
#	l10n/bundle.l10n.json
#	src/documentdb/ClustersClient.ts
#	src/tree/connections-view/DocumentDBClusterItem.ts
guanzhousongmicrosoft and others added 13 commits July 6, 2026 09:42
…low leak

Three fixes from the PR-readiness review (5-agent rubber-duck, all-correct
consensus; Fixes 2 and 3 unanimous):

- Publish the container port on 127.0.0.1 (loopback) instead of 0.0.0.0 so the
  local instance (auto-generated creds + TLS-allow-invalid) is not LAN-exposed,
  matching the "Runs on: This machine" UX and the loopback isPortFree pre-check.
- QuickStartClusterItem.getTreeItem() now preserves the base tree item but forces
  its state-aware description, so the Running row shows "Running - localhost:<port>"
  instead of the inherited "TLS/SSL Disabled" badge (the base getTreeItem ignores
  descriptionOverride). Only the Running row is affected.
- viewQuickStartLogs keeps a single CancellationTokenSource and cancels the prior
  follow before starting a new one, so repeated View Logs clicks no longer stack
  concurrent docker logs -f streams.

Gates: jest 2706/2706, build, lint, prettier.

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

Plan v3 for full multiple managed instances in v1 (reverses the single-instance
decision per owner). Foundation verified twice against the code; round-1 (15) +
round-2 (9) findings folded in. WI-0 unanimously review-ready. See
review-and-resolutions.md for both review rounds.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… (WI-0)

WI-0 of the multi-instance plan: make the Quick Start service testable without a
real Docker daemon.

- Add IContainerRuntime (13 IO methods); ContainerRuntimeImpl implements it.
- Move the pure inspectors isRunning/getBoundHostPort to standalone exported
  functions, keeping the interface an IO-only surface.
- QuickStartServiceImpl gains constructor(runtime: IContainerRuntime =
  ContainerRuntime) and calls this.runtime.*; the class is exported so tests can
  inject a mock runtime.

Behavior-preserving. Gates: build, lint, jest 2768/2768.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ration (WI-1)

WI-1 of the multi-instance plan: introduce per-instance identity and migrate an
existing single instance onto the alias-keyed model with no rename and no data loss.

- Add DEFAULT_ALIAS + containerName/volumeName/clusterId/secretKey/imageRefKey
  helpers. containerName(DEFAULT_ALIAS)/volumeName(DEFAULT_ALIAS) equal the legacy
  names, so an existing container/volume is adopted unchanged.
- New quickStartRegistry.ts: the persisted registry (schema carries the WI-2
  lease/operationId fields), a per-process-locked updateRegistry, and a step-wise
  resumable legacy-key migration (awaited before reconcile; copy -> ensure each ->
  delete legacy last; port derived from the connection string / live inspect).
- Repoint the service's persisted keys (SECRET_KEY, IMAGE_REF_STATE_KEY,
  QUICK_START_CLUSTER_ID) to alias-derived values, with a legacy fallback on the
  volume-wipe-gating reads and the imageRef reuse chain. Delete purges legacy keys.
- QUICK_START_CLUSTER_ID value changes (quickstart-local-documentdb ->
  quickstart-vscode-documentdb-local); it is an ephemeral in-memory cache key only.

Reviewed by 5 agents over 2 rounds (initial + fix confirmation) - round-2 unanimous
APPROVE, data-safe across every crash interleaving. Adds 21 unit tests (registry +
migration matrix + service-level R1 fallback), the first state-machine-adjacent tests
(enabled by WI-0's injectable runtime).

Gates: build, lint, jest 2787/2787.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…2, part 1)

Foundation for the multi-instance state machine: keep the persisted registry in
sync with the live default instance so WI-2's tree/reconcile can enumerate it.

- Add upsertInstanceRecord / removeInstanceRecord to quickStartRegistry.
- finalizeReadyInstance upserts the default instance's record (phase 'ready',
  bound port) when it becomes Running.
- deleteContainer removes the record (a full clean slate - no ghost tree row).

Closes the WI-1 review follow-ups (opus-4.7 NB#1 / opus-4.8) that flagged the
registry as write-only. Behaviour is unchanged (the registry is not yet read for
logic); the Map-based per-alias state machine + concurrency model land next.

Gates: build, lint, jest 2790/2790.

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

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

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the seven single-instance fields (state/metadata/errorMessage/
missing/provisioning/pendingReadiness/lifecycleBusy) with a
Map<alias, InstanceRuntimeState> plus a lazy stateFor(alias) accessor.
This is a behavior-preserving refactor: every call site keys on
DEFAULT_ALIAS, so the state machine still behaves as single-instance.

- setStatus/getStatus/isBusy now route through stateFor(); getStatus
  and setStatus take an alias (getStatus defaults to DEFAULT_ALIAS, so
  the router is unchanged); isBusy stays a getter (localQuickStartRouter
  reads it as a property).
- Add isBusyFor(alias), listStatuses(): InstanceStatus[] and the
  toInstanceStatus() mapper as the seam WI-3/4/5 will consume.
- Add InstanceRuntimeState (per-alias) and InstanceStatus (tree DTO);
  PendingReadiness gains alias/displayName.

Full jest 2790/2790, build/lint/prettier green. No user-facing strings.

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

Behavior-preserving refactor: every public/private method now derives its
container name, volume name, secret/imageRef keys and cache clusterId from an
alias parameter (default DEFAULT_ALIAS), instead of the hardwired legacy
constants. Because containerName(DEFAULT_ALIAS) et al. equal the legacy
constants, single-instance behavior is unchanged.

- findManagedContainer(alias)/isManaged(id, alias): match the
  vscode.documentdb.alias label, with a legacy fallback (absent/empty label
  adopts as DEFAULT) so a pre-alias-label container is still found/adopted.
- readStoredConnectionString(alias)/getReusableCredentials(alias): read
  secretKey(alias); the legacy flat key is a DEFAULT-only fallback.
- start/stop/restart/deleteContainer(alias)/runLifecycle(alias, op): per-alias
  lifecycleBusy + alias-derived names/keys; delete purges legacy keys for the
  default only.
- finalizeReadyInstance derives identity from pending.alias/displayName
  (metadata.alias/clusterId no longer hardcoded to DEFAULT).
- Drop now-unused SECRET_KEY/QUICK_START_CONTAINER_NAME/QUICK_START_VOLUME_NAME.

liveStateGuard/confirmStaysRunning keep operating on the container id (no alias
param) — the per-instance message is a WI-4 concern; see wi2-core-plan.md.

Full jest 2790/2790, build/lint/prettier green. No user-facing strings.

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

Reconcile is now registry-driven and enumerates the union of the durable
registry and the live labelled containers (grouped by the alias label; an
absent/empty label is the DEFAULT instance), rebuilding each alias's state:

- Case 1 (container + recoverable secret) -> adopt (running->Running,
  exited->Stopped) and promote the record to 'ready', clearing any stale lease.
- Case 2 (fresh provisioning lease) -> Provisioning.
- Case 3 (no container + ready record) -> Missing (kept, recoverable); a stale
  provisioning reservation is scavenged (crashed pre-create, nothing created).
- Case 4 (container + no secret + no fresh lease) -> surfaced as Error /
  credential-unavailable: NEVER removed, volume NEVER touched (R2). Inverts the
  old remove-orphan behavior (QuickStartService.test.ts R2 test rewritten).

Also:
- listStatuses() ordered DEFAULT-first (WI-3 tree seam).
- refreshLiveState() refreshes every in-memory alias, skipping in-flight ones;
  never scavenges (reconcile/activation only).
- deleteContainer() falls back to findManagedContainer when in-memory metadata
  is absent, so Delete works on a surfaced Missing/credential-unavailable node.
- Registry gains isProvisioningLeaseFresh() + PROVISIONING_LEASE_TTL_MS.

Tree impact is nil for the current getStatus(DEFAULT) consumer (Missing gates on
metadata, so a reloaded no-container default still shows the rocket; recreate
reuses the volume). nextSuffix self-heal deferred to WI-2e.

Full jest 2798/2798 (+8 reconcile tests), build/lint/prettier green.

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

Fixes the data-safety findings from the 3-agent foundation review
(recorded in review-and-resolutions.md):

- C1 (HIGH, data loss): provision() no longer silently wipes a
  credential-unavailable instance's data volume. When !reusing AND (a
  managed container exists OR a durable 'ready' record exists), it now
  surfaces CREDENTIAL_UNAVAILABLE_MESSAGE and aborts instead of removing
  the container / wiping the volume (RR4 / plan §5.2). Only a truly-fresh
  alias (no container, no 'ready' record) reaches the clean-slate wipe —
  a dead failed-attempt orphan (container already removed by provision's
  finally, no 'ready' record) still retries fine.
- C2 (concurrency): reconcile's stale-reservation scavenge re-validates
  phase==='provisioning' && still-stale INSIDE the locked write, so a
  record a concurrent finalize/adopt just promoted to 'ready' is never
  dropped by the by-alias filter.
- C3 (concurrency): refreshLiveState captures the containerId before the
  inspect await and skips the mutation if the alias's metadata changed
  (a concurrent deleteContainer / re-adopt), avoiding a stale missing=true.

+3 provision wipe-gate tests. Full jest 2801/2801, build/lint/prettier green.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Docs-only: the 5-agent confirmation review (gpt-5.4/5.5, opus 4.6/4.7/4.8, max
effort) unanimously APPROVED the C1/C2/C3 fixes. Records the accepted C1
residual and the WI-2e-2 allocation checklist (incl. the C4 DEFAULT-hardwired
provision path to thread).

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

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

✅ Code Quality Checks

Check Status How to fix
Localization (l10n) ✅ Passed
ESLint ✅ Passed
Prettier formatting ✅ Passed

This comment is updated automatically on each push.

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

📦 Build Size Report

Metric Base (main) PR Delta
VSIX (vscode-documentdb-0.9.1.vsix) 8.01 MB 8.05 MB ⬆️ +44 KB (+0.5%)
Webview bundle (views.js) 5.88 MB 5.92 MB ⬆️ +37 KB (+0.6%)

Download artifact · updated automatically on each push.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: In progress

Development

Successfully merging this pull request may close these issues.

3 participants