Skip to content

Bump actions/setup-python from 5 to 6#9

Closed
dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/github_actions/actions/setup-python-6
Closed

Bump actions/setup-python from 5 to 6#9
dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/github_actions/actions/setup-python-6

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Sep 4, 2025

Copy link
Copy Markdown
Contributor

Bumps actions/setup-python from 5 to 6.

Release notes

Sourced from actions/setup-python's releases.

v6.0.0

What's Changed

Breaking Changes

Make sure your runner is on version v2.327.1 or later to ensure compatibility with this release. See Release Notes

Enhancements:

Bug fixes:

Dependency updates:

New Contributors

Full Changelog: actions/setup-python@v5...v6.0.0

v5.6.0

What's Changed

Full Changelog: actions/setup-python@v5...v5.6.0

v5.5.0

What's Changed

Enhancements:

Bug fixes:

... (truncated)

Commits
  • e797f83 Upgrade to node 24 (#1164)
  • 3d1e2d2 Revert "Enhance cache-dependency-path handling to support files outside the w...
  • 65b0712 Clarify pythonLocation behavior for PyPy and GraalPy in environment variables...
  • 5b668cf Bump actions/checkout from 4 to 5 (#1181)
  • f62a0e2 Change missing cache directory error to warning (#1182)
  • 9322b3c Upgrade setuptools to 78.1.1 to fix path traversal vulnerability in PackageIn...
  • fbeb884 Bump form-data to fix critical vulnerabilities #182 & #183 (#1163)
  • 03bb615 Bump idna from 2.9 to 3.7 in /tests/data (#843)
  • 36da51d Add version parsing from Pipfile (#1067)
  • 3c6f142 update documentation (#1156)
  • Additional commits viewable in compare view

Dependabot compatibility score

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot merge will merge this PR after your CI passes on it
  • @dependabot squash and merge will squash and merge this PR after your CI passes on it
  • @dependabot cancel merge will cancel a previously requested merge and block automerging
  • @dependabot reopen will reopen this PR if it is closed
  • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)

Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5 to 6.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](actions/setup-python@v5...v6)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot @github

dependabot Bot commented on behalf of github Sep 4, 2025

Copy link
Copy Markdown
Contributor Author

Labels

The following labels could not be found: skip news. Please create it before Dependabot can add it to a pull request.

Please fix the above issues or remove invalid values from dependabot.yml.

@dependabot @github

dependabot Bot commented on behalf of github Sep 10, 2025

Copy link
Copy Markdown
Contributor Author

Looks like actions/setup-python is no longer a dependency, so this is no longer needed.

@dependabot dependabot Bot closed this Sep 10, 2025
@dependabot
dependabot Bot deleted the dependabot/github_actions/actions/setup-python-6 branch September 10, 2025 07:34
tkislan added a commit that referenced this pull request Jul 2, 2026
- Guard init runs with a per-kernel in-flight CancellationTokenSource so
  overlapping start events don't run init twice and a restart cancels an
  in-flight run (CodeRabbit #9).
- Replace void fire-and-forget snapshot calls with explicit .catch logging (#10).
- Unregister the old server handle from the provider on environment switch so
  stale entries don't leak (#12).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ
tkislan added a commit that referenced this pull request Jul 17, 2026
* refactor(deepnote): re-key notebook manager per (projectId, notebookId) + add project-id resolver

Chunk 1 of single-notebook migration (§4 partial, §5). No behaviour change.

- Manager caches originals in a nested Map<projectId, Map<notebookId, project>>
  so sibling files sharing a project.id no longer clobber each other.
- New API: getOriginalProject(projectId, notebookId) exact/no-fallback,
  getAnyProjectEntry(projectId), storeOriginalProject/updateOriginalProject
  (3-arg), updateProjectIntegrations iterates all entries.
- Update IDeepnoteNotebookManager and IPlatformDeepnoteNotebookManager; repoint
  all project-level read-only callers to getAnyProjectEntry.
- Add canonical readDeepnoteProjectFile and resolveProjectIdFor{File,Notebook}.
- Selection state and init-run tracking intentionally kept (removed in later chunks).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01URccsVKXeNKZqqPi89L4ro

* refactor(deepnote): render single notebook + serialize by metadata, drop selection machinery

Chunk 2 of single-notebook migration (§1 + Cleanup).

- deserializeNotebook renders the first non-init notebook (findDefaultNotebook),
  falling back to the only/init notebook; never composes init.
- serializeNotebook resolves the target from document metadata alone (projectId +
  notebookId required) and looks it up with the exact getOriginalProject, throwing
  clear errors instead of falling back to a wrong sibling.
- detectContentChanges collapses to a single-notebook comparison.
- Remove the ?notebook=<id> selection machinery: findCurrentNotebookId, the
  manager's selection state + interface methods, the explorer's query-param opens
  and selectNotebookForProject calls, and the tree item's custom resourceUri.
- Explorer no longer depends on IDeepnoteNotebookManager.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01URccsVKXeNKZqqPi89L4ro

* feat(deepnote): split legacy multi-notebook files into single-notebook siblings

Chunk 3 of single-notebook migration (§0, §2, §3).

- Add allocateSiblingUri: the single filesystem-aware, collision-safe sibling
  filename allocator (bumps -2/-3 before .deepnote, honors an in-batch reserved
  set, bounded retries).
- Add a notebook file factory (buildSingleNotebookFile / buildSiblingNotebookFileUri)
  for creating sibling single-notebook files (wired into the explorer in a later
  chunk).
- Add DeepnoteMultiNotebookSplitter: on opening a multi-notebook .deepnote file,
  offer to split it into one new single-notebook file per notebook. The action
  flushes the editor if dirty, writes all children, migrates the environment
  selection, then closes the tab and deletes the original to trash. A child-write
  failure leaves the original intact (write-before-delete).
- Wire the splitter into activation with an optional (desktop-only) environment
  mapper; add a refresh() passthrough on the explorer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01URccsVKXeNKZqqPi89L4ro

* feat(deepnote): propagate project metadata across sibling files on disk

Chunk 4 of single-notebook migration (§6).

- Add DeepnoteProjectMetadataPropagator (desktop): given a project id and a
  project-level mutator, enumerate every sibling .deepnote file on disk (open or
  closed), apply the change, and write it back. Skips no-op writes, refreshes the
  manager cache for open siblings, and collects per-file failures instead of
  aborting. Fires an onFileWritten hook so the file watcher treats each write as a
  self-write (no reload/save storm).
- Route integration updates and project rename through the propagator so closed
  siblings stay consistent; web falls back to the cache-only / single-file paths.
- Expose getOriginalProject/updateOriginalProject on the platform manager interface.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01URccsVKXeNKZqqPi89L4ro

* feat(deepnote): group the explorer by project and add a notebook status bar

Chunk 5 of single-notebook migration (§7).

- Tree is grouped: ProjectGroup (by project id) -> ProjectFile -> Notebook. A
  single-notebook file is a leaf labelled with its notebook; legacy multi-notebook
  files stay collapsible. The init notebook is excluded from counts everywhere.
- Refresh is grouping-safe: refreshNotebook evicts every sibling cache entry for a
  project id and all refreshes fire a full-tree change (no per-item fires).
- Commands are project-scoped vs notebook-scoped; new/duplicate/add-notebook create
  sibling files via the factory (never appended), delete removes the file for a
  single-notebook file, and notebook names are unique within a project group.
- Add a status bar item showing the active Deepnote notebook with a
  "Copy Active Deepnote Notebook Details" command.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01URccsVKXeNKZqqPi89L4ro

* refactor(deepnote): key the Jupyter server and environment per notebook

Chunk 6 of single-notebook migration (§8).

- Key the server starter maps, the config handle, and the kernel auto-selector by
  notebook.uri.toString() - the same identity the kernel and controller use - so a
  notebook's server is 1:1 with its kernel. Sibling notebooks of one project no
  longer share a server; the working directory and SQL env are taken from each
  notebook's own file.
- Fix environment deletion: stop every server using the environment (including
  closed notebooks whose server is still running) before removing the mappings,
  driven from the notebook->environment mapper. Drop the dead environmentServers
  map that was never populated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01URccsVKXeNKZqqPi89L4ro

* feat(deepnote): scope snapshots per notebook with a backward-compatible reader

Chunk 7a of single-notebook migration (§9).

- Write snapshots with notebook-scoped filenames via @deepnote/convert
  (generateSnapshotFilename / parseSnapshotFilename), replacing the local slug and
  filename regex.
- readSnapshot resolves snapshots path-free (it runs at deserialize, which has no
  URI): glob by project id, rank the notebook-scoped match first and keep legacy
  project-scoped snapshots as a fallback, and skip an empty-output "latest" (save
  race) or a corrupt file while walking candidates. Legacy snapshots are read, never
  migrated or deleted.
- Defer the execution snapshot save until outputs settle (quiet window with a max
  wait) and cancel it on re-execute / close.
- Use convert's computeSnapshotHash on the save path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01URccsVKXeNKZqqPi89L4ro

* feat(deepnote): run the init notebook per kernel from its sibling file

Chunk 7b of single-notebook migration (§10).

- The init runner now subscribes to kernel start and restart events and runs the
  init notebook found in its own sibling .deepnote file (matched by project id +
  initNotebookId via isValidSiblingInitCandidate), instead of looking it up in the
  main file's notebooks.
- Track "init has run" per kernel in a WeakSet<IKernel>: a fresh kernel runs init
  once, and an in-place restart (which fires onDidRestartKernel) re-runs it so the
  kernel is re-initialized before the next user cell. A missing sibling is logged
  and skipped without permanently marking the project.
- Remove the manager's persistent init-run tracking and the selector's init
  staging; the runner owns init triggering.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01URccsVKXeNKZqqPi89L4ro

* fix(deepnote): satisfy lint and spell-check on the migration branch

- void the fire-and-forget onExecutionComplete call (no-floating-promises),
  matching the existing void performSnapshotSave pattern.
- Use American "behavior" in a comment.
- Add test-only technical words (basenames, initmain, Résumé, unparseable) to cspell.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01URccsVKXeNKZqqPi89L4ro

* test(deepnote): use the real @deepnote/convert in unit tests instead of duplicating it

The mocha ESM loader wholesale-mocked @deepnote/convert and reimplemented its pure
helpers (resolveSnapshotNotebookId, splitByNotebooks, isValidSiblingInitCandidate,
snapshot filename generate/parse, hashing, etc.). That duplicated upstream logic
with no drift detection: if convert changed, the mock silently kept the old
behavior and tests stayed green against a fiction.

- Remove the @deepnote/convert interception from build/mocha-esm-loader.js so unit
  tests exercise the real package's pure functions (and now track its actual API).
- Mock only the one genuinely side-effecting export, convertIpynbFilesToDeepnoteFile
  (real node:fs I/O), via esmock in the explorer import suites where it is used.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01URccsVKXeNKZqqPi89L4ro

* fix(deepnote): address code-review findings on the snapshot/serializer paths

From the Codex review of the PR (F1/F3/F4/F5), all independently verified:

- F1 (P1): snapshot save fetched the cached project with getAnyProjectEntry(projectId),
  which can return the wrong sibling when multiple single-notebook siblings of one
  project are open, silently skipping the snapshot write. Use the exact
  getOriginalProject(projectId, notebookId) lookup instead.
- F3: collectNotebookNamesForProject globbed **/*.deepnote without skipping snapshot
  sidecars, so stale snapshot notebook names polluted the name-uniqueness set. Filter
  snapshot files (matching the tree provider and propagator).
- F4: detectContentChanges compared notebooks[0]; for a legacy [init, main] file the
  edited notebook is not at index 0, so edits were missed and modifiedAt preserved.
  Match the notebook by id.
- F5: the deferred-save timer fired performSnapshotSave as a floating promise; wrap the
  save body in try/catch/finally so a build/write failure is logged (not an unhandled
  rejection) and execution state is always cleared.

Adds regression tests for F1 (exact lookup), F3 (snapshot exclusion), and F4 (match by id).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01URccsVKXeNKZqqPi89L4ro

* fix(deepnote): exact snapshot lookup in the file watcher + close-cancel init runs

Addresses round-2 code-review findings G2 and G3 (both verified P2).

- G2: deepnoteFileChangeWatcher's snapshot block-id recovery used the project-only
  getAnyProjectEntry(projectId), which can return a different open sibling's cached
  project (siblings share project.id), leaving originalBlocks undefined and silently
  skipping recovered outputs. Use the exact getOriginalProject(projectId, notebookId)
  — the same fix already applied to snapshotService (F1), here in the watcher path
  that was missed.
- G3: moving init execution to the event-driven runner dropped the notebook-close
  cancellation that the kernel auto-selector used to provide, so closing a notebook
  mid-init left the remaining init blocks executing against a closed notebook. Tie the
  init run to a CancellationTokenSource cancelled on notebook close and dispose it in
  a finally.

Adds regression tests for both (each fails on the pre-fix code).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01URccsVKXeNKZqqPi89L4ro

* refactor(deepnote): remove metadata propagation and project-level export/delete

Removes two pieces of functionality; also folds in the branch's in-progress
updates this work was layered on top of (they could not be isolated, as the
removals are interleaved with and built on top of that WIP).

Removed - project-metadata propagator:
- Delete DeepnoteProjectMetadataPropagator and its types, drop the DI binding,
  and unwire it everywhere (activation, file-change watcher self-write hook,
  integration webview, explorer rename). Project-level fields are no longer
  fanned out across sibling files: each notebook owns its own integrations, and
  project-name drift is accepted for now. Drop the now-dead updateOriginalProject
  manager method and two stale comments.

Removed - project-level explorer commands:
- Delete the exportProject and deleteProject commands (constants, command-arg
  type, registrations, package.json command defs + sidebar menus, nls titles,
  and their unit tests). Per-notebook export remains via the existing
  exportNotebook command (first non-init notebook of the file).

Also includes the branch's pending updates the above was built on: dependency
bumps (incl. @deepnote/convert 4.0), the getOriginalProject ->
getProjectForNotebook manager rename and getAnyProjectEntry removal, and
assorted snapshot/serializer/kernel adjustments.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* Revert unnecessary renamings

* fix(deepnote): trim indented bullet content before stripping markdown

@deepnote/blocks@4.6.0+ renders `text-cell-bullet` blocks with
`indent_level >= 1` using leading spaces (two per level) before the bullet
marker. stripMarkdown's bullet regex only matches at column 0, so the
leading indentation must be trimmed first for the plain-text cell value to
round-trip correctly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* refactor(deepnote): import convert helpers directly, drop snapshotFiles re-exports

snapshotFiles.ts re-exported six snapshot-filename helpers from
@deepnote/convert. Remove the re-export block and import the helpers
directly from @deepnote/convert at each use site (snapshotService.ts and
the snapshotFiles unit test). snapshotFiles.ts now keeps only its local
helpers (SNAPSHOT_FILE_SUFFIX, isSnapshotFile, extractProjectIdFromSnapshotUri)
plus the single internal use of parseSnapshotFilename.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* refactor(deepnote): update buildSnapshotPath to use object destructuring

Refactor the buildSnapshotPath method to accept an object as an argument, improving readability and maintainability. Update all relevant calls to this method throughout the snapshotService and its unit tests to match the new signature. This change enhances the clarity of parameter usage and reduces the risk of errors when passing arguments.

* test(deepnote): remove redundant subagent-written tests

Trim the single-notebook test suites by removing duplicate and
tautological tests and collapsing/merging several others, shrinking the
PR's test additions by ~640 lines with no loss of real coverage.

Cuts target only tests this branch added:
- exact-(projectId, notebookId)-lookup restatements duplicated across
  the watcher, serializer, snapshot, and manager suites
- wrapper tests already covered by the delegate's own tests
  (addNotebookToProject, sibling-file allocation, project-id resolution)
- tautologies over trivial template/getter functions (serverUtils)
- framework-registration smoke tests (status bar)

Merges keep the one meaningful assertion and drop the duplicate
scaffolding (e.g. legacy-delete no-op folded into the existing delete
test; two init builders parametrized into one).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* feat(deepnote): retire the split-away original as a .legacy backup

When splitting a legacy multi-notebook .deepnote file into single-notebook
siblings, rename the original to `<name>.deepnote.legacy` instead of moving it
to the OS trash. The `.legacy` suffix takes it out of the extension's view (it
no longer matches `*.deepnote`) while keeping it on disk next to the split
results, so the user can restore it by removing the suffix.

Unlike `workspace.fs.delete({ useTrash: true })`, this is deterministic and does
not depend on an OS trash backend (which can be absent on headless Linux).
Collisions bump the name to `.legacy-2`, `.legacy-3`, … and the rename still
happens only after every child is durably written (write-before-retire).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* test(e2e): add multi-notebook split test with per-spec screenshots

Add an ExTester end-to-end test that drives the real VS Code UI through the
on-open split of a legacy multi-notebook .deepnote file: it asserts the split
prompt, the one-file-per-notebook result, the retained `.legacy` backup, that
each sibling opens without re-prompting, and that content plus the project
integration fan out into every split file.

Add a `createScreenshotter(this)` helper that captures step screenshots into a
per-spec directory derived from the running test file
(`test/e2e/screenshots/<spec>/`), plus the `sales-analytics.deepnote` fixture.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* test(e2e): add single-notebook-open and init-notebook split/runner suites

Add ExTester end-to-end suites covering:
- opening a plain single-notebook file (opens directly, no split prompt, the
  status bar shows the notebook name);
- splitting a multi-notebook file that declares an init notebook (the init
  notebook becomes its own single-notebook sibling; each main sibling still
  references it via initNotebookId);
- the init-notebook runner: the sibling init notebook runs hidden in a main
  notebook's kernel so its definitions are available, and re-runs after a kernel
  restart.

Add the quick-notes and etl-pipeline fixtures (including the pre-split
extract/init siblings), and disable the kernel-restart confirmation in the E2E
settings so the restart test can drive it non-interactively.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* test(e2e): add Explorer project-grouping suite

Add an ExTester end-to-end test asserting the Deepnote Explorer groups sibling
.deepnote files by project: three files sharing one project.id collapse into a
single "Marketing" group ("3 files") whose leaves are the three notebooks, while
a file from a different project appears as its own group. Reads the tree by
diffing visible leaves before/after expanding (avoids the page-object library's
flaky CustomTreeItem.getChildItems). Adds the three marketing fixtures.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* fix(e2e): open the workspace folder reliably across multiple suites

When several E2E suites run in one ExTester session (as in CI, via the
`*.e2e.test.js` glob), every workspace-folder open after the first failed with
"Failed to open folder after 5 attempts" in the suite's `before all` hook — only
the alphabetically-first suite passed.

Root cause: the simple "Open Folder" dialog (files.simpleDialog.enable)
navigates one directory level *toward* the typed path per OK click and only
accepts the folder once the browser is AT it. The helper clicked OK once then
re-opened the dialog each attempt, which reset navigation back to the default
directory — for the 2nd+ open that default is the previous, now-deleted
workspace, so the dialog fell back to "/" and never converged on the target.

Fix: click OK repeatedly within a single dialog until the pre-open workbench
element detaches (reload = folder accepted), instead of re-opening per attempt;
and set `window.openFoldersInNewWindow: "off"` so "Open Folder" reuses the
current window, keeping that reload detectable. Verified with four suites (16
tests) opening four folders in one session.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* test(e2e): add active-notebook status bar suite

Add an ExTester end-to-end test for the Deepnote status-bar item: it shows the
active notebook's name (with the "Copy Active Deepnote Notebook Details"
tooltip), hides when a non-notebook editor is focused, and — on click — copies
the notebook details to the clipboard with a confirmation toast. The clipboard
is verified by pasting into a scratch text file and reading it back.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* test(e2e): add notebook-management command suite

Add an ExTester end-to-end test for the notebook-management commands that create
and rename sibling .deepnote files from the Deepnote explorer: New Notebook,
Add Notebook (project-group context menu), Duplicate Notebook, and Rename
Notebook — each verified by the resulting notebook name inside the sibling files
plus the confirmation toast. Delete Notebook is included as a pending test: its
context-menu -> native confirmation-modal interaction is unreliable to drive
under ExTester (documented inline), so it is left as a manual check.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* test(e2e): add integrations UI suite

Add an ExTester end-to-end test for the Deepnote integrations UI: opening
"Manage Integrations" for a notebook whose project declares an integration lists
it (the "Sales BigQuery" integration on the sales-analytics-revenue fixture),
while a plain notebook (quick-notes) shows no such integration. Adds the
sales-analytics-revenue fixture (a single-notebook split of the Sales Analytics
project carrying the BigQuery integration + its SQL cell).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* test(e2e): add project-rename fan-out suite

Add an ExTester end-to-end test for renaming a Deepnote project from the
Explorer: none of the three "Marketing" siblings is opened, then the project
group is renamed to "Growth" via its context menu; the new name is asserted to
fan out to every sibling .deepnote file on disk (and the old name gone), with the
Explorer group relabelled and a confirmation toast.

Extract the shared Deepnote tree helpers (getDeepnoteExplorerSection,
readDeepnoteTreeRows, findDeepnoteGroup/Leaf, selectDeepnoteContextMenu) into
test/e2e/helpers/deepnoteTree.ts for reuse across the tree-driven suites.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* test(e2e): add init-only file edge-case suite

Add an ExTester end-to-end test for the edge case where a .deepnote file's only
notebook is its init notebook. Opening bootstrap-only.deepnote renders that
notebook as a fallback (status bar shows "Bootstrap"), does not raise the split
prompt (it is a single-notebook file), and the Explorer shows it with
"0 notebooks" (the init notebook is excluded from the count). Adds the
bootstrap-only fixture.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* test(e2e): add split-prompt safety suite

Covers the safety behaviour around the multi-notebook split prompt:
dismissing the prompt leaves the original file untouched (no split
siblings, no `.legacy` backup).

The dirty-flush-before-split check is left as a pending `it.skip`:
ExTester cannot reliably type into a rendered notebook cell (keystrokes
land in the cell's command mode instead of the editor buffer), so the
unsaved-edit path cannot be driven through the UI. That guarantee — and
the write-before-retire ordering — are covered directly by the splitter's
unit tests ("dirty gate (load-bearing safety)").

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* test(e2e): add snapshots suite

Covers manual-plan Suite I:
- I1 (no kernel): a legacy project-scoped snapshot — filename with no
  notebook-id segment — still loads its saved output when the notebook is
  opened. New fixtures `legacy-snapshot-demo.deepnote` + a legacy-form
  snapshot sidecar carrying a saved stdout.
- I2 (kernel): running a cell in two sibling notebooks of the same project
  writes two separate notebook-scoped `snapshots/*.snapshot.deepnote`
  files (distinct notebook-id segments), so snapshots don't bleed between
  siblings.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* test(e2e): add environment suite

Covers manual-plan Suite G:
- G1 (kernel): splitting a multi-notebook file that had a selected
  environment migrates that environment onto every split child. Verified
  via the `.vscode/deepnote.json` sidecar: select an env for the original,
  accept the split, delete the sidecar, open a split child — the sidecar is
  regenerated solely from the child's migrated mapping.
- G2 is a documented `it.skip`: verifying that deleting an environment
  stops even a CLOSED-but-running notebook's server requires observing an
  out-of-process server's lifecycle, for which ExTester has no deterministic
  UI/disk signal. That guarantee is covered directly by the unit test
  ("one notebook OPEN, the other CLOSED but its server still running") in
  deepnoteEnvironmentsView.unit.test.ts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* test(e2e): implement G2 — env delete stops a closed notebook's server

Replaces the G2 it.skip with a real, passing test. The earlier skip
assumed a stopped out-of-process server had no observable signal — but the
server starter writes a PID lock file per running server at
`os.tmpdir()/vscode-deepnote-locks/server-<pid>.json` (removed, and the
process killed, when the server stops). The test reads that PID from disk
and checks liveness with `process.kill(pid, 0)` — fully cross-platform
(no lsof/netstat/ps): runs a cell to start the server, closes the tab
(server survives — no onDidClose→stopServer), deletes the environment via
the Environments-view context menu + confirmation modal, and asserts the
closed notebook's server process is gone and its lock file removed.

Uses a dedicated `E2E Delete Env` so it never disturbs the shared env.
The delete modal is DOM-driveable because `window.dialogStyle: custom` is
set; it is confirmed via a raw-DOM click on the `.monaco-dialog-box`
button (ExTester's ModalDialog is unreliable for these dialogs).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* fix(deepnote): honour files.enableTrash when deleting a notebook

Deleting a single-notebook file hard-coded `useTrash: true`, but the OS
trash is not reliably available everywhere (headless CI, containers,
filesystems with no freedesktop trash spec — where the call can hang or
fail). That is the same cross-platform fragility that moved the
multi-notebook split to a `.legacy` rename. Honour the user's
`files.enableTrash` setting exactly as VS Code's own Explorer does: trash
when enabled (recoverable), permanent delete when disabled. Unit tests
cover both paths.

The E2E suite sets `files.enableTrash: false`, so its deletes are permanent
and never depend on a trash-capable filesystem — which also lets the
previously-skipped delete-notebook test (D5) run. D5 was skipped as an
undriveable confirmation modal, but the modal was in fact being confirmed;
the delete was hanging on trash (only visible once the E2E ran against a
repackaged VSIX). D5 now confirms the {modal:true} dialog via a new shared
`confirmModalDialog` raw-DOM helper (ExTester's ModalDialog matches buttons
by a `title` attribute VS Code doesn't set on these dialogs); the
environment suite's G2 delete uses the same helper.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* test(e2e): strengthen assertions flagged by review (F1-F3, F6-F7)

A review of the new E2E suites found tests that could pass while the
feature was broken. Harden each so the assertion is causally tied to the
product behavior (verified each suite passes locally):

- initNotebookRunner: the kernel-restart test could pass on the STALE
  `init-ran` output left by the first test (a restart doesn't clear cell
  output until the re-run emits its first output). Clear the output and
  confirm the marker is gone BEFORE restarting, so the reappearing marker
  is provably freshly produced.
- integrations: the "plain notebook has no integration" test only asserted
  the absence of "Sales BigQuery", which passes on a blank/failed webview
  (helper returns ''). Also assert the panel's empty-state text renders,
  proving the integrations UI actually opened.
- snapshots: the notebook-scoped-snapshot tests only checked filenames.
  Read each snapshot's contents and assert it holds only its own output,
  not the sibling's — catching content cross-contamination that
  notebook-scoped filenames alone would hide.
- openSingleNotebook / initOnlyFile: the "does not prompt to split" checks
  could pass when the file never opened (the open failure was swallowed
  into a boolean while the no-prompt assertion still saw `false`). Let the
  open wait throw so a failed open fails the suite loudly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* docs(deepnote): trim redundant comments across the PR

Remove narration/restatement and verbose docblocks a senior engineer
doesn't need, and shorten the remaining "why" comments (ExTester/harness
quirks, workarounds, ordering rationale, gotchas) to <=2 lines. Comments
only — no logic changes; product tsc, compile-e2e, lint, and the changed
modules' unit tests (2440 passing) all pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* test(e2e): add file-watcher suite

Covers DeepnoteFileChangeWatcher's two live-reload paths through the real
VS Code UI (no kernel), which were previously unit-tested only:
- main-file-sync: editing an open notebook's .deepnote on disk re-renders
  the cell source (assert new source present AND old source gone).
- snapshot-output-update: a *.snapshot.deepnote appearing on disk applies
  the saved output to the open cell (assert it was absent, then present).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* refactor(deepnote): update file key handling and clean up comments

- Changed the method of obtaining the file key from `deepnoteFileUri.toString()` to `deepnoteFileUri.fsPath` across multiple instances in `deepnoteServerStarter` to ensure consistency and accuracy.
- Removed redundant comments in `deepnoteEnvironmentManager` to streamline the codebase.
- Updated unit tests in `deepnoteServerStarter.unit.test.ts` and `deepnoteEnvironmentsView.unit.test.ts` to reflect the changes in file key handling.

These changes enhance code clarity and maintainability without altering functionality.

* test(deepnote): stub isServerRunning with sinon instead of manual reassignment

Replace the hand-rolled monkey-patch
`(serverStarter as any).isServerRunning = async () => true` with a proper
`sinon.stub(...).resolves(true)`, restored via `sinon.restore()` in teardown.
The method stays private (reached through an `as any` cast, matching the
file's existing private-access convention) while gaining sinon's call
tracking and automatic restore.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* fix(deepnote): render init-only files as openable leaves and reveal them correctly

- Add isSingleNotebookFile so an init-only .deepnote (whose only notebook is the
  init notebook) renders as a single-notebook leaf instead of a non-openable
  "0 notebooks" node (CodeRabbit #7).
- findTreeItem returns the real leaf ProjectFile node for single-notebook files
  instead of a synthetic Notebook child that reveal() cannot target (#6).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* fix(deepnote): key the tree file caches by uri.toString() for cross-platform consistency

Unify cachedProjects/fileItemCache keys to uri.toString() (matching the watcher
and kernel-selector convention) so file-change invalidation no longer misses on
Windows, where uri.path differs from the native path. context.filePath stays
native for Uri.file() via a lossless round-trip. (CodeRabbit #4)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* fix(deepnote): harden Jupyter import and report partial rename failures

- renameProject warns with the count of files that could not be updated instead
  of a blanket success toast when some sibling writes fail (CodeRabbit #2).
- Jupyter import converts each notebook independently (per-iteration try/catch)
  and reports failures, so one malformed file no longer aborts the batch (#11).
- Pass native filesystem paths to the converter via getFilePath() instead of
  uri.path, fixing imports on Windows (#5).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* fix(deepnote): clean up orphaned sibling files when a split write fails

If a mid-loop write throws, best-effort delete the siblings already written this
run before rethrowing, so a retry's name allocation isn't bumped to a duplicate
by leftover files. (CodeRabbit #3)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* fix(deepnote): harden kernel and snapshot lifecycle

- Guard init runs with a per-kernel in-flight CancellationTokenSource so
  overlapping start events don't run init twice and a restart cancels an
  in-flight run (CodeRabbit #9).
- Replace void fire-and-forget snapshot calls with explicit .catch logging (#10).
- Unregister the old server handle from the provider on environment switch so
  stale entries don't leak (#12).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* fix(deepnote): mention notebook ID in the integrations error message

showIntegrationsUI bails when either the project or notebook ID is missing, so
the message now reads "Cannot determine project or notebook ID". (CodeRabbit #8)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* test(e2e): extract magic delays and dedup shared helpers

- Extract the bare restart-settle delay in initNotebookRunner and the six
  openIntegrationsFor delays into named constants (CodeRabbit #13, #14).
- Move the duplicated readStatusBarText, showView, and notebookCount helpers
  into test/e2e/helpers (statusBar/views/yaml) and import them in the specs (#15, #16).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* test(e2e): assert init-only file renders as an openable notebook leaf

E2E caught that initOnlyFile.e2e.test.ts still asserted the pre-#7 "0 notebooks"
Explorer node. Update it to assert the openable single-notebook leaf that #7 now
renders (label = notebook name, "N cells"). Re-ran the file: 4 passing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* fix(deepnote): unregister the old server handle only after a switch succeeds

Capture the old handle up front and unregister it after
ensureKernelSelectedWithConfiguration succeeds, and only if setup registered a
different handle. A failed or cancelled environment switch no longer strands the
still-selected controller on a dead handle (DeepnoteServerNotFoundError until
reload). Adds rebuildController regression tests for the failed-switch and A->B
paths, and corrects a stale placeholder-test comment.

Fable review finding F1 (regression from the earlier server-handle unregister fix).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* fix(deepnote): resolve the init notebook for init-only leaf commands

Init-only files render as openable notebook leaves, so their Rename/Duplicate/
Export/Delete menus now appear. resolveTargetNotebook falls back to
resolveLeafNotebook (was getNonInitNotebooks[0], empty for init-only), and the
private single-notebook-file test delegates to the shared isSingleNotebookFile
so delete removes the file instead of emptying it. Adds an E2E test that deletes
an init-only leaf and asserts the file is removed with no "Notebook not found".

Fable review finding F2 (gap from the init-only leaf rendering change).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* fix(deepnote): implement getParent so Reveal in Explorer works

TreeView.reveal requires the provider to implement getParent; without it reveal
always rejected and the command fell back to an info toast. Add getParent
(Notebook -> ProjectFile, ProjectFile -> ProjectGroup) keyed to the existing
uri.toString()/projectId caches. Adds an E2E test that reveals the active
notebook and asserts the leaf is selected with no fallback toast.

Fable review finding F3 (the earlier findTreeItem leaf-match was inert without this).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* fix(deepnote): report the accurate imported-notebook count on partial failure

convertJupyterUrisToDeepnoteFiles returns the failure count; both import callers
subtract it and suppress the success toast entirely when every conversion fails
(=== 1 guard). No more "N imported successfully" alongside a failure warning.
Adds unit tests for the partial-failure count and the all-failed case.

Fable review finding F4 (consumer gap from the per-iteration import error handling).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* test(e2e): use NOTEBOOK_NAME in the init-only delete toast assertion

The deleted-toast regex hardcoded "Bootstrap"; build it from NOTEBOOK_NAME so it
can't drift from the fixture name used everywhere else in the test. Runtime-
identical (CodeRabbit nit).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P2CLu8UmD8ceGNyv8u96pQ

* docs(deepnote): trim comments in the latest review-fix changes

Comment-only pass over the last review-response commits: drop test-intent
preambles that restate test names, review-finding codenames (F2/F3), and the
stale implementation pseudocode in the UT-2 doc test; condense the remaining
comments to at most two lines.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T52xcYnMMDH4xV3ERdwjKN

* docs: sweep comments across the full PR diff

Second comment pass over every added/changed line in the PR (multi-agent scan):
remove commented-out code, narration that restates adjacent code or test names,
a planning leftover, and reviewer-oriented framing; condense every kept comment
(incl. e2e suite headers and public JSDoc) to at most two lines; drop plan-section
references (§0/§2/§3/§7) that are unfollowable outside the review.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T52xcYnMMDH4xV3ERdwjKN

* chore: restore preLaunchTask in launch.json

Reverts a local-dev convenience (commented-out Build preLaunchTask) that
should not ship in the PR.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T52xcYnMMDH4xV3ERdwjKN

* chore(cspell): drop the PR's dictionary additions and reword instead

Reverts cspell.json to the base word list. basenames/initmain/Résumé become
filenames, init-main.deepnote, and Cliché in the affected tests ('unparseable'
had no remaining usage); the CI failure ('unregisters') is reworded to 'drops'.
Spell check now passes with zero dictionary changes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T52xcYnMMDH4xV3ERdwjKN

* docs: revert comment churn on unchanged code

Restores base comments everywhere the PR reworded, condensed, or deleted
comments in functions whose code is identical to main (multi-agent audit):
deepnoteServerStarter and IDeepnoteServerStarter JSDoc, serviceAccountValidation,
an integrationWebview call-site note, two explorerView JSDoc blocks, three
kernel auto-selector field comments, and the e2e helpers/constants/helloWorld
trims. Legit doc updates that track real code changes are kept.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T52xcYnMMDH4xV3ERdwjKN

* test(deepnote): type the server-starter test's private access through one seam

Replace the per-site 'as any' casts and eslint suppressions with a structural
mirror of DeepnoteServerStarter's private surface (typed with the real
DeepnoteServerInfo/IDisposable/PendingOperation shapes) behind a single
internals() accessor, following the serverHandles() pattern in the kernel
auto-selector test. Private-method calls, the sinon isServerRunning stub, and
map assertions are now compile-checked; the redundant dynamic vscode imports
are gone. Zero eslint-disable comments remain.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T52xcYnMMDH4xV3ERdwjKN

* test(deepnote): replace force casts with typed seams across the PR's tests

Extend the internals() single-seam pattern to ten more test files: structural
mirrors typed with real production types for private access (explorer view,
snapshot service, tree provider, integration webview), typed builders for VS
Code mock shapes (WorkspaceFolder, FileStat, NotebookDocument/Editor, events),
and satisfies/as-const for data-file literals. Deletes every as any, : any,
as never, and test eslint-disable outside documented ts-mockito seams.

The casts were hiding real defects, now fixed: an assertion on a nonexistent
serializer field (always false), an openFile test whose raw 'ProjectFile'
string never matched the enum so it exercised the early-return path, and
block literals missing the required metadata field.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T52xcYnMMDH4xV3ERdwjKN

* refactor(deepnote): shrink test mirrors — publicize helper, extract buildSnapshotPath

Plan chunk A (items 1,2,5,6,7): make collectNotebookNamesForProject public and
call it directly from tests; move buildSnapshotPath into snapshotFiles.ts as an
exported pure function (path tests now import it; the Run-All spy becomes a
writeFile-URI assertion); drop already-public createSnapshot from the snapshot
mirror; export applyVisualFields and test it as a function, replacing one hollow
tree test and deleting another that only exercised Map.delete; add the missing
tests: no-pending-save guard on document changes, positive SQL env forwarding
into runtime-core startServer, per-branch applyVisualFields coverage, and
InvalidProjectNameError type guarantees.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T52xcYnMMDH4xV3ERdwjKN

* refactor(deepnote): extract ExecutionMetadataTracker from SnapshotService

Plan chunk B (item 3): the tracker owns per-notebook cell-execution state
(counters, per-cell metadata, error, timestamps) behind a typed API; the
service keeps environment capture in its own EnvironmentCaptureState map and
delegates the public getters. captureEnvironmentBeforeExecution seeds the
session via tracker.ensureExecutionState so startedAt keeps meaning capture
time; the Run-All check reads getExecutedBlockCount (number | undefined) so
untracked notebooks can never classify as Run-All; clearExecutionState clears
both maps. The tracker is an optional last constructor parameter (inject +
optional, defaulting internally — no container binding), so tests inject a
shared instance and the 42 private record* mirror sites become plain typed
calls; the snapshot mirror is down to its six accepted members. New direct
tracker suite plus boundary tests: both-sides clear, default-tracker
construction, untracked-notebook Run-All corner, capture idempotence.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T52xcYnMMDH4xV3ERdwjKN

* refactor(deepnote): extract IServerHandleRegistry from the kernel auto-selector

Plan chunk C (item 4): the notebook-to-server-handle map moves behind an
injectable get/set registry (symbol + interface in kernels/deepnote/types.ts,
Map-backed impl, one addSingleton binding), mirroring the DeepnoteServerProvider
precedent. DeepnoteKernelAutoSelector takes the registry as a decorated 17th
constructor parameter and its three former private-map sites use it; the
private field and the tests' serverHandles() cast helper are deleted — the
rebuildController tests seed through the injected registry's public API, and
the success path now also asserts the registry ends up tracking the new
handle. Adds a small direct registry suite.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T52xcYnMMDH4xV3ERdwjKN

* test(deepnote): use deepStrictEqual for tracker summary assertions

Addresses CodeRabbit's nitpick on the ExecutionMetadataTracker suite: compare
metadata.summary as a whole object (repo guideline) instead of field-by-field;
the controlled timestamps make totalDurationMs deterministic, so the full shape
is pinned exactly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T52xcYnMMDH4xV3ERdwjKN

* test(deepnote): replace runtime-core inline mock with a typed mock module

The @deepnote/runtime-core mock lived as an untyped JS string inside
mocha-esm-loader.js, and tests reached its __ helpers through a dynamic
import force-cast to a hand-maintained interface.

Move the mock to src/test/mocks/deepnoteRuntimeCore.ts with exports typed
against the real package (RuntimeCore['startServer'] etc.), so the compiler
catches drift from the actual API. The loader now resolves the bare
specifier to the compiled mock file, so the code under test and the tests
importing the helpers share one module instance — the cast, the mirror
interface, and the async accessor are all gone.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRc87PBXaWnx2WnhQQ842J

* test(deepnote): drive unit tests through public seams, remove internals() mirrors

Replace the hand-written structural-mirror interfaces and their `as unknown as`
internals() accessors with each class's real entry points, so a private rename
can no longer silently break (or vacuously pass) a test:

- server starter: public startServer/stopServer + the recorded runtime-core mock
- integration webview: the real `save` webview message
- snapshot service: real execution events (notifyQueueComplete / changeCellState)
  observed at the public createSnapshot seam, via one shared activation fixture
- explorer view: captured command handlers invoked through handlerFor(Commands.X)

Also make the vscode-mock EventEmitter honor its `disposables` argument so an
activated service's subscriptions are disposed between tests. Drops the private
performSnapshotSave stubs / updateLatestSnapshot spies, hand-built change events,
and three hollow DI-identity tests. The small tree-provider mirror is kept
deliberately — removing it would add more scaffolding than it removes.

Full unit suite green (0 failing); ESLint clean on the touched files.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGod1izfU9yA8ZQfxD5yeZ

* chore(deepnote): server-config handle keying, canonical DeepnoteFile, e2e assertNotNull

Working-tree changes authored outside this task, committed together at the
user's request:

- deepnoteServerUtils: rename the param to deepnoteFileUri and key the server
  config handle by fsPath. Every caller goes through this single helper, so the
  format stays consistent (no hand-built handle strings elsewhere).
- platform types: getProjectForNotebook now returns the canonical DeepnoteFile
  from @deepnote/blocks instead of the local DeepnoteProject alias.
- e2e: add an assertNotNull(value, message) helper and adopt it across the
  suites, replacing `expect(x).to.not.equal(undefined)` + `x!` non-null
  assertions with a single guard that fails loudly and narrows the type.

The project type-checks and the unit suite is green. The e2e suites were not
run here (they require the ExTester/xvfb harness).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGod1izfU9yA8ZQfxD5yeZ

* style(deepnote): reword 'unstubbed' comments to pass spell check

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGod1izfU9yA8ZQfxD5yeZ

* refactor(deepnote): replace inline start function with direct serverStarter calls

Updated unit tests for DeepnoteServerStarter to directly invoke serverStarter.startServer instead of using a separate start function. This change improves clarity and consistency in the test suite by eliminating the intermediate function. All relevant test cases have been updated accordingly to ensure functionality remains intact.

* refactor(deepnote): unify tree item structure with extra property

Refactored the Deepnote tree item structure to encapsulate additional data within an `extra` property, enhancing type safety and clarity. Updated related methods and tests to accommodate this change, ensuring consistent handling of tree item types across the codebase. This refactor improves maintainability and aligns with the new data model for Deepnote items.

* chore(deepnote): remove unused DeepnoteKernelStatusIndicator

The DeepnoteKernelStatusIndicator service and its duplicate .node.ts copy
were added in #152 but never registered in either service registry, so
the service never activated. There are zero references anywhere in the
codebase, and the two copies had drifted (the .ts had status-bar theme
colors and a reveal command the .node.ts lacked). Remove both dead files.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGod1izfU9yA8ZQfxD5yeZ

* refactor(deepnote): replace notebook URI string conversion with getNotebookKey utility

Updated various files to utilize the new getNotebookKey function for consistent notebook key generation. This change enhances code clarity and maintainability by centralizing the logic for obtaining notebook keys, ensuring uniformity across the codebase. Adjustments were made in the DeepnoteLspClientManager, ServerHandleRegistry, and several other components to reflect this update.

* refactor(deepnote): simplify DeepnoteInitNotebookRunner interface

Removed the IDeepnoteInitNotebookRunner interface and updated the DeepnoteInitNotebookRunner class to directly implement IExtensionSyncActivationService. Adjusted service registration in serviceRegistry to reflect this change, enhancing clarity and reducing complexity in the codebase.

* refactor(deepnote): import platform file helpers directly, drop re-export shim

The notebooks-layer deepnoteProjectUtils re-exported readDeepnoteProjectFile
and getNotebookKey from the platform layer. Remove those pass-through
re-exports and point every caller at the canonical platform modules
(deepnoteProjectFileReader, deepnoteProjectUtils) so there is a single source
of truth for reading and keying `.deepnote` files. computeRequirementsHash
stays in the notebooks-layer util.

The obsolete deepnoteProjectUtils.unit.test.ts only exercised the removed
readDeepnoteProjectFile re-export; that behavior is already covered by
deepnoteProjectFileReader.unit.test.ts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGod1izfU9yA8ZQfxD5yeZ

* refactor(deepnote): read snapshot files via readDeepnoteProjectFile

Replace the inline workspace.fs.readFile + TextDecoder + deserializeDeepnoteFile
reads in SnapshotService with the canonical readDeepnoteProjectFile helper, so
snapshot reads go through the same single-source-of-truth parser used
elsewhere. Drops the now-unused deserializeDeepnoteFile import.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGod1izfU9yA8ZQfxD5yeZ

* test(deepnote): count e2e notebooks via schema parse instead of regex

notebookCount now parses the `.deepnote` YAML with deserializeDeepnoteFile
(the same schema-validating parser used to deserialize deepnote files) and
returns project.notebooks.length, rather than counting "- blocks:" lines with
a regex. Verified against the split e2e suite (5 passing).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGod1izfU9yA8ZQfxD5yeZ

* refactor(deepnote): drop deprecated DeepnoteProject alias for DeepnoteFile

Replace every usage of the deprecated `DeepnoteProject` type alias with
`DeepnoteFile` imported directly from `@deepnote/blocks`, then remove the
now-unused re-export from deepnoteTypes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGod1izfU9yA8ZQfxD5yeZ

* fix(deepnote): clone integrations per cached sibling to prevent shared-reference leaks

updateProjectIntegrations assigned the caller-owned integrations array by
reference into every cached sibling, so mutating one cached project — or the
caller mutating its input after the call — leaked across all cached projects.
Deep-clone the array per entry so each sibling is independent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGod1izfU9yA8ZQfxD5yeZ

* test(deepnote): assert exact notebook pair in snapshot save stubs

getProjectForNotebook was stubbed with anything(), anything() across the
snapshot save-path tests, so a wrong project/notebook lookup from the save
path would go undetected. Stub the exact (projectId, notebookId) pair each
test saves, so the notebook-scoped contract is verified and a mismatched
lookup now fails the test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGod1izfU9yA8ZQfxD5yeZ

* test(deepnote): add shared DeepnoteFile fixtures and adopt across tests

Add createDeepnoteFile/Project/Notebook/Block/WorkspaceFolder builders to
deepnoteTestHelpers, each taking a Partial<> of the real type (no casts,
no bespoke options type), and replace the per-file fixture builders that
had drifted across the deepnote unit tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGod1izfU9yA8ZQfxD5yeZ

* refactor(deepnote): extract shared flushNotebookDocumentIfDirty guard

Saves an open, dirty notebook before a disk read-modify-write so the file
watcher's reload cannot clobber unsaved cell edits. Shared so the rename
and integration-write paths can both guard their writes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGod1izfU9yA8ZQfxD5yeZ

* fix(deepnote): save open dirty notebooks before rename to prevent edit loss

renameProject and renameNotebook read each file from disk and rewrote it
without saving an open, dirty document first; the watcher then reloaded the
stale disk content over the live cells and saved it, destroying unsaved
edits. Flush (save) the open document first and abort if the save is
declined, re-reading from disk so the rewrite preserves live content.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGod1izfU9yA8ZQfxD5yeZ

* fix(deepnote): persist integration changes to every sibling file

updateProjectIntegrations only touched cached (open) siblings, so editing
integrations from one notebook silently changed open siblings but left
closed ones stale. Write the new integration list into every sibling
.deepnote file on disk (open or closed), rewriting only project.integrations
and flushing any open dirty sibling first so cells are not clobbered.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGod1izfU9yA8ZQfxD5yeZ

* fix(deepnote): roll back split side effects and report failures accurately

The split's cleanup only covered the child-write phase; failures during
env migration, tab close, backup allocation, or rename left generated
sibling files and partial env mappings behind, and a post-rename failure
still told the user the original was "left unchanged". Track applied steps
and unwind them in reverse on any failure, and derive the message from
whether the rename happened and rollback fully restored.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGod1izfU9yA8ZQfxD5yeZ

* fix(deepnote): scope snapshot discovery to the notebook before the file cap

readSnapshot globbed all of a project's snapshots and capped the result at
200 before filtering for the requested notebook, so once siblings pushed a
project past 200 files the notebook's own latest could be truncated away.
Search the notebook-scoped snapshots first (capped on their own), then fall
back to legacy project-wide files.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGod1izfU9yA8ZQfxD5yeZ

* chore(deepnote): reword the split prompt to flag the legacy layout

The multi-notebook split prompt now states that bundling notebooks in one
.deepnote file is a legacy layout being replaced by one file per notebook,
instead of just describing the file. Sync the unit test's PROMPT_MESSAGE
constant and the e2e suites' SPLIT_PROMPT regex (no longer keyed on the
dropped word "contains") and content assertions to the new copy.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGod1izfU9yA8ZQfxD5yeZ

* fix(deepnote): keep Add Notebook from collapsing the tree and compounding filenames

Two issues on the explorer's "Add Notebook" (and the sibling New/Duplicate
flows) for a project that already spans multiple notebooks/files:

- Filenames accumulated other notebooks' names. `buildSiblingNotebookFileUri`
  derived the new file's stem from `group.files[0]`'s filename, which is itself
  a split sibling (`{base}-{slug}`). A freshly added `{base}-{slug}.deepnote`
  sorts before the source (`-` < `.`), becomes the next `files[0]`, and each add
  appended another slug (`marketing-a-b-c.deepnote`). Now the stem comes from the
  project name (`slugifyProjectName(project.name)`, matching the snapshot-filename
  convention) and never reads a sibling filename, so it can't compound.

- Adding a notebook collapsed expanded groups. The add flows called the full
  `refresh()`, which resets the initial-scan flag and tears the whole tree down
  to a loading node before rebuilding. Switched them to the scoped
  `refreshNotebook(projectId)` (already used by rename/delete), which re-scans and
  picks up the new file without the teardown.

Tests: project-name-based naming and the scoped-refresh behavior are covered by
new DeepnoteExplorerView / factory unit tests; the notebookCommands E2E suite
asserts the exact base filenames.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGod1izfU9yA8ZQfxD5yeZ

* refactor(deepnote): inject DeepnoteTreeDataProvider into the explorer

DeepnoteExplorerView built its own `new DeepnoteTreeDataProvider(logger)` in
the constructor, so a unit test asserting on the provider had to force-cast
`(explorerView as unknown as {...}).treeDataProvider` to reach the private field.

Pass the provider in through the constructor instead (built and handed over by
deepnoteActivationService.ts, which already instantiates the explorer manually).
The parameter is typed as the concrete DeepnoteTreeDataProvider, not the base
`TreeDataProvider<DeepnoteTreeItem>`: the explorer calls refresh/refreshNotebook/
refreshProject/findTreeItem and disposes the provider, none of which are on that
interface. The addNotebookToProject test now injects a ts-mockito
`mock<DeepnoteTreeDataProvider>()` and asserts with `verify(...)`, dropping the
force-cast; the other instantiation sites pass a real provider to preserve
behavior.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGod1izfU9yA8ZQfxD5yeZ

* chore(deepnote): reword split test comment for spell check

Use "Filenames" instead of "Basenames" so the comment passes cspell.

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(deepnote): cover the open-dirty sibling in the project-rename e2e

The project-rename e2e only exercised closed siblings, so the guard that saves
open, dirty notebooks before the rewrite (otherwise the file-watcher reload
clobbers the live cell edits) was unprotected. Extend the existing suite: open
one sibling, type an unsaved marker into its code cell, then Rename Project, and
assert the marker survived on disk alongside the new project name.

Verified as a real regression guard — with the pre-rewrite flush removed, this
new case fails (marker lost) while the existing three still pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGod1izfU9yA8ZQfxD5yeZ

* fix(deepnote): give notebook tree items clearer icons and flag legacy files

The `notebook` codicon renders as a clock face at tree size (verified against the
codicon set in VS Code 1.111 — it's a real glyph, `\ebaf`, not a fallback), so
every `.deepnote` file showed a clock in the Explorer. Replace it:

- Single-notebook files now use `file-code`, matching the notebook children they
  represent — every "notebook you open" looks the same.
- Legacy multi-notebook files use `book` (a container of notebooks), and their
  tooltip gains a "(legacy)" suffix to mark the retired one-file-many-notebooks
  layout.

Icons rendered and confirmed in a headless VS Code 1.111 (folder / book /
file-code, no clock); tree-item and provider unit-test icon and tooltip
assertions updated accordingly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGod1izfU9yA8ZQfxD5yeZ

* fix(deepnote): report integration save from disk truth, not the cache

Saving integrations reported success from the in-memory cache update alone, so a
folderless / out-of-workspace notebook (findFiles covers only open folders) or a
partial sibling fan-out could post "Configuration saved successfully" having
written nothing to the file being edited — integration membership then vanished
on reopen.

persistProjectIntegrations now writes the ACTIVE file explicitly first, then fans
out, and returns { activePersisted, siblingsFailed } judged from disk (not the
cache); the webview only claims success when the active file reached disk and
warns on an incomplete fan-out. persistProjectIntegrations / writeIntegrationsToFile
also take a single params object instead of positional arguments.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AGod1izfU9yA8ZQfxD5yeZ

* fix(deepnote): cap snapshot discovery after ranking, not before

findSnapshotOutputs passed maxResults to findFiles, which truncates an UNORDERED
set to 200 BEFORE ranking — so a notebook with >200 of its own snapshots could
have its `latest` dropped (stale outputs on open), and a sibling flood could push
a notebook's legacy fallback out of the window (no outputs on open).

The glob is now uncapped and the 200 bound is applied to the ranked candidate
list, so the best candidate is always considered and only the read loop is
bounded. Tests honor findFiles' maxResults so a pre-ranking cap is observable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: htt…
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.

0 participants