Files
sessions/pyproject.toml
Myeongseon Choi 836d7e4a73
All checks were successful
boundary-lint / PR boundary-claim (Lint (push) Has been skipped
boundary-lint / duplication-deadline (Layer 1/2) (push) Successful in 19s
boundary-lint / ban-list lint (Lint (push) Successful in 19s
ci / test-health gate (push) Successful in 16s
ci / mutation test (broker) (push) Has been skipped
Release Publish (Gitea session_helper) / verify-release-tag (push) Successful in 17s
ci / rust debug (push) Successful in 2m40s
ci / rust release (push) Successful in 2m45s
ci / python (push) Successful in 1m37s
Release Publish (Gitea session_helper) / publish-linux-x86_64 (push) Successful in 4m21s
fix(sync): mark hydrate / on-demand fetch / format-refresh writes as self-save (v0.7.41)
User trace (v0.7.40 session) revealed a write echo loop that fires
every time a Sessions cache file is opened or its formatter runs:

  23:05:36.236  hydrate file/read writes remote bytes to local cache
  23:05:36.284  local_watcher.change_detected (= our own write)
  23:05:41.619  watcher → _save_remote_file_for_workspace → file/write
                pushes the same bytes back to the remote
  23:05:41.629  ... and re-enqueues sessions_format_then_pipeline_after_save
  23:05:41.646  exec/once for the new formatter run starts
  23:05:41.905  Sublime aborts (formatter in flight)

Root cause: ``_RECENT_SELF_SAVE_REMOTE_PATHS`` (the cooldown the
local watcher uses to filter our own pushes) was only marked in
``_force_overwrite_remote`` and the regular ``file/write`` path —
not in any of the three places where Sessions writes remote bytes
into the local cache. So every cache materialisation looked like a
genuine user edit to the watcher.

Fix: call ``_mark_recent_self_save(remote_path)`` immediately after
each successful ``open_remote_file_into_local_cache`` call:

* ``_apply_hydrate_result`` — sidebar-placeholder hydrate finish path.
* ``_open_remote_file_for_workspace`` worker — Open Remote File +
  on-demand fetch.
* ``_refresh_local_cache_after_format`` — re-download after a
  successful remote formatter run.

The 5-second cooldown is the same one save echoes already use, so no
new tunable. New test
``test_refresh_local_cache_after_format_marks_self_save_for_watcher``
pins the format-refresh leg; the hydrate / on-demand legs share the
identical one-line pattern.

This is not the root cause of the intermittent macOS abort itself
(the same ``pointer being freed was not allocated`` signature
predates the watcher per the user report), but it removes a steady
stream of spurious file/write + format/lint round-trips that was
clearly increasing the FFI traffic surface area on every file open.

A planned follow-up will replace the four overlapping mechanisms
(``_RECENT_SELF_SAVE_REMOTE_PATHS``, ``_track_g_remote_ref_fingerprints``,
``_track_g_local_branch_baseline``, the ad-hoc
``_ON_DEMAND_FETCH_BYPASS`` flag) with a single origin-tagged
last-write-wins log so this whole class of "we wrote it ourselves"
filtering is unified.

1,368 tests pass; coverage 80.69% (gate=80%).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 23:30:14 +09:00

64 lines
1.1 KiB
TOML

[project]
name = "sessions-sublime"
version = "0.7.41"
description = "Sublime-facing Python code for Sessions."
requires-python = ">=3.8"
license = {text = "MIT"}
authors = [{name = "Myeongseon Choi", email = "key262yek@gmail.com"}]
urls = {Homepage = "https://git.teahaven.kr/sublime-rs/sessions", Repository = "https://git.teahaven.kr/sublime-rs/sessions"}
[dependency-groups]
dev = [
"pre-commit",
"pytest",
"pytest-cov",
"ruff",
]
[tool.pytest.ini_options]
testpaths = ["sublime/tests"]
[tool.coverage.run]
source = ["sublime/sessions"]
omit = ["sublime/tests/*"]
[tool.coverage.report]
show_missing = true
skip_empty = true
exclude_lines = [
"pragma: no cover",
"if TYPE_CHECKING:",
"raise NotImplementedError",
]
[tool.uv]
package = false
[tool.ruff]
line-length = 88
target-version = "py38"
[tool.ruff.lint]
select = [
"E",
"F",
"I",
"B",
"D100",
"D101",
"D102",
"D103",
"D417",
]
[tool.ruff.lint.pydocstyle]
convention = "google"
[tool.ruff.lint.per-file-ignores]
"sublime/tests/*.py" = ["D100", "D103"]
"scripts/*.py" = ["D100", "D101", "D102", "D103"]
[tool.ruff.format]
quote-style = "double"
indent-style = "space"