Skip to content

Add clear script environment cache command (PEP 723 PR 13/16) - #1724

Open
Stella Huang (StellaHuang95) wants to merge 5 commits into
microsoft:mainfrom
StellaHuang95:pep723-pr13-clear-cache-v2
Open

Add clear script environment cache command (PEP 723 PR 13/16)#1724
Stella Huang (StellaHuang95) wants to merge 5 commits into
microsoft:mainfrom
StellaHuang95:pep723-pr13-clear-cache-v2

Conversation

@StellaHuang95

Copy link
Copy Markdown
Contributor

Part of #1602 (PEP 723 inline script env support). Design doc: #1601.

Builds on the merged persistence work in #1697 and is rebased on current main.

Roadmap context

This is PR 13 of 16 in the PEP 723 inline-script roadmap. It adds the explicit, user-confirmed cache lifecycle operation that the later TTL work will reuse.

Phase 5: Lifecycle and polish PR Status
PR 7: persistence (get / set + Memento) merged (#1697)
PR 13: clear inline-script cache this PR
PR 14: opportunistic 14-day TTL eviction follow-up
PR 15: lifecycle telemetry separate
PR 16: status-bar decision resolved; no code PR

Why this PR

The extension can create extension-owned inline-script environments and persist script associations, but it has no complete way to remove that state. Clearing only the files would leave Memento associations and pythonProjects entries pointing at deleted interpreters; clearing only settings would leave disk usage behind.

This PR adds one coordinated lifecycle operation covering:

  • extension-owned cache entries;
  • persisted and in-memory script associations;
  • active selection events; and
  • generated inline-script project settings.

Because this is destructive and the cache is shared by extension-host processes, the implementation is intentionally fail-closed around path ownership and locks.

What this PR does

Adds an internal, confirmation-gated clear command

  • Registers python-envs.clearScriptEnvCache only while the hidden inline-script feature flag is enabled.
  • Does not contribute the command to package.json or the Command Palette before rollout.
  • Shows a modal warning covering cached environments, associations, and project entries.
  • Cancelling the prompt performs no filesystem, state, or settings changes.
  • Runs cache cleanup before settings removal, so failed/partial cache cleanup does not silently rewrite project configuration.

Keeps generic cache clearing behavior safe

  • The existing public python-envs.clearCache command continues to clear existing non-inline managers.
  • It skips the preview inline manager because the generic path has no inline-specific confirmation or project-settings lifecycle.
  • The dedicated command invokes the inline manager directly and performs the complete cleanup transaction.

Serializes in-process maintenance

  • Adds a manager-local maintenance queue and barrier.
  • create(), get(), and set() cannot observe or mutate half-cleared state.
  • A clear request refuses to begin when creation already started.
  • A creation request that arrives after clear begins waits for maintenance to settle.
  • Multiple maintenance requests are serialized without globally serializing unrelated managers.

Coordinates deletion across extension hosts

  • Acquires and holds each cache entry's cross-process lock through deletion.
  • Classifies locks as missing, held, retained, stale, orphaned, malformed, or unavailable.
  • Uses PID liveness to distinguish a live owner from a stale owner.
  • Makes retained markers generation-specific by preserving the owner's PID/nonce.
  • Reclaims only the exact stale/retained generation marker that was inspected.
  • If another process replaces that generation before the atomic claim, reclamation loses safely and touches nothing.
  • Ambiguous legacy fixed retained markers remain recognizable but are conservatively not reclaimed.

Validates every destructive path

Before deleting an entry, cleanup verifies that:

  • global storage and script-envs-v1 are normal directories rather than symlinks/junctions;
  • the versioned cache root is the expected direct child of global storage;
  • neither path is a filesystem root or dangerously shallow;
  • physical realpath containment matches the expected ownership boundary;
  • the cache root has not changed since the cleanup snapshot; and
  • the target entry is a normal direct-child directory inside that same physical root.

The entry lock is acquired first, then root and entry ownership are revalidated immediately before removal.

Keeps state consistent through partial failures

  • Attempts cache entries independently and records successful removals.
  • Aggregates and surfaces deletion/persistence failures rather than returning success-shaped output.
  • Invalidates only associations whose environment was removed or is definitively missing.
  • Preserves associations for cache entries that could not safely be removed.
  • Cancels pending rehydration for invalidated scripts.
  • Clears warm environment and validation caches.
  • Advances association revisions so stale async work cannot restore removed selections.
  • Emits onDidChangeEnvironment only for selections actually invalidated.

Removes generated inline project settings safely

  • Resolves pythonProjects entries independently from global, workspace, and workspace-folder sources.
  • Removes only entries whose manager is the inline-script manager.
  • Preserves non-inline duplicate entries and higher-precedence overrides.
  • Handles same relative paths across multiple workspace roots.
  • Aggregates global/workspace updates so each shared scope is written once.
  • Unloads only loaded projects that have no remaining configuration source.

Cleanup semantics

Condition Behavior
Entry is unlocked and physically owned Lock, revalidate, delete
Lock belongs to a live process Refuse that deletion
Exact stale/retained generation can be claimed Reclaim, acquire a fresh lock, delete
Lock is unavailable, malformed, orphaned, or legacy-ambiguous Preserve entry and surface failure
Root or entry is redirected/outside ownership boundary Refuse deletion
One entry fails after another was removed Preserve valid survivors; invalidate removed associations; report aggregate failure
Persistence update fails after disk cleanup Keep in-memory state consistent and surface the persistence error
Cache is already absent Clear stale associations safely; remain idempotent

Example

Clear Script Environment Cache
→ modal confirmation
→ enter manager maintenance barrier
→ verify physical cache root
→ acquire exact per-entry lock
→ revalidate ownership immediately before deletion
→ delete safe entries
→ reconcile Memento + in-memory selections + events
→ remove generated inline pythonProjects settings

Tests

Coverage includes:

  • prompt cancellation and command ordering;
  • generic clear behavior with the preview manager absent/present;
  • in-process create/clear ordering;
  • live, stale, retained, orphaned, malformed, unavailable, and legacy lock states;
  • exact-generation reclamation and delayed-reclaimer/new-creator races;
  • holding entry locks through deletion;
  • unsafe, shallow, redirected, symlinked, and root-swapped paths;
  • successful, missing-cache, idempotent, and partial-failure cleanup;
  • persistence failures and pending-rehydration races;
  • global/workspace/workspace-folder setting precedence;
  • multi-root projects and same-path entries; and
  • default-off command registration.

Validation on the rebased branch:

  • npm run compile-tests
  • npm run compile
  • npm run lint
  • focused lock/cache-clear/settings/command suites: 38 passing

The full Windows unit run reaches 1638 passing and 5 pending; the existing concurrent writeMetaJson rename test can still intermittently fail with EPERM on Windows. That writer is unchanged by this PR and the same failure is reproducible on main.

Performance

  • No activation scan, timer, or background maintenance is added.
  • All work is initiated by the internal clear command.
  • Per-entry locks avoid globally serializing independent environment creation across extension hosts.
  • The maintenance barrier exists only inside the enabled inline manager and is active only during cleanup.

User impact

No default-path user impact. The manager and command remain behind the undeclared, default-off python-envs.inlineScripts.enabled flag, and the command is not publicly contributed.

When the internal flag is manually enabled, the existing generic cache command still behaves as before for non-inline managers. Inline cleanup is available only through the dedicated confirmed lifecycle.

Scope and follow-up

This PR intentionally does not implement:

  • automatic routing or project registration;
  • activation-time discovery;
  • silent/opportunistic deletion;
  • TTL expiration; or
  • lifecycle telemetry.

PR 14 will reuse this safety and state-cleanup foundation to remove entries whose lastUsedAt exceeds the planned 14-day TTL.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3cb82ae9-7424-40a4-9156-8c54ac6e0895
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3cb82ae9-7424-40a4-9156-8c54ac6e0895
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3cb82ae9-7424-40a4-9156-8c54ac6e0895
Coordinate per-entry deletion locks, keep partial failures consistent, and clean inline project settings safely.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 6b12d843-8011-4bfc-9ba9-f75761eadee2
Claim exact stale or retained lock markers before inline cache cleanup.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 6b12d843-8011-4bfc-9ba9-f75761eadee2
@rchiodo

Rich Chiodo (rchiodo) commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

🔒 Automated review in progress — Rich Chiodo (@rchiodo) is auto-reviewing this PR.

Comment thread src/features/settings/settingHelpers.ts

@rchiodo Rich Chiodo (rchiodo) 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.

Approved via Review Center.

@rchiodo Rich Chiodo (rchiodo) added the review-auto:approved Automated review: no blocking findings (approval posted). label Aug 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature-request Request for new features or functionality review-auto:approved Automated review: no blocking findings (approval posted).

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants