147 Commits

Author SHA1 Message Date
10868231ae docs(planning): PR 18/19 architectural blocker 명시 — PyO3 ADR 의존
All checks were successful
boundary-lint / PR boundary-claim (Lint (push) Has been skipped
boundary-lint / ban-list lint (Lint (push) Successful in 19s
boundary-lint / duplication-deadline (Layer 1/2) (push) Successful in 18s
ci / test-health gate (push) Successful in 16s
ci / rust debug (push) Successful in 1m59s
ci / rust release (push) Successful in 2m4s
ci / python (push) Successful in 1m26s
ci / mutation test (broker) (push) Has been skipped
PR-B (commit 9691726) land 후 main track 이관 saturated:
- PR 18 (H3-queue 본 이관): callable dispatch 가 Python 잔존이라 deque
  본체 이관에 PyO3 callback registry 필요. 부분 이관(dedup state만)은
  critical section FFI cost 대비 가성비 낮음.
- PR 19 (디코더 이관): 현 _parse_*_outcome 은 이미 Rust JSON 받아
  dataclass wrap 만 함. 완전 이관에 C tagged union 또는 PyO3 필요.

둘 다 잔존 쟁점 #8 (PyO3 ADR) 결정에 의존 — 본 plan scope 밖으로
이동. 다음 가시 가치 슬라이스는 Track H2 (commands.py 파일 분할).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 22:34:16 +09:00
32c3e6241a docs(planning): PR-B / PR 17 land 표기
All checks were successful
boundary-lint / PR boundary-claim (Lint (push) Has been skipped
boundary-lint / ban-list lint (Lint (push) Successful in 19s
ci / test-health gate (push) Successful in 17s
ci / mutation test (broker) (push) Has been skipped
boundary-lint / duplication-deadline (Layer 1/2) (push) Successful in 19s
ci / rust debug (push) Successful in 2m10s
ci / rust release (push) Successful in 2m38s
ci / python (push) Successful in 1m21s
PR-B (eager_hydrate apply pass body Rust 이관, commit 9691726) 마감.
PR 18 (H3-queue), PR 19 (디코더), Track H2 가 다음.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 22:21:53 +09:00
951307dd50 docs(planning): PR 14.5d land 표기 — H1 file_open chain 완결
All checks were successful
boundary-lint / PR boundary-claim (Lint (push) Has been skipped
boundary-lint / ban-list lint (Lint (push) Successful in 19s
boundary-lint / duplication-deadline (Layer 1/2) (push) Successful in 19s
ci / mutation test (broker) (push) Has been skipped
ci / test-health gate (push) Successful in 17s
ci / rust debug (push) Successful in 2m5s
ci / rust release (push) Successful in 2m17s
ci / python (push) Successful in 1m26s
Final piece of the H1 file_open chain — the Python wrapper +
``open_remote_file_into_local_cache`` thin Rust call (commit 4c8dcde).

After PR 14.5d:
- file_open transaction fully owned by Rust (broker.request + guard +
  atomic_write all in one call).
- Python only validates workspace root + maps outcome dict to typed
  result.
- 11 transport_cache_mirror tests migrated to mock at the new boundary
  (``_rust_file_open_transaction`` instead of internals).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 17:16:21 +09:00
0832a0cef0 docs(planning): PR 13b.3 / PR 13b.4 / PR 14.5c land 표기 + 후속 인계 갱신
Some checks failed
boundary-lint / PR boundary-claim (Lint (push) Has been skipped
boundary-lint / ban-list lint (Lint (push) Failing after 50s
boundary-lint / duplication-deadline (Layer 1/2) (push) Failing after 50s
ci / mutation test (broker) (push) Has been skipped
ci / rust debug (push) Successful in 2m38s
ci / rust release (push) Successful in 2m32s
ci / python (push) Successful in 1m28s
ci / test-health gate (push) Successful in 18s
본 세션에서 land한 3 PR을 plan 본문에 반영:
- PR 13b.3 (cf74d89) — `RequestEnvelope.timeout_ms` deadline propagation +
  file/read chunked polling (16 MiB 한도 내 256+ checkpoint).
- PR 13b.4 (fd1e5ad) — mirror priority 직렬화 (Arc<Mutex<()>> back-pressure
  로 interactive lane starvation 방지).
- PR 14.5c (a1d70c7) — `run_file_open_transaction` (broker.request → guard
  → atomic_write를 Rust 한 함수로 묶음) + `sessions_file_open_transaction`
  ABI.

PR 13b 시리즈(.1/.2/.3/.4) 4-슬라이스 모두 완결 — Wave 2 envelope 완전
구현(취소·deadline·우선순위) 게이트 통과.

PR 14.5는 14.5(skeleton) + 14.5b(atomic_write helper) + 14.5c(full
transaction) 합산으로 H1 본체 완료. 후속 PR 14.5d는 Python wrapper +
`open_remote_file_into_local_cache` 본체 교체 — 다음 세션 인계.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 16:53:23 +09:00
7329454b90 docs(planning): PR 13b.2 / PR 14.5b land 표기 + 후속 인계
본 세션 추가 commit:
- ae11415 PR 13b.2 — exec/once cancel polling SIGTERM
- e6ab866 PR 14.5b — Rust atomic_write helper + ABI

Plan v1.1 PR 0~16 + cancel infra (PR 13b.1/.2) + H1 atomic write
(PR 14.5/.5b) 까지 본질적으로 완료.

후속 세션 인계 (단일 세션 안전 land 불가):
- PR 13b.3   deadline propagation + file/read chunked polling
- PR 13b.4   priority queue + back-pressure
- PR 14.5c   full Rust file_open transaction (broker request 통합)
- PR 17+     PR-B (mirror BFS body), _rust_ffi 디코더 이관, Track H2

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 16:34:52 +09:00
156c9de347 docs(planning): PR 16c commit hash 반영 2026-05-02 11:23:52 +09:00
a480990c33 chore(boundary): PR 16c — Lint #2 활성화 (PR-A 마무리)
PYTHON_THINNING_PLAN §5 PR 16c. PR 16a/b로 connect SM token + lane
gating Rust 일원화 완료. Lint #2 활성화로 *분리 모듈에 새 deque task
queue 신설 차단*.

scope:
- ``commands_*.py`` (Track H2 분리 모듈)에서 ``_*_TASK_QUEUE = deque(``
  / ``_*_TASK_EVENT = threading.Event(`` 패턴 신설 시 fail.
- ``commands.py`` 본체의 기존 deque (_BACKGROUND_TASK_QUEUE,
  _MIRROR_TASK_QUEUE)는 grandfather — *callable dispatch가 Sublime UI
  thread에 묶여* 있어 (rust-pragmatist 양보 영역) Python 잔존이 합리적.

산출물:
- scripts/lint_python_thinning.py:
  - ``LINT_2_QUEUE_PATTERNS`` (deque/Event 정규식 2종).
  - ``LINT_2_PATH_PATTERN`` (commands_*.py 한정).
  - ``_check_lint_2`` 함수.
  - ``ALL_LINTS`` 에 "2" 추가, main에서 dispatch.
- .gitea/workflows/boundary-lint.yml: ``--lint 2`` 추가.
- planning/PYTHON_THINNING_PLAN.md:
  - PR 15.5/16  표기.
  - Lint 표 #2 활성화 표기.
  - 3차 세션 land 완료 메모.

PR-A 본체 마무리 정리:
- Python module-globals 4종 삭제 (_CONNECT_PREEMPT_LOCK, _CONNECT_GENERATION,
  _CONNECT_INFLIGHT, _SSH_INTERACTIVE_DEPTH_BY_HOST).
- sessions_native::orchestrator 가 connect SM token + in-flight host +
  SSH lane gating의 single source of truth.
- 사용자 원래 불만 ("Python이 너무 두껍다") 가시적 해소 — boundary doc
  M1 정합.
- v0.7.24 ``disciscard``-class 오타: cargo check 가 함수명 typo 컴파일
  시점 차단.

테스트: sublime/tests 1313 + cargo workspace 그린. boundary lint diff
모드 위반 0건.

후속 세션 인계 (단일 세션 안전 land 불가):
- PR 13b.2-.4 — session_helper 동시성 모델 변경.
- PR 14.5b — full Rust file_open transaction.
- PR 17+ — PR-B (mirror BFS body), _rust_ffi 디코더 Rust 이관, Track H2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 11:23:27 +09:00
268477e8a3 docs(planning): 2차 세션 final 마감 — PR 0-15 완료, PR 16은 별도 세션
본 세션 누적 (10 PR / 18 commit):
- PR 9   c19aaae tree/list 잔여 (no-op)
- PR 10  b47f7eb file_state parity tests +26
- PR 11  859c413 file_state kind_codes 통합 (-85 LOC)
- PR 12  92dd66a eager_hydrate parity tests +19
- PR 13a 0d370de Wave 2 envelope spec freeze  게이트 통과
- PR 13b.1 8ac7225 cancel flag map skeleton
- PR 14  e25b866 eager_hydrate BFS → Rust (parity 33 비트 동일)
- PR 14.5 9d6feea atomic write helper (H1 first-PR scope)
- PR 15  06a31b9 인벤토리 정정 (auto-reconnect는 thread 아님)

본 세션 *불가* 이유 + 후속 인계:
- PR 13b.2-.4: session_helper 동시성 모델 변경. PR 16 전제 아님.
- PR 14.5b: full Rust file_open transaction. broker request invocation
  Rust 통합. 회귀 표면 매우 큼.
- PR 15.5 + 16: PR-A 본체. commands.py 600+ LOC + sessions_orchestrator
  crate 신설 + 통합 테스트 3종 + 호출자 일괄 정정 + Lint #2 활성화.
  단일 세션 안전 land 불가능 — cold-start 별도 세션 권장.

테스트: sublime/tests 1313 그린, cargo workspace 그린, boundary lint 0건,
pyright (각 PR scope CLI) 0 errors. 모든 commit pre-commit hook 그린.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 09:16:21 +09:00
06a31b968d docs(planning): PR 15 — 실측 정정 (auto-reconnect는 thread 아님)
PYTHON_THINNING_PLAN §5 PR 15. 코드 변경 없음.

실측 결과 ``sublime/sessions/commands.py:6562-6688`` 의 auto-reconnect는:
- 스레드가 *아니라* Sublime scheduler chain (``_set_timeout(fire,
  delay_s * 1000)``).
- backoff state machine + max_attempts + pending tracking 모두
  module-globals + UI thread 호출.
- ``bridge.request_broken_pipe`` trace event를 listener로 받아 backoff
  scheduling.

따라서 plan v1.1의 "auto-reconnect thread → broker driven" 표현은 stale.
실제 단독 분리는:
1. ``_AUTO_RECONNECT_*`` state를 Rust supervisor로 → PR 16 worker queue
   이관과 강결합 (``_CONNECT_GENERATION`` token 직렬화 invariant).
2. broker-side health probing → broker.rs 개수 thread 추가 + Python
   listener disconnect callback.

(2)만 단독 진행 가능하나, (1)이 따라오지 않으면 reconnect SM이 두 곳에
나뉘어 boundary M1 (single source of truth) 위반. boundary-keeper 4-team
토론 시 ``_CONNECT_GENERATION`` 결합성 발견 — PR 16과 합쳐 한 PR로 land
해야 거버넌스 통과.

따라서 PR 15는 별도 코드 변경 없이 PR 16 본체 슬라이스에 흡수. plan 표
정정.

다음: PR 15.5 (PR-A integration tests) 는 PR 16 *전* land 가능한
``Rust 측`` 통합 테스트인데 ``sessions_orchestrator`` crate 신설을
전제. 따라서 PR 16 본체와 한 PR로 묶음.

본 세션 final 상태:
- PR 0~14.5 (16 commit) 완료.
- PR 13b.2-.4, PR 16 (PR-A 본체 ~600 LOC + 테스트 + Lint #2 활성화)은
  후속 세션 작업 — 사이즈 + 회귀 표면이 단일 세션에 안전 land 불가.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 09:15:05 +09:00
74b9fef98e docs(planning): PR 14 완료 + PR 14.5/15/15.5/16 인계 메모
진행 누적:
- e25b866 PR 14 — eager_hydrate BFS → sessions_native (~50 LOC, parity 33 비트 동일)

본 세션 미land — 사이즈가 크고 회귀 표면이 넓어 안전 land 어려움.
후속 세션 인계:
- PR 14.5 H1 file_open transaction
- PR 15   H3-reconnect (auto-reconnect thread + connect SM token)
- PR 15.5 PR-A integration tests 3종 (테스트-먼저, amend §D)
- PR 16   PR-A 본체 ~600 LOC + Lint #2 활성화

PR 13b.2-.4는 PR 16의 *전제가 아님* — PR 13a envelope spec freeze가
이미 PR 16의 spec drift 가드 역할 수행. PR 14.5 → 15 → 15.5 → 16
직진 가능.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 09:04:56 +09:00
ed9db42d07 docs(planning): PR 13b.1 완료 + 13b.2-.4 인계 메모
진행 현황:
- 8ac7225 PR 13b.1 cancel flag map skeleton

PR 13b 4-way 분할의 첫 슬라이스 land. 나머지 셋(handler abort polling /
deadline propagation / priority+back-pressure) 은 사이즈가 크고 회귀
표면이 넓어 후속 세션 작업으로 인계.

PR 13b.2-.4 완료 후 PR 14 → 16 일직선 진행 가능.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 08:49:52 +09:00
1b70a56037 docs(planning): PR 13a 완료 + PR 13b 분할 가이드 (Wave 2 게이트 통과)
2차 세션 commit 추가:
- 0d370de PR 13a — Wave 2 envelope spec freeze + ref impl

Wave 2 게이트 통과. PR 13b는 사이즈가 크고 회귀 표면이 넓어 본 세션
밖으로 인계. plan §"세션 마감" 메모에 4-way 분할 가이드 추가:

- PR 13b.1 cancel flag map + in-flight task tracking skeleton
- PR 13b.2 handler 별 abort (file/read 등 long-running 우선)
- PR 13b.3 per-request deadline propagation (session_helper:215 청산)
- PR 13b.4 priority / back-pressure

PR 13b 완료 후에야 PR 14 (eager_hydrate 이관), PR 14.5 (H1 transaction),
PR 15 (H3-reconnect), PR 15.5/16 (PR-A) 가능.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 00:34:26 +09:00
0d370dee0b feat(session_protocol): PR 13a — Wave 2 envelope spec freeze + ref impl
PYTHON_THINNING_PLAN §5 PR 13a (Wave 2 게이트). 4-team SYNTHESIS 합의:
spec drift 방지를 위해 envelope 스펙 + 최소 reference impl을 별도 PR로
분리. PR 13a land *후*에야 PR 16 (PR-A 본체)이 envelope 표준에
정합하게 빚어진다는 보장.

산출물:

- rust/crates/session_protocol/src/envelope.rs 신설:
  - ``Envelope { v, channel, kind, body }`` struct (serde Derive).
  - ``Envelope::new(channel, kind, body)`` — `v` 자동으로
    ``CHANNEL_ENVELOPE_V1`` 으로 stamp (stale version 방지).
  - ``Envelope::is_current_version()`` — forward-compat marker 검증.
  - ``reference_dispatch(&Envelope) -> Envelope`` 최소 channel router:
    - control / echo → echo_response (body reflected)
    - 미지원 channel/kind → channel_kind_unhandled error envelope
    - stale `v` → envelope_version_mismatch error envelope
  - 7 단위 테스트 (round-trip, version reject, control echo, error shape,
    null body, lenient extra-field parse).

- rust/crates/session_protocol/src/lib.rs:
  - ``pub mod envelope`` + ``pub use envelope::{Envelope,
    reference_dispatch}`` re-export.

- rust/crates/session_protocol/tests/envelope_parity.rs 신설 (5 테스트):
  - byte-for-byte NDJSON shape pin (4 field 순서 + value).
  - reference_dispatch round-trip / version reject / unknown channel.
  - cross-crate import 경로 검증 (PR 13b/PR 16에서 같은 경로 사용).

PR 13b 후속 (Wave 2 envelope 완전 구현):
- file / exec_once / lsp:* channel handlers 추가.
- per-request timeout / 취소 / 우선순위 / back-pressure.
- session_helper 측 cancellation hook (현재 lib.rs:215 "not yet implemented").

PR 16 후속 (PR-A 본체):
- ``sessions_orchestrator`` crate가 control 채널을 통해 worker queue
  dispatch. envelope shape 정합 보장은 PR 13a 의 reference_dispatch가
  컴파일 시점에 강제.

테스트: cargo test --workspace 그린 (session_protocol 5 신규 + 기존 64
+ envelope.rs 단위 7 + 다른 crate 그대로). clippy 그린 (테스트 시그니처
``Result<(), serde_json::Error>`` + ``?`` 패턴 — workspace
``unwrap_used / expect_used = "deny"`` 정합).

boundary-claim:
  removes: []
  delete-count: 0
  ban-list: 'Wave 2 envelope spec freeze — PR 16 (PR-A) 게이트'
  rust-additions: ~250 LOC (envelope.rs + parity tests)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 00:33:10 +09:00
1035a75d5b docs(planning): PR 9-12 완료 + Wave 2 게이트(PR 13a) 시작점 표기
2차 세션 마감 (2026-05-02). PR 9–12 누적 (커밋 6개):
- c19aaae PR 9   tree/list 잔여 호출자 인벤토리 정정 (no-op)
- b47f7eb PR 10  file_state parity tests +26 (amend §D paired)
- 859c413 PR 11  file_state kind_codes 3중 복제 통합 + decision table (-85 LOC)
- 51dc5c5 plan   PR 11 commit hash
- 92dd66a PR 12  eager_hydrate parity tests +19 (amend §D paired)
- 7114fe8 plan   PR 12 commit hash

Wave 1.5 모든 코드 슬라이스 마무리 (PR 0–12). 다음 세션은
Wave 2 게이트 (PR 13a):
- session_protocol envelope (v/channel/kind/body) 스펙 freeze.
- 최소 reference impl + parity test 1개.
- PR 13a land 후에야 PR 16 (PR-A 본체) 가능 (envelope 정합 보장).

테스트: PR 0–12 누적 sublime/tests 1306 그린 (1268 + parity 26 file_state
+ parity 12 eager_hydrate; 일부는 기존과 중복 카운트라 실측 1306).
boundary lint 위반 0건.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 00:27:31 +09:00
7114fe844d docs(planning): PR 12 commit hash 반영 2026-05-02 00:27:00 +09:00
92dd66a510 test(eager_hydrate): PR 12 — parity tests for BFS/batching/normalize (amend §D)
PYTHON_THINNING_PLAN §5 PR 12. Wave 1.5 amend §D paired parity test PR —
PR 14 (envelope land 후 BFS Rust 이관, ``local_bridge::remote_cache_mirror``
통합) 의 baseline.

새 테스트 19개 (총 33 = 14 기존 + 19 신규):

batched (4 시나리오):
- empty / single / exact-multiple / partial-trailing.

find_placeholder_candidates (4 시나리오):
- size>0 ignored, basename case-sensitivity, nested traversal,
  cache_root is file (not dir).

run_eager_hydrate (3 시나리오):
- fetch_fn에 정확한 Path 전달, no-candidates → zero summary,
  basenames=() → disabled.

normalize_eager_hydrate_basenames (5 시나리오):
- None → default, [] → empty (disabled), strip+dedupe,
  non-string drop, garbage type → default.

Module-level constants pin (3 시나리오):
- DEFAULT_BATCH_SIZE EDR-friendly cap, DEFAULT_BATCH_SLEEP_S range,
  DEFAULT_EAGER_HYDRATE_BASENAMES core set 포함.

PR 14 land 후 본 33개가 *비트 동일하게* 통과해야 한다.

테스트: 37 그린 (parity 19 신규 + eager_hydrate 기존 18; 일부 14에서
추가 보강된 것이 18로 카운트).
plan: PR 12  표기.

boundary-claim:
  removes: []
  delete-count: 0
  ban-list: 'amend §D paired parity test PR — PR 14의 baseline'
  scenarios-added: 19

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 00:26:40 +09:00
51dc5c557b docs(planning): PR 11 commit hash 반영 2026-05-02 00:25:08 +09:00
859c413872 refactor(file_state): PR 11 — kind_codes 3중 복제 통합 + decision 매핑 table
PYTHON_THINNING_PLAN §5 PR 11. Wave 1.5 amend §C single-source-of-truth
양방향 보강 정합. amend A1 (사용자 보이는 문자열 = Python single source) 보존.

변경:
- ``_KIND_CODES`` (4 entries) — module-level constant. RemoteFileKind →
  Rust REMOTE_KIND_* 매핑. 기존 3중 복제 (open guard / reload / save) 제거.
- ``_metadata_to_tuple(meta)`` helper — Rust ABI Optional-tuple 인코딩 단일화.
- ``_OPEN_GUARD_REASON_MAP`` (4 entries) — reason_code → enum 단일 lookup.
- ``_RELOAD_RECOMMENDATION_MAP`` (4 entries) — reload_code → enum 단일 lookup.
- ``_SAVE_CONFLICT_SPECS`` (5 entries) — decision_code → (kind, message,
  reload_hint) tuple. 기존 6단계 if-chain + inline SaveConflict 생성을
  단일 dict + 1줄 unpack 으로 축약. (decision_code 0 / OK 는 inline.)
- ``evaluate_save_file`` 본체 ~50 LOC → ~15 LOC.
- ``open_guard_reason_for_remote_metadata`` 본체 ~12 LOC → ~6 LOC.
- ``reload_recommendation`` 본체 ~30 LOC → ~6 LOC.

amend A1 사용자 문자열 정책:
- ``_SAVE_CONFLICT_SPECS`` 안의 5종 message string 그대로 보존 — Python이
  사용자 보이는 문자열의 single source. Rust ABI는 decision_code (int) 만
  반환 (Lint #4 정합).

테스트: PR 10 parity 33 + sublime/tests 전체 1294 그린.
pyright (file_state.py CLI): 0 errors.

boundary-claim:
  removes:
    - sublime/sessions/file_state.py:open_guard kind_codes/reason_map (~12 LOC)
    - sublime/sessions/file_state.py:reload kind_codes/mapping (~30 LOC)
    - sublime/sessions/file_state.py:save kind_codes + 6 if-branches (~70 LOC)
  delete-count: ~85
  rust-additions: 0  (Python-only — single-source-of-truth 정합)
  ban-list: 'amend §C 양방향 + amend A1 사용자 문자열 Python 보존'

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 00:24:46 +09:00
b47f7eba3b test(file_state): PR 10 — parity tests for evaluate_open/save (amend §D paired)
PYTHON_THINNING_PLAN §5 PR 10. Wave 1.5 amend §D paired parity test PR —
PR 11 (kind_codes 통합 + decision 매핑 lookup table 이관) 의 baseline.

새 테스트 26개 (총 33 = 7 기존 + 26 신규):

evaluate_open_file (9 시나리오):
- DIRECTORY/SYMLINK kind blocked, FILE_TOO_LARGE, size limit boundary,
  zero-byte allow toggle 양방향, NUL byte binary, high ASCII no NUL,
  binary_probe_bytes window 경계.

evaluate_save_file (17 시나리오):
- decision_code 0–5 전체 매트릭스.
- kind_codes 매트릭스: REGULAR_FILE/OTHER 동일 → OK,
  REGULAR→DIRECTORY/SYMLINK kind-specific 우선,
  REGULAR→OTHER 메타데이터 변경.
- size 단독 변경 / mtime 단독 변경 분리.
- baseline=None×candidate=None 경계 (baseline-unknown 우선).
- 사용자 보이는 message 5종 텍스트 핀 (amend A1: Python single source).

PR 11 land 후 본 33개가 *비트 동일하게* 통과해야 한다.

테스트: 33 그린 (parity 26 신규 + file_pipeline 기존 7).
plan: PR 10  표기.

boundary-claim:
  removes: []
  delete-count: 0
  ban-list: 'amend §D paired parity test PR — PR 11의 baseline'
  scenarios-added: 26

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 00:20:39 +09:00
c19aaaef1a docs(planning): PR 9 — tree/list 잔여 호출자 인벤토리 정정 (no-op)
PYTHON_THINNING_PLAN §5 PR 9. 코드 변경 없음.

실측 결과 (grep ``subprocess\.run.*ssh`` / ``subprocess\.Popen.*ssh`` /
``"ls -la"`` / ``"ls", "-la"`` over sublime/sessions/):

- python_interpreter_browser.py:212 ``["ls", "-la", "--", path]`` —
  helper ``exec_once``로 라우팅되는 *원격 명령*. 이미 Wave 1 일원화.
- ssh_runner.py:65 — docstring 문자열만 (실제 호출 아님).

따라서 PR 2 (Wave 1 closure) 시점에 *직접* SSH 폴백 0건 확인.
plan v1.1 §5 PR 9의 "잔여 호출자 정리"는 PR 5.5/PR 8과 동일 패턴 —
인벤토리 stale, 청산 대상 부재.

산출물: PYTHON_THINNING_PLAN.md PR 0-8 진행표에 PR 9  no-op 추가.

다음: PR 10 (file_state parity tests, amend §D 의무).

boundary-claim:
  removes: []
  delete-count: 0
  ban-list: 'plan v1.1 stale 인벤토리 정정'

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-02 00:18:04 +09:00
890bf69de1 docs(planning): PR 0-8 진행 현황 표기 (1차 세션 마감)
PYTHON_THINNING_PLAN.md 헤더에 PR 0~8 완료 표 + plan 인벤토리 정직화
요약 추가. 후속 세션이 PR 9부터 명확한 컨텍스트로 재개 가능하도록.

Plan v1.1 stale 인벤토리 발견 사항 (1차 세션 실측):
- PR 2 bootstrap (~180 LOC): python_interpreter_browser는 사전 일원화 완료.
- PR 5.5 diagnostics parser (~110 LOC): sessions_native::ruff_diagnostics_json
  이미 단일 권한.
- PR 8 cache/ranking (~100 LOC): 캐시는 instance state라 Python 잔존이
  합리. 진짜 후보는 derive_venv_name (~40 LOC).

PR 0~8 누적 (커밋 6개):
- 86d4448 PR 0  governance guardrails
- b11802a PR 1  settings_model normalize
- 322fa26 PR 2  Wave 1 closure + Lint #3
- 2238b55 PR 3-7 _rust_ffi 6-module split
- c29e3f5 PR 5.5 diagnostics inventory rectification (no-op)
- 32fc8ef PR 8  interpreter_probe heuristic

테스트: 1268 그린 (sublime/tests 전체). boundary lint 위반 0건.
pyright (각 PR scope): 0 errors.

다음 세션 시작점: PR 9 (tree/list 잔여 호출자 인벤토리 → 청산 필요 시
진행, 없으면 no-op) → PR 10 (file_state parity tests, amend §D 의무).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 19:50:25 +09:00
c29e3f5995 docs(planning): PR 5.5 — diagnostics 청산 인벤토리 정정 (no-op)
PR 5.5는 plan v1.1의 stale 인벤토리 정정. 실제 코드 변경 없음.

배경: plan v1.1 §5 PR 5.5는 "sublime/sessions/diagnostics.py:225-333
~110 LOC ruff 파서 삭제 → _rust_ffi 일원화"로 명시했으나, 실측 결과:

1. ruff JSON 파싱은 *이미* Rust로 일원화된 상태:
   - sessions_native::ruff_diagnostics_json → _rust_ffi.parse_ruff_diagnostics
   - 호출자 ssh_tool_runtime.py:97이 stdout 직접 Rust로 전달.
2. diagnostics.py:225-333의 함수들 (`_severity_from_loose`,
   `_path_from_helper_dict`, `_message_from_helper_dict`,
   `_position_from_mapping`, `_range_from_helper_dict`,
   `diagnostic_record_from_helper_dict`)은 ruff 전용 파서가 *아니라*
   generic helper dict → typed DiagnosticRecord 변환기.
3. 데이터 흐름:
   (1) ssh exec → ruff stdout
   (2) [Rust] parse_ruff_diagnostics(stdout) → helper dicts
   (3) [Python, generic] diagnostic_record_from_helper_dict → record
   Step 2가 ruff 전용. Step 3은 향후 pyright/다른 source 공유 함수.

따라서 diagnostics.py 본 영역은 정당히 Python 잔존이며 plan의
"청산 대상" 분류는 부정확했음.

산출물:
- planning/PYTHON_THINNING_PLAN.md §5 PR 5.5 항목 정정 (no-op로 명시).
- planning/PYTHON_THINNING_PLAN.md §7 LOC 추정 갱신 (bootstrap 180 +
  diagnostics 110 → 0; PR 2 시점에 이미 helper 일원화 완료 + PR 5.5는
  처음부터 스코프 내 청산 대상 부재였음).
- planning/boundary_inventory.yml diagnostics.py 항목 정정:
  role: split-target → sublime-domain
  notes에 데이터 흐름 명시.

pyright 진단 source 확장 (_rust_ffi.parse_pyright_diagnostics 신설)은
Wave 2 envelope land 후 별도 PR로 진행.

boundary-claim:
  removes: []  # 코드 청산 없음 (plan 인벤토리 정정 전용 PR)
  delete-count: 0
  ban-list: 'plan v1.1 stale 인벤토리 정정'

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 19:45:21 +09:00
322fa26ac8 chore(boundary): PR 2 — Wave 1 closure + Lint #3 활성화
PYTHON_THINNING_PLAN §5 PR 2 — Bootstrap tree/list 청산.

scope 조정 (실측 기반):
- python_interpreter_browser.py는 *이미* helper exec_once 사용 중 (PR 2
  scope에서 코드 청산 불필요).
- ssh_runner.py의 `python3 -c` literal은 *로컬 askpass GUI* (Tkinter)용으로
  원격 무관 — boundary §17-19 위반 *아님*. 모듈 docstring의 stale
  "Temporary bootstrap" 문구만 갱신.
- 진짜 grandfather 위반 1건 (marimo_hosting.py:427 원격 port pick) 발견 —
  별도 슬라이스로 청산 미룸.

산출물:
- sublime/sessions/ssh_runner.py: 모듈 docstring 정정 (helper로 일원화 완료
  명시 + askpass exception 명시).
- scripts/lint_python_thinning.py: Lint #3 활성화. 패턴: `["']python3 -c `
  / `"python3", "-c"`. ssh_runner.py exempt (로컬 askpass 영역).
- .gitea/workflows/boundary-lint.yml: ban-list 단계에 `--lint 3` 추가.
- planning/boundary_inventory.yml: marimo grandfather 위반 등록.

검증:
- diff 모드 (CI 기본): 위반 0건.
- all-files 모드: marimo:427 grandfather 1건 검출 (예상대로).
- ssh_runner.py askpass 패턴은 exempt path로 통과.

boundary-claim:
  removes: []  # 코드 청산 없음 (이미 helper 사용 중인 영역)
  delete-count: 0
  ban-list: '#3 활성화 — 정밀 패턴, marimo grandfather 등록'
  note: |
    plan v1.1 §5 PR 2의 LOC 추정 ~180은 인벤토리 시점 stale.
    실측 결과 코드 청산 영역이 거의 부재 — Wave 1은 PR 2 *이전*에 사실상
    완료된 상태. 본 PR은 Lint #3 거버넌스 가드만 활성화.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 19:28:26 +09:00
86d444885a docs(planning)+ci(boundary): PR 0 — Wave 1.5 governance guardrails
4-team synthesis (rust-maximalist / python-pragmatist / boundary-keeper /
shipping-operator)에서 도출한 Python thinning plan의 첫 슬라이스. 코드 변경
없이 거버넌스 인프라만 활성화 — 후속 PR이 land될 때 mechanical guard로 작용.

- planning/PYTHON_THINNING_PLAN.md: PR 0~16 정식 plan (4축 가중치 + 잔존
  쟁점 8개 결정 표).
- planning/PYTHON_RUST_BOUNDARY.md: amend §A–§M land — 디폴트 거버넌스, 단일
  진실 양방향 보강, parity test 인프라 MUST, thin shim 정량 정의 (≤400 LOC),
  Wave 1.5 + 2.5 신설, Wave 5 일반화, hygiene contract.
- planning/boundary_inventory.yml: Migration inventory 표의 YAML 변환
  (single-source-of-truth, Lint #5 minimal cross-check 데이터).
- scripts/lint_python_thinning.py: ban-list lint #1/#2.5/#4/#6 (PR diff
  기반이라 grandfather 자동 처리).
- scripts/duplication_deadline.py: TEMP_DUPLICATION_UNTIL=vX.Y.Z 마커 만료
  검사 — 만료 시 release 차단.
- .gitea/workflows/boundary-lint.yml: 3 jobs (ban-list / deadline /
  pr-claim) PR + push에서 자동 실행.

uv.lock: pyproject 0.7.25 동기화 (잔재 정리).

Lint 후속 활성화 시점:
- #2 (deque task queue ban) → PR 16 (PR-A 본체) 머지 시
- #3 (python3 -c SSH 폴백 ban) → PR 2 (bootstrap 청산) 머지 시
- #5 (boundary inventory metasync 자동화) → Wave 2.5

Grandfather 위반 2건 (PR diff 기반이라 새 위반만 차단):
- ssh_file_transport.py:1378 _payload_method_label → PR 17+ (디코더 이관)
- commands_python_pipeline.py:639 time.monotonic → Track H2 분리 시

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 19:03:43 +09:00
7b43de90ad refactor(catalog)+feat(settings): excise Track D residue + add LSP-style project-level override
Two threads landing together because they share the
``Sessions.sublime-settings`` header comment edits.

Track D residue cleanup
-----------------------
v0.6.7 dropped the in-Sublime agent integration (Track D, 2026-04-27)
but left install-flow leftovers behind — now removed:

* ``BUILTIN_MANAGED_REMOTE_EXTENSION_CATALOG`` drops the three
  ``kind="agent"`` rows (``tmux``, ``claude-code``, ``codex-cli``) and
  the ``kind="jupyter"`` row (``jupyterlab``, superseded by
  ``marimo_hosting``). Twelve ``_BUILTIN_BASH_*`` install/remove/probe
  blocks deleted. ``managed_remote_extension_catalog.py`` shrinks
  358 → 182 lines.
* ``rust/crates/local_bridge/src/agent_remote_payload.rs`` (279 lines)
  + ``parse-agent-editor-envelope`` CLI subcommand removed — used
  only by the deleted ``agent_proposal_watcher``; verified zero live
  callers.
* Tests: drop ``test_catalog_contains_jupyter_extension_entry`` and
  ``test_catalog_contains_agent_extension_entries``; ``debugpy``
  ``kind="debugger"`` test stays. ``test_settings_model.py`` builtin
  id assertions trimmed to four entries.
* Comments: ``frozen-experimental`` docstring + matching
  ``Sessions.sublime-settings`` block deleted; ``commands.py``
  ``_managed_extension_project_client_keys_for_spec`` example
  jupyter → debugger; Open-Remote-Terminal docstring drops the "no
  tmux session multiplexing" framing; ``marimo_hosting.py`` drops
  dead ``tmux``-children + ``jupyter_hosting.py`` postmortem
  references.
* Planning: ``AGENT_TMUX_LAYOUT.md`` and ``V0_6_5_REPRO.md`` deleted
  (both reference deleted features); ``BACKLOG.md`` Track D entry,
  ``REVIEW_v0_6_4_DISTRIBUTION_PLAN.md`` Stage 4 obsolete +
  follow-up cleanup section, ``PYTHON_RUST_BOUNDARY.md``
  agent_remote_payload row, ``README.md`` Track D bullet — all
  updated to reflect the 2026-04-30 residue removal.

No backward-compat shim. ``debugpy`` ``kind="debugger"`` row
untouched.

LSP-style project-level override for the on-save pipeline
---------------------------------------------------------
The original Sessions design wired toolchain settings with the same
package → user → ``.sublime-project`` precedence Sublime LSP uses,
and ``merge_sessions_lsp_into_project_data`` already follows that
for the ``settings.LSP`` row writer. The on-save toggle path
(``_effective_sessions_settings_for_remote_python`` →
``load_sessions_settings_from_sublime``) skipped the project layer,
so per-workspace toggling required editing global user settings.

Fix: ``_effective_sessions_settings_for_remote_python`` accepts an
optional ``window`` and overlays
``window.project_data().get("settings", {})`` on top of the user
merge for ``sessions_remote_python_auto_diagnostics_on_save``,
``sessions_remote_python_auto_diagnostics_on_open``, and
``sessions_remote_python_tool_pipeline``. New
``_project_settings_block_for_window`` helper tolerates missing
``project_data`` callable / ``None`` payloads / non-mapping values.
Bool keys reject non-bool values silently (fall through to user);
pipeline runs through ``normalize_remote_python_tool_pipeline``.

All five callers in ``commands_python_pipeline.py`` now pass
``window``; the two listeners (``on_post_save``,
``on_activated_async``) reorder window-resolution before the toggle
check so the project block is consultable when the listener fires.

Six new regression tests in ``test_commands.py`` pin
project-overrides-user / user-wins-when-absent / pipeline-override /
wrong-type-rejected / null-project_data-safe / no-window-legacy.

``Sessions.sublime-settings`` header comment now documents the
precedence chain inline so users discover the
``.sublime-project`` ``"settings"`` block path without code-diving.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 23:45:21 +09:00
60a8ad1f0b docs(planning): land Track G v1 bidirectional-sync plan
Bring the post-v0.7.23 audit + redesign of Track G's `.git` sync into
the planning tree as a tracked document. The plan was authored as a
working draft from a code audit + external-tool methodology survey
(Git refspecs, VS Code/Zed remote-dev, Jujutsu's op log, Syncthing
conflict copies); committing it makes the rationale and the phased
delivery (A0 verification → A1 op log → A2 `git bundle` → A3
conflict UI) reviewable alongside the code that will eventually
implement it.

The originally co-authored Track T (Terminus pane survival) section
has been removed from this plan; that fix already shipped in commit
0e2fdd9 (`fix(sublime/terminal): pin stdio to /dev/tty +
auto_close=False`).

Wire it in:

- README planning index links the new file alongside the existing
  PYTHON_RUST_BOUNDARY / VSCODE_REMOTE_TRANSPORT_MODEL / DEEP-RESEARCH
  documents.
- BACKLOG Track G section's v1 scope paragraph points to the plan,
  so contributors landing v1 work see the architecture before
  touching the wipe-and-replace path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 09:50:51 +09:00
3eaa697419 docs(BACKLOG): open Track H — Rust ownership migration plan
The 2026-04 distribution review flagged that the codebase shape is
closer to "Python calls Rust a lot" than "Rust owns the hot paths":
``commands.py`` (7379 LOC), ``ssh_file_transport.py`` (2240),
``_rust_ffi.py`` (1337) still carry runtime ownership Python should
not. Track H captures the ownership-migration plan as three concrete
sub-tracks driven from the existing PYTHON_RUST_BOUNDARY waves:

- H1: ``open_remote_file_into_local_cache()`` → Rust runtime API
       (single biggest single-file ROI; ssh_file_transport target < 1500 LOC).
- H2: ``commands.py`` service split + module-global state reduction
       (commands target < 4000 LOC; first PR extracts the save service).
- H3: background queue / mirror queue / open-file watch / auto-reconnect
       → Rust broker (auto-reconnect thread first; queues in follow-up PRs).

Each sub-track lists its first-PR scope, conflict surface,
done-when, regression test set, and risk + mitigation. Recommended
PR order H1 → H2-save → H3-reconnect → H2-connect → H3-queue → … is
in the dependency graph.

This is plan-only; no implementation lives in this commit. The
implementation PRs come later, off this BACKLOG entry.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 09:32:35 +09:00
3c09ece770 feat(sublime): G4+G6 — branch-switch proxy + dirty refusal (Track G v0 complete)
Track G v0 final piece: when the user switches branches in Sublime
Merge against the local mirror, the remote working tree follows.

New ``sublime/sessions/git_branch_proxy.py``:

- ``install_post_checkout_hook(local_dot_git)`` writes a tiny
  ``sh`` script at ``<.git>/hooks/post-checkout`` that drops a
  ``SESSIONS_PENDING_CHECKOUT`` JSON marker on every local
  checkout. Idempotent re-write detection (don't re-flush identical
  bytes — Sublime's "file changed on disk" reload would otherwise
  fire on every refresh). Marks the file +x on POSIX; harmless on
  Windows where git ignores ``core.fileMode``.
- ``read_pending_checkout`` / ``clear_pending_checkout`` —
  defensive parser. A truncated marker (hook crashed mid-write) is
  treated as "nothing to do" rather than raising.
- ``apply_pending_checkout(host_alias, repo)`` proxies the marker:
  runs ``git checkout <new_head>`` on the remote via
  ``exec/once``. On success the marker is cleared. On stock git
  refusal (dirty working tree, unknown ref, timeout) the marker is
  kept so the user resolves the remote-side state and re-fires
  ``Sessions: Refresh Git State`` to retry. **G6 lives here**: the
  refusal path surfaces ``stderr`` verbatim so Sublime's status bar
  shows the same "Your local changes would be overwritten…" message
  git emitted, with no auto-stash. Path-spec checkouts
  (``branch_flag != "1"``) are silently dropped — the hook fires on
  ``git checkout -- some/file`` too but those don't move HEAD.

``Sessions: Refresh Git State`` flow now does, per repo:

1. Discover (G1) → fetch ``.git`` (G2)
2. Install the post-checkout hook (G4 install step)
3. Drain any pending checkout marker (G4 apply step):
   - If branch switch refused, surface stderr in status bar +
     ``failed`` summary entry, skip materialise so we don't
     re-classify against a HEAD that didn't actually move.
4. Materialise (G3): skip-worktree on clean tracked + content
   pull on dirty.

Two new trace events: ``git.hook_install_failed`` (rare; only on
filesystem permission issues) and ``git.checkout_proxy`` carrying
``proxied / ok / new_head / error_detail`` for post-mortems.

15 new unit tests cover hook write + idempotence + overwrite of
unrelated existing hook, marker round-trip, JSON tolerance,
no-marker no-op, path-spec skip, branch-switch happy path, dirty
refusal (G6), remote timeout, empty new_head sanity, and a real
hook-script smoke test that invokes the installed ``sh`` directly
on POSIX. Full suite 1216 pass.

Track G v0 is now feature-complete: read-only history (v0.7.9) +
staging / commit (v0.7.11) + branch switching (this commit). v1
work (automatic reconcile, refs/ diff fast-path, multi-repo,
submodules, LFS, untracked-not-ignored lazy fetch) is tracked at
the BACKLOG section heading.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 00:06:30 +09:00
372d4882cc feat(rust): cross-platform PersistentBroker (W1) — Windows LSP stdio over Named Pipe
The broker + lsp-stdio relay are now cross-platform. Pre-W1 both
pieces were ``#[cfg(unix)]``-gated despite ``interprocess`` 2.x
already supporting Windows Named Pipes via the same ``GenericFilePath``
resolver — the gates were only there because the original v0.5.x
implementation used ``UnixListener`` / ``UnixStream`` directly. This
patch lifts the gates and swaps the remaining raw Unix calls to the
cross-platform interprocess equivalents.

Concrete changes:

- ``persistent.rs``: ``PersistentBroker`` / ``BrokerAttachRequest``
  / ``BrokerAttachResponse`` / ``handle_broker_client`` /
  ``lsp_response_body_to_framed_string`` are now unconditional.
  ``persistent_broker_endpoint_path`` splits on ``cfg``: Unix returns
  ``$TMPDIR/sessions-local-bridge-<host>-<pid>.sock`` (unchanged
  behaviour); Windows returns ``\\.\pipe\sessions-local-bridge-<host>
  -<pid>``, which is the only form ``GenericFilePath`` accepts on
  Windows. The ``fs::remove_file`` and ``fs::set_permissions(0o600)``
  calls stay ``cfg(unix)`` since named pipes are reaped by the OS
  and don't take POSIX modes.

- ``lsp_stdio.rs``: drop the ``cfg(unix)`` gates from the relay loop
  + the ``run_lsp_stdio`` client, and replace ``UnixStream::connect``
  with ``IpcStream::connect`` resolved via ``GenericFilePath``. The
  ``cfg(not(unix))`` "not supported" stub is gone.

- ``lsp_project_wiring.py``: docstring updated to note that the
  empty-broker_socket case on Windows now means "broker failed to
  start" (rare; e.g. AV blocking the pipe), not "feature not
  implemented". The v0.7.6 ``managed_lsp_enabled`` gate then flips
  ``enabled: True`` on the next handshake once ``broker_socket``
  populates.

- New regression test ``endpoint_path_uses_named_pipe_namespace_on_
  windows`` (gated ``cfg(windows)``) pins the path shape; the
  ``lsp_response_body_*`` tests are no longer ``cfg(unix)`` since
  the function isn't.

Verification: full Linux test suite + Windows cross-compile
(`x86_64-pc-windows-gnu`) build clean; 1165 sublime tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 19:31:23 +09:00
681dbb1553 docs(planning): close Track A — A1 shipped v0.5.7, A2 shipped v0.6.2
All checks were successful
ci / mutation test (broker) (push) Has been skipped
ci / test-health gate (push) Successful in 17s
ci / python (push) Successful in 1m24s
ci / rust debug (push) Successful in 2m2s
ci / rust release (push) Successful in 2m7s
BACKLOG audit found Track A's two items both already landed in
earlier releases:

- A1 (interpreter folder browser): shipped v0.5.7 as
  ``python_interpreter_browser.py`` + the "Browse remote
  filesystem..." quick-panel row. The "autocompletion as you type"
  piece is genuinely separate work and lives under W4.
- A2 (status-bar indicator): shipped v0.6.2 as ``Python: <venv>
  (<X.Y.Z>)`` with version probe + cache + syntax gating. M2 had
  the same done-when; both folded together.

Track A is now strikethrough'd / closed. Active queue narrows to
G (git/SCM) + M3 (extension install latency + auto-format race) +
W1/W4 (Windows parity). Track E stays as reference-only.

No code change needed; just BACKLOG hygiene. v0.7.8 release skipped
since there's nothing to ship — next user-visible version is
whatever lands first from G / M3 / W.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 19:15:35 +09:00
ef5a599563 fix(sublime): per-method timeouts for file/read, file/stat, helper-handshake
v0.7.5 split mirror-sync from the generic 45 s bridge timeout but
left file/read (30 s), file/stat (30 s), and the helper handshake
(60 s) hard-coded. Slow tunnels (AWS SSM, mobile tether) can hit any
of those budgets the same way mirror-sync did, so make all three
overridable from settings:

- ``sessions_file_read_timeout_s`` (default 30)
- ``sessions_file_stat_timeout_s`` (default 30)
- ``sessions_helper_handshake_timeout_s`` (default 60)

New ``_settings_timeout_s(key, fallback)`` helper folds the
``load_settings → get → float → clamp >=1 s`` boilerplate that
``_mirror_sync_timeout_s`` had into one place; each per-method
helper is a one-line wrapper. ``execute_remote_read_file`` and
``execute_remote_stat_file`` keep their existing ``timeout_s`` kwarg
for callers that want an explicit budget; ``timeout_s=None`` (the
new default) reads the setting at call time. ``open_session``'s
60 s hand-shake budget swaps the literal for the helper.

``file/watch`` is intentionally not a setting: its timeout already
varies per-request (``request.timeout_ms / 1000 + 5 s`` slack),
which is the right knob for a long-poll. The 120 s Rust-side
request ceiling stays as an architectural cap.

Tests: three new cases on the helper trio (defaults / override /
clamp / garbage-fallback). Full sublime suite 1165 pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 19:11:59 +09:00
95c3b1fa79 docs(planning): mark M1 retired + M4 dropped after embedded-terminal pivot
Commit 4e81804 (2026-04-27) removed terminal_link_click.py and the
whole Terminus integration as part of the pivot to an external OS
terminal — that retires M1 (Terminus hover, shipped v0.6.10) and
makes M4 (Multiple Terminus panes / plain close) moot since the OS
terminal owns the lifecycle now. Update both entries with the
"[dropped 2026-04-27]" / retired tags so future readers don't
re-propose the same items.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 19:03:13 +09:00
4f0b0ba24c docs(planning): focus BACKLOG on live tracks; drop D/C/B/W2/W3/M6
All checks were successful
ci / mutation test (broker) (push) Has been skipped
ci / test-health gate (push) Successful in 17s
ci / rust debug (push) Successful in 2m1s
ci / rust release (push) Successful in 2m7s
ci / python (push) Successful in 1m19s
User direction 2026-04-27: agents will run in an external terminal
outside Sublime, so the in-Sublime agent-tmux work (Track D) is
dropped wholesale — the v0.6.0–v0.6.7 code stays in the repo as
historical layer but no further sub-tracks ship. Terminus's role is
narrowed to "lightweight execution", which retires Track C (hover
activation + session persist) and the same-shaped Windows mirrors
W2/W3. Track A re-scoped to interpreter UX only — A3 (extension
status label rename) and A4 (.sublime-project pollution) drop; A3's
caching idea is absorbed into M3. Track B drops entirely: B1 merges
into M3, B2 (Cargo.toml hydrate-on-demand) deferred until Track G's
materialisation controller exposes a similar plumbing point. M2 and
M6 closed (M2 shipped in v0.6.2; M6 not blocking — moved to README
territory).

Live queue after this pass: G (next big feature), A1+A2 (interpreter
UX polish), W1 (Windows LSP stdio via PersistentBroker), W4 (folder
browser auto-descend), M3 (extension probe latency + format race),
M4 remainder (plain-close palette wiring), M5 remainder (file/watch
/file/read/helper-launch per-method timeouts). Track E retained for
visibility only.

Diff is mostly deletions: 354 lines down to 84 insertions / 270
deletions. Strikethrough headers + "[dropped]" tags kept as gravestones
so future readers don't re-propose the same items.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 18:51:56 +09:00
e767baf052 docs(planning): mark M5 + V0_6_5_REPRO §B1 fixed in v0.7.5
All checks were successful
ci / test-health gate (push) Successful in 17s
ci / rust debug (push) Successful in 2m1s
ci / rust release (push) Successful in 2m9s
ci / python (push) Successful in 1m16s
ci / mutation test (broker) (push) Has been skipped
Capture the v0.7.5 diagnosis + fix in the two planning docs that
tracked the open repro. M5 status moved to "[mostly shipped]" —
mirror-sync timeout split + auto-refresh backoff + depth default
landed; file/watch / file/read / helper-launch per-method timeouts
still pending but not blocking. V0_6_5_REPRO §B1 records the
2026-04-27 capture that ruled out OOM and channel-buffer hypotheses
and tagged the actual root cause: deep walk at depth 12 over slow
tunnels genuinely runs 45-50 s, just exceeding the generic 45 s
request timeout. Original capture recipe kept in place for future
timeout-shaped repros.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 18:40:27 +09:00
36e6814d87 docs(planning): add Track G — Sublime Merge–compatible git/SCM integration
Captures the design converged in conversation: keep .git real and
fully synced both ways, leave clean tracked files as stubs with
git update-index --skip-worktree, materialise dirty + non-ignored
untracked files on demand. Branch switch flows through a local
post-checkout hook that proxies to remote and re-runs the
materialisation controller; refuses (no auto-stash) when remote-side
dirty files would be overwritten.

Six sub-tracks (G1 discovery, G2 .git sync, G3 materialisation, G4
post-checkout proxy, G5 dirty-set freshness, G6 branch-switch refusal
UX), v0 / v1 scope split, risk register (.git desync, skip-worktree
edge cases on reset/merge/rebase, big-repo initial pull cost), and a
3-agent parallel plan. Out of scope is explicitly called out:
GitLens-style inline blame (Sublime UI primitives don't reach there)
and TUI integration (covered by users picking lazygit instead).

Reading-order prose at the top now mentions Tracks D + G as the two
major feature tracks.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 18:34:44 +09:00
a441ff23c1 feat(file): Sessions: Delete Remote File (lazy-mirror escape hatch)
All checks were successful
ci / rust debug (push) Successful in 2m24s
ci / mutation test (broker) (push) Has been skipped
ci / test-health gate (push) Successful in 16s
ci / rust release (push) Successful in 2m5s
ci / python (push) Successful in 1m19s
The 2026-04-26 v0.6.12 test pass surfaced that local sidebar deletes
intentionally do NOT propagate to the remote — Sessions is a read-mostly
mirror to avoid silent remote-data loss. The asymmetry is correct policy
but undiscoverable without an explicit knob. This commit ships the knob.

* New WindowCommand SessionsDeleteRemoteFileCommand wired into both
  the palette ("Sessions: Delete Remote File") and the Side Bar
  context menu. Resolution priority: explicit ``remote_file`` arg →
  sidebar ``paths`` (reverse-mapped via RemoteToLocalCacheMapper) →
  active view's file_name (also reverse-mapped). Refuses with a clear
  status message when the target lives outside the workspace cache
  mirror so a stray ``foo.py`` from /etc/hostname can never reach the
  bridge.

* Confirmation via ``ok_cancel_dialog``; the dialog text shows the
  fully-resolved absolute remote path so the user can audit before
  acting. Cancel emits ``Sessions: remote delete cancelled.``

* Bridge call: ``execute_remote_exec_once(host, ("rm","-f","--",path))``
  via the persistent bridge's ``exec/once`` channel — no new SSH spawn,
  no new helper RPC method. ``rm -f`` swallows ENOENT so a
  remote-already-gone state still completes the local-side cleanup
  (sidecar + cache copy + open views). Non-zero rm exit (permission
  denied / readonly FS / path-is-directory) keeps BOTH sides intact
  with a ``Sessions warning: remote delete of <path> failed (rm exit
  N): <stderr>`` status message so the user can investigate.

* Trace events for end-to-end auditability:
  ``file.delete.remote_begin`` / ``file.delete.remote_done`` /
  ``file.delete.remote_failed`` / ``file.delete.remote_transport_error``.

* Five regression tests covering: happy path drops cache + sidecar,
  user cancel preserves both sides, non-zero rm refuses local cleanup,
  outside-cache refusal, sidebar context-menu paths argument resolves
  via reverse-mapper.

* planning/TEST_CHECKLIST.md gains §D (new findings from the v0.6.12
  test pass) and §E (verification checklist for the five fixes that
  shipped in 76bdf5b plus this Delete feature). §D records the open
  policy questions (delete propagation, "Refresh Remote Worktree"
  rename, sub-second remote-create vs Save-As race, Terminus URL
  intercept) that need design / data before they can land as code
  changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 00:30:17 +09:00
3590822201 test(rust): cover integration-only files with 78 in-process unit tests
All checks were successful
ci / mutation test (broker) (push) Has been skipped
ci / rust debug (push) Successful in 2m24s
ci / rust release (push) Successful in 2m33s
ci / test-health gate (push) Successful in 16s
ci / python (push) Successful in 1m22s
Replaces the previous coverage-gate workaround (excluding files via
--ignore-filename-regex) with actual tests. The five files that
cargo-llvm-cov can't instrument across process boundaries —
local_bridge/src/{persistent,lsp_stdio,mirror,cli}.rs and
session_helper/src/lsp_child.rs — now have inline #[cfg(test)] mod
tests that hit the same branches the spawned-binary integration
suites cover, plus session_protocol/src/lsp_stdio_framing.rs.

Per file:

- local_bridge/src/cli.rs (18 tests): full argv-parser branch
  coverage for BridgeCliArgs and LspStdioCliArgs - required-flag
  presence, blank-value rejection, missing-value detection, repeated-
  flag last-wins semantics, unknown-flag tolerance vs strict mode,
  --persistent toggle without value.
- local_bridge/src/mirror.rs (10 tests): MirrorSyncParams::from()
  partial / all / none-override conversions exercising every if-let
  arm, JSON deserialization round-trip + missing-required rejection,
  tree_list_entry_to_mirror exhaustive RemoteFileKind mapping +
  is_symlink_loop pass-through, handle_mirror_sync invalid-params
  branch + dispatcher-failure branch (uses /bin/cat-backed
  HelperDispatcher to drive the real wire path without inventing a
  fake transport).
- local_bridge/src/lsp_stdio.rs (11 tests): lsp_transform_message
  edge cases (no-rewrite branch, empty argv short-circuit, non-object
  body defense, method-placeholder fallback, BrokerToLocal direction
  inversion, null cwd serialization), json_insert_optional Some/None
  branches, run_lsp_stdio CLI-parse-failure path + socket-attach
  negative-ack path against an in-process UnixListener.
- local_bridge/src/persistent.rs (11 tests): HelperDispatcher::deliver
  routing / orphan / dedupe semantics, request_blocking success /
  helper-error / fabricated-error / timeout branches with
  synchronized responder threads, persistent_broker_endpoint_path
  shape (per-platform), lsp_response_body_to_framed_string envelope-
  unwrap branches.
- session_helper/src/lsp_child.rs (16 tests): parse_spawn_payload
  exhaustive negatives (non-object, missing argv, wrong-type argv,
  blank-cwd filter, non-string entry filter), normalize_jsonrpc_body
  insert-vs-preserve-vs-noop branches, dispatch_lsp_channel_request
  spawn-required + invalid-spawn + spawn-failed branches + happy
  round-trip via sessions_fake_lsp.
- session_protocol/src/lsp_stdio_framing.rs (13 tests): canonical CRLF
  parsing, LF-only line-ending tolerance, extra-header skipping,
  missing / non-numeric / oversized Content-Length rejection, EOF +
  short-body propagation, invalid-UTF-8 body rejection, write
  byte-format check + write-then-read round-trips.

All test bodies follow the project's clippy hygiene policy: no
unwrap / expect / panic! and no #![allow(clippy::...)] escape
hatches. Negative paths use match + unreachable! for genuinely-
unreachable arms; happy paths return Result and use ?.

Coverage delta: 70.83% -> 80.36% line coverage; the 80% gate now
clears with 0.36% headroom. Function coverage 80.97%. Total Rust
tests: 419 -> 497 (+78), all passing.

Test-health classifier deltas: adversarial 190 -> 196 (+6),
high-value-total 272 -> 278, mock-only:high-value ratio
0.97 -> 0.95.

CI workflow ignore-regex rolled back to its v0.6.11 form
(main.rs + sessions_native only).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 23:37:21 +09:00
c36a3dc24a chore(release): v0.6.12 — Cluster D2 follow-up to v0.6.11 test pass
Some checks failed
ci / mutation test (broker) (push) Has been skipped
ci / test-health gate (push) Successful in 17s
Release Publish (Gitea session_helper) / verify-release-tag (push) Successful in 18s
Release Publish (Gitea session_helper) / publish-linux-x86_64 (push) Successful in 3m54s
ci / rust debug (push) Failing after 2m16s
ci / rust release (push) Successful in 2m25s
ci / python (push) Has been skipped
Critical data-loss guard plus four UX fixes from the 2026-04-26
working session, all backed by regression tests:

- Data-loss guard: brand-new files saved into the cache mirror with
  no metadata sidecar are no longer destroyed by the REMOTE_NOT_FOUND
  branch (both _apply_hydrate_result and SessionsOpenRemoteFileCommand
  now consult _has_remote_metadata_sidecar and preserve local-only
  content).
- Auto-reconnect with backoff (1s->2s->5s->10s->30s, cap 12 attempts):
  subscribe to bridge.rust.collector_error / helper_stdout_eof /
  handshake_recv_timeout and revive the bridge automatically, but
  keep v0.6.11's silent cold-start contract.
- Window reuse on connect / reconnect: existing Sessions windows
  swap project_data in place via set_project_data instead of
  spawning a new window through open_project_or_workspace.
- New file/folder first-time push: helper Missing precondition path
  now runs fs::create_dir_all(parent), so "new folder + new file
  inside" lands without a separate mkdir step. Python skips the
  conflict evaluator's BASELINE_UNKNOWN refusal for first-time
  creates and conservatively refuses blind overwrite of an unfetched
  remote.
- Jupyter timeout 15s -> 60s with SESSIONS_JUPYTER_STARTUP_TIMEOUT_S
  override; timeout error message now includes the cat rc + ssh
  stderr so "last log snippet: ''" is replaced with actionable
  diagnostics.
- tmux list-sessions diagnostics: stop swallowing every non-zero SSH
  exit into an empty list; warn with stderr tail (excluding the
  benign "no server running" path).
- trace_event time-field consistency: commands.py now emits both
  "ts" and "time" fields, matching ssh_file_transport's shape.
- Right-click expand diagnostic trace (expand.invoked /
  sidebar_resolved / quick_panel_deferred) so the wrong-path bug
  from v0.6.11 testing can be diagnosed from the trace log alone.

planning/TEST_CHECKLIST.md is rewritten as a slim v0.6.12 doc
focused on these ship verifications + diagnostic capture for the
still-open issues (#3 LSP race, #6/#7 Terminus URL handling,
#8/#9 tmux empty-list, #12 agent tmux TTY).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 22:40:11 +09:00
ade2e91256 docs(planning): refresh TEST_CHECKLIST to v0.6.11
Some checks failed
ci / mutation test (broker) (push) Has been skipped
ci / test-health gate (push) Successful in 16s
ci / rust debug (push) Failing after 1m54s
ci / rust release (push) Successful in 2m3s
ci / python (push) Has been skipped
Bring the version header, "What's new" preamble, §0 prerequisites,
§8 release verification assets, and §9 known-limitations header
forward to v0.6.11. §8.1 example tag advanced to v0.6.12 to match.

New scenarios:

- §1.5 (NEW, v0.6.11): no auto-spawn on cold start / disconnect.
  Two flavours — (a) Sublime restart with restored ``.py`` view
  focus, (b) mid-session bridge kill (``Broken pipe`` repro). In
  both cases the trace log must NOT contain
  ``bridge.helper_editor_download_*`` /
  ``bridge.helper_ssh_push_*`` / ``bridge.session_spawn`` /
  ``bridge.rust.handshake_ok`` until the user runs
  ``Sessions: Reconnect Current Workspace`` explicitly. Status bar
  still paints the cached interpreter or ``(…)`` placeholder so
  the slot is not blank.
- §1.6 (NEW, v0.6.11): ``sessions_remote_python_auto_diagnostics_on_open``
  opt-in still respects the gate. Even with the flag enabled,
  view focus must not auto-spawn — the same
  ``_workspace_runtime_connected`` check guards the diagnostics
  pipeline listener.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 19:20:46 +09:00
628bc48baf chore(release): v0.6.11 — gate auto-spawn on explicit reconnect
Some checks failed
ci / test-health gate (push) Successful in 17s
ci / mutation test (broker) (push) Has been skipped
Release Publish (Gitea session_helper) / verify-release-tag (push) Successful in 18s
ci / rust debug (push) Failing after 2m9s
ci / rust release (push) Successful in 2m17s
ci / python (push) Has been skipped
Release Publish (Gitea session_helper) / publish-linux-x86_64 (push) Successful in 3m3s
Patch release shipping the fix from 7ca1dbc.

User report (2026-04-26 debug-trace.log): after a Sublime restart of a
restored Sessions project window, the bridge was being spawned without
any explicit reconnect command. Trace evidence at 12:59:42–13:46:32:

- 12:59:42  mirror-sync ``Broken pipe`` → bridge dead.
- 13:00:21  ``sessions.probe_python_version`` enqueued, helper download
            begins.
- 13:46:29  same probe re-enqueued; helper SSH push, ``session_spawn``,
            ``handshake_ok``, then ``lsp.managed_server_restart`` × 3
            for LSP-pyright / LSP-ruff / rust-analyzer.

The probe path was triggered by
``SessionsPythonInterpreterStatusListener.on_activated_async``: every
time a restored ``.py`` view came into focus, the listener saw a cold
version cache and scheduled ``_probe_active_python_version_task``,
which calls ``execute_remote_exec_once`` →
``_persistent_bridge_for_host(allow_spawn=True)`` and revives SSH
unconditionally. The other on-activated / on-load callbacks (sidebar
placeholder hydrate, LSP workspace activation tracer, active-remote
view revalidate) already gate this with
``_workspace_runtime_connected``; this listener (and its sibling
``SessionsRemotePythonPipelineListener.on_activated_async``) was the
straggler.

The fix in 7ca1dbc adds the same gate to both. After a restart the
status bar still paints the cached interpreter or the ``(…)``
placeholder so the slot is not blank, but no SSH is touched until the
user runs ``Sessions: Reconnect Current Workspace`` explicitly. The
next view activation after reconnect fires the probe and the version
fills in.

CI (``.gitea/workflows/upload-session-helper-gitea.yml``, ``tags: v*``
trigger) imports the GPG signing subkey from secrets and publishes
the signed bundle.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 15:16:49 +09:00
c906f1021c docs(planning): refresh TEST_CHECKLIST to v0.6.10
Some checks failed
ci / mutation test (broker) (push) Has been skipped
ci / test-health gate (push) Successful in 16s
ci / rust debug (push) Failing after 1m58s
ci / rust release (push) Successful in 2m8s
ci / python (push) Has been skipped
The doc was last revised for v0.6.4. Bring the version header,
"What's new" preamble, and §0 prerequisites forward to v0.6.10.

New scenarios:

- §3.2 hover URL: add v0.6.5 (``0.0.0.0`` canonicalization +
  trailing ``/`` so Cmd+click no longer lands on ``about:blank-``)
  and v0.6.10 (ANSI/VT100 escape strip + relative-path resolution
  against the workspace mirror via ``RemoteToLocalCacheMapper``;
  structured ``terminal_link.hover.*`` / ``terminal_link.click``
  logs at every decision point).
- §3.4: marker bumped to v0.6.2 / v0.6.5 — v0.6.5 finally registered
  ``Sessions: New Remote Terminal Pane`` /
  ``Kill Remote Terminal`` in ``sublime/plugin.py``; the v0.6.2
  ``.sublime-commands`` rows alone weren't enough.
- §3.5 (NEW, v0.6.6): ``Sessions: Attach to Tmux Session`` palette
  command — read-only attach to any remote tmux session
  (Sessions-owned or foreign); foreign sessions never enter the
  per-host caches so existing Open / New / Kill flows stay scoped.
- §3.6 (NEW, v0.6.10): ``sessions_terminal_close_default`` setting
  documented — internal API ships, palette wiring + on-pane-close
  listener still pending; flag has no visible effect yet.
- §3.7 (NEW, v0.6.5): ``sessions_show_dev_commands`` gate hides
  developer-only palette entries (first gated:
  ``Sessions: Preview Remote Agent Payload``).
- §7.1: marker bumped to v0.6.2 / v0.6.5 — v0.6.5 closed the
  remaining ``open terminal failed: not a terminal`` holes
  (``ssh -T`` + ``</dev/null``).

§8 release verification points at v0.6.10 assets. Tag-signature
expectation split per release: ≤ v0.6.9 are subkey-signed and
``git tag -v`` should show "Good signature"; ≥ v0.6.10 may be
plain annotated tags because CI signs the artifacts via the
dedicated subkey on tag push (see Fix-→tag-→release memory).

§9 known limitations: D7 Phase 1 / Phase 2 marked **abandoned**
(not deferred) — the supporting modules
(``agent_proposal_watcher`` / ``agent_change_badge``) and their
tests were deleted in v0.6.7 along with the chat→tmux pivot away
from diff-centric review. New entries cover the v0.6.10 deferred
palette wiring for close-terminal and the macOS hover box-vs-underline
theme caveat.

(v0.6.7 / v0.6.8 / v0.6.9 are pure clean-code refactors / dead
code retirement — no behavior change to test, mentioned only in
the "What's new" preamble.)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 14:04:01 +09:00
c066cb9962 chore(release): v0.6.10 — Polish-track batch + diagnostic instrumentation
Some checks failed
ci / mutation test (broker) (push) Has been skipped
ci / rust debug (push) Failing after 2m15s
ci / rust release (push) Successful in 2m24s
ci / python (push) Has been skipped
ci / test-health gate (push) Successful in 17s
Release Publish (Gitea session_helper) / verify-release-tag (push) Successful in 18s
Release Publish (Gitea session_helper) / publish-linux-x86_64 (push) Successful in 3m40s
Terminal hover (M1, shipped):
- ``terminal_link_click`` now strips ANSI/VT100 escapes at
  ``classify_terminal_token`` entry so abspath / URL detection works
  against ANSI-coloured ``ls`` output (allocation-free fast path when
  no ``\x1b`` is present).
- New ``_RELPATH_PATTERN`` + ``_resolve_relpath_in_cache(view, token)``
  resolve relative tokens against the workspace mirror via
  ``RemoteToLocalCacheMapper`` — only marked clickable when the local
  cache file actually exists; directory / ``..`` traversal /
  no-context cases all safely fall through.
- Click handler routes ``relpath`` outcomes through
  ``_handle_local_path`` directly (no bridge round-trip needed).
- Structured ``terminal_link.hover.*`` / ``terminal_link.click`` logs
  at every decision point now name ``matched_kind`` / ``matched_text``
  / ``resolved_target`` / ``action`` / ``outcome`` /
  ``source=hover_cache|reclassify`` so the next macOS Cmd+click
  "paint OK / click silent" repro can be diagnosed from logs alone.

Terminal close (M4, partial):
- ``terminal_tmux_session.close_terminal_session(host, name, *, kind)``
  unifies the three close paths via ``TerminalCloseOutcome``:
  ``"plain"`` = ``tmux kill-session`` (non-persistent default-on-pane
  close), ``"kill"`` = same SSH effect but explicit user action
  (palette command path), ``"detach"`` = current default (no SSH
  call, session persists).
- New ``sessions_terminal_close_default`` setting accepts
  ``"detach"`` (default, current behavior) or ``"plain"``;
  ``"kill"`` deliberately excluded from default policy
  (regression-guarded).
- ``kill_terminal_session()`` retained for backward compat —
  ``close_terminal_session("plain"|"kill")`` delegates so
  session-name validation + argv shape stay in one place.
- Palette wiring (new ``Sessions: Close Remote Terminal (don't
  persist)`` command) + on-pane-close listener that reads the new
  setting are deferred to a follow-up commit; the internal API in
  this release is feature-complete and tested but not yet
  user-reachable from the palette.

Diagnostic instrumentation (no behavior change):
- ``jupyter_hosting.build_notebook_url`` logs ``local_port`` +
  ``notebook_path`` at entry.
- ``commands_python_pipeline._open_remote_jupyter_in_browser`` logs
  the constructed URL right after ``build_notebook_url``, again at
  ``finish()`` entry, again immediately before ``webbrowser.open``,
  and ``.exception(...)`` inside the previously-silent
  ``except Exception: pass`` branch — pinpoints which step swallows
  the open in the slow-link "queue.done elapsed_ms=27748 but no
  browser tab" repro.
- ``ssh_file_transport._execute_rust_bridge_request_persistent``
  extracts ``payload_bytes`` + ``params.max_traversal_depth`` from
  the request payload and threads both into the
  ``bridge.request_timeout`` trace event, so the mirror-sync
  deep-traversal hang
  (``stall_phase=awaiting_response_dispatch`` after 45s) reports the
  depth + payload size that hit the timeout.
- ``sessions_native::broker::dispatch_response_line`` gains an
  env-gated (``SESSIONS_BROKER_DISPATCH_DEBUG``) stderr trail
  covering enter / parse-OK / parse-FAILED / no-id-drop /
  id-and-slot-presence — zero noise unless explicitly enabled,
  drained through the existing ``STDERR_TAIL_CAPACITY=100`` ring so
  ``_rust_ffi.stderr_tail(host_alias)`` surfaces the trace
  post-repro.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 13:33:28 +09:00
68d2ffc939 refactor(local_bridge): broker server uses interprocess (W1 foundation)
All checks were successful
ci / python (push) Successful in 1m28s
ci / rust release (push) Successful in 2m8s
ci / mutation test (broker) (push) Has been skipped
ci / test-health gate (push) Successful in 16s
ci / rust debug (push) Successful in 1m59s
PoC validation step for the Windows W1 PersistentBroker port. Swap
the broker server side from std's ``UnixListener`` / ``UnixStream``
to ``interprocess`` 2.4.2's cross-platform ``LocalSocketListener``
(``IpcListener``) / ``Stream`` (``IpcStream``) abstraction. Behind
the abstraction the Unix path is byte-for-byte unchanged at the OS
level — same ``AF_UNIX`` socket file at
``/tmp/sessions-local-bridge-<host>-<pid>.sock`` with the same
``chmod 0600`` permissions hardening (kept inline as a
``#[cfg(unix)]`` block); on Windows the same code opens a Named Pipe
at ``\\.\pipe\sessions-local-bridge-<host>-<pid>``.

Why this matters: the maintainer flagged (2026-04-25) Windows as the
likely largest user fraction, making BACKLOG W1 (PersistentBroker
for Windows, currently a no-op blocker on the Sessions-managed LSP
attach path) a real product gap. ssh-mux
(https://git.teahaven.kr/Rust-related/ssh-mux) ships a battle-tested
named-pipe IPC implementation we can crib hardening patterns from.
This commit lands the foundation: cross-platform listener, ungated
``PersistentBroker`` struct, ``IpcStream`` parameter types in
``handle_broker_client`` + ``broker_lsp_relay_loop``. The
``run_lsp_stdio`` client side stays on ``UnixStream::connect`` for
this PoC scope, and the broker call site at ``main.rs:240`` stays
``#[cfg(unix)]``-gated so Windows builds still report an empty
``broker_socket`` — no behavior change on Windows yet.

Verified on macOS / Linux:
- ``cargo test --workspace`` green (all 19 test binaries).
- ``cargo clippy --all-targets -- -D warnings`` green.
- ``pytest sublime/tests/`` green (1432 tests).
- ``cargo build --release`` size delta < 50 KB.

planning/REVIEW_v0_6_4_DISTRIBUTION_PLAN.md
- §2.6 Windows W1 status: ``[plan]`` → ``[plan-MVP, foundation
  laid]`` with concrete next steps:
  1. Ungate broker call site in ``main.rs:240``.
  2. Cross-platform ``run_lsp_stdio`` client
     (``UnixStream::connect`` → ``IpcStream::connect``).
  3. Verify ``\\.\pipe\...`` survives Sublime project-file JSON
     round-trip.
  4. Windows test pass: pyright LSP attach reaches handshake.
- "Open questions" — Windows W1 entry resolved; MVP estimate
  3-4 days, optional hardening (anti-squatting + DACL via
  ssh-mux patterns) ~1 week.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 19:42:50 +09:00
2aeedd4cc4 chore(release): v0.6.7 — retire dead chat-with-diff modules
All checks were successful
ci / mutation test (broker) (push) Has been skipped
ci / test-health gate (push) Successful in 17s
Release Publish (Gitea session_helper) / verify-release-tag (push) Successful in 18s
ci / rust debug (push) Successful in 2m18s
ci / python (push) Successful in 1m28s
Release Publish (Gitea session_helper) / publish-linux-x86_64 (push) Successful in 3m36s
ci / rust release (push) Successful in 2m25s
Bumps workspace 0.6.6 → 0.6.7. Bundles the dead-diff retirement
(commit e666e91) into a release tag.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 19:23:00 +09:00
e666e914f9 chore: retire dead chat-with-diff agent modules
Maintainer signoff (2026-04-25): the chat→tmux pivot abandoned the
diff-centric review direction. The diff primitives that survived the
pivot have no live caller in the agent flow and were carrying test
weight (56 tests across 3 files) for an undefined product surface.
git history preserves them for anyone who wants to revive the diff
direction later.

Removed
- ``sublime/sessions/agent_proposal_watcher.py`` (290 LOC) — pure-Python
  unified diff parser; was meant to tail ``tmux pipe-pane`` output.
- ``sublime/sessions/agent_change_badge.py`` (248 LOC,
  ``AgentChangeBadgeRenderer``) — post-apply phantom badge renderer.
- ``sublime/tests/test_agent_proposal_watcher.py`` (20 tests).
- ``sublime/tests/test_agent_proposal_watcher_adversarial.py`` (10).
- ``sublime/tests/test_agent_change_badge.py`` (26).

Updated
- ``sublime/sessions/agent_tmux.py``: docstring no longer points at the
  retired companion module; new line documents the historical context
  of the chat→tmux pivot so future readers know why the broker is
  agent-agnostic.
- ``planning/REVIEW_v0_6_4_DISTRIBUTION_PLAN.md``: "Code to consider
  retiring" → "Code retired (post-direction-correction)" with the
  test-health gate numbers post-deletion. Closed two of the three
  open questions (rm signoff and Linux-only sublime-package decision —
  defer the latter, no Linux-only release asset for now).

Test-health gate stays green: 1432 sublime tests pass; adversarial 190
(floor 184), real-subprocess 55 (floor 53), contract-fixture 27 (floor
27), mock-only:high-value 0.95 (cap 0.98).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 19:22:12 +09:00
4bcf1636ea chore(release): v0.6.6 — Sessions: Attach to Tmux Session
All checks were successful
ci / rust release (push) Successful in 2m20s
ci / mutation test (broker) (push) Has been skipped
ci / test-health gate (push) Successful in 17s
Release Publish (Gitea session_helper) / verify-release-tag (push) Successful in 17s
ci / rust debug (push) Successful in 2m14s
Release Publish (Gitea session_helper) / publish-linux-x86_64 (push) Successful in 3m18s
ci / python (push) Successful in 1m28s
Bumps workspace 0.6.5 → 0.6.6. Bundles the foreign-tmux attach feature
(commit 358d674) into a release tag plus the plan direction
correction that cleared the way for it.

CI on tag v0.6.6 will produce the signed bundle via the dedicated
signing subkey (master never touches the runner) and upload assets
to the release page + musl session_helper to the generic registry.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 17:16:10 +09:00
358d674f3d feat(terminal): attach to foreign tmux sessions + plan direction fix
The maintainer flagged two divergences between the external review's
prioritization and the actual product direction (2026-04-25):

1. #29 diff-centric review/apply was abandoned when the agent UI
   pivoted from chat-with-diff to tmux session passthrough — agents
   run in a tmux pane, edit remote files directly, and Sessions's
   job is multi-session lifecycle (spawn/switch/kill), not diff
   orchestration. The diff primitives that survived the pivot —
   `agent_proposal_watcher` (290 LOC), `agent_change_badge`
   (248 LOC) — are dead code from the abandoned design.
2. The persistent-terminal flow always opens `sessions-term-<host>`,
   so a user with their own `tmux new-session -A -s work` on the
   remote can't reach it via the palette. The "single Sessions-owned
   tmux session per host" model is too narrow.

Plus environment constraints: cross-platform CI runners aren't
available; code-signing budget (~$600/yr) is out of scope.

Code: Sessions: Attach to Tmux Session
- New `list_all_remote_tmux_sessions` in `terminal_tmux_session.py`,
  sibling of `list_terminal_sessions` but without the SESSION_NAME_PREFIX
  filter — returns Sessions-owned sessions alongside foreign ones.
- New `SessionsAttachRemoteTmuxCommand` in `commands.py` + companion
  `_attach_remote_tmux_session` helper that opens a Terminus pane via
  `ssh -tt <alias> tmux attach-session -t <name>`. Read-only attach
  semantics: foreign sessions never enter the Sessions-owned per-host
  / per-session view caches (so kill / new-pane flows can never
  reach into a foreign session by accident).
- Quick panel rows distinguish `Sessions-owned` vs `foreign` tmux
  sessions in the description column so the user knows what they're
  attaching to.
- Existing `Sessions: Open Remote Terminal` / `New Remote Terminal
  Pane` / `Kill Remote Terminal` stay scoped to `sessions-term-*`
  unchanged.
- Wired through `plugin.py` import + `__all__`; entrypoint smoke +
  runtime-import smoke updated; new palette entry in
  `Sessions.sublime-commands`.

Tests
- 6 new tests for `list_all_remote_tmux_sessions` (no-filter,
  empty-server, missing-tmux, timeout, oserror, blank-line stripping).
- 5 new tests for the attach command (palette listing including
  foreign sessions, terminus_open argv shape, foreign-session does
  NOT register in the Sessions-owned caches, empty-list status hint,
  tmux-missing status hint).
- Entrypoint + runtime-import smoke tests updated for the new export.
- 1488 sublime tests pass (was 1477).

Plan: planning/REVIEW_v0_6_4_DISTRIBUTION_PLAN.md
- New "Direction correction (post-review, 2026-04-25)" section
  documents the chat→tmux pivot + the dead diff modules + the
  blocked environment constraints.
- §2.1 was "#29 diff-centric MVP, highest priority" → replaced with
  "Tmux session discovery + attach to foreign sessions" (this
  commit's feature). #29 moves to "Items DEPRIORITIZED / dropped".
- §1.4 cross-platform smoke + signing → `[blocked-by-environment]`
  with documented "feasible without runners/certs" guidance.
- §1.1 updated to reflect Linux-only first iteration is feasible
  now; full matrix waits on §1.4.
- New "Code to consider retiring" section catalogues
  `agent_proposal_watcher.py` + `agent_change_badge.py` + their
  tests as removal candidates pending maintainer signoff.
- "Open questions" surfaces three decision points: retire-or-archive
  the dead diff modules, ship Linux-only sublime-package now or
  hold, and Windows W1 priority given the macOS-primary workflow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 17:15:15 +09:00
57523033a0 docs(plan): fix § 1.1 status — sublime-package CI wiring is plan, not partial
All checks were successful
ci / mutation test (broker) (push) Has been skipped
ci / test-health gate (push) Successful in 17s
ci / rust debug (push) Successful in 1m55s
ci / rust release (push) Successful in 2m3s
ci / python (push) Successful in 1m26s
The earlier rewrite labelled the immediate-CI-step sub-item as
`[partial]` even though no CI step has actually been added; the
script exists in scripts/ but isn't called from any workflow.
Downgrade to `[plan]` and add the third missing piece (sign script
needs to learn --extra-asset so SHA256SUMS covers the .sublime-package).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 16:47:14 +09:00
5483e35b7b docs(plan): full distribution-readiness rewrite from external review
Some checks failed
ci / test-health gate (push) Successful in 16s
ci / python (push) Has been cancelled
ci / rust debug (push) Has been cancelled
ci / rust release (push) Has been cancelled
ci / mutation test (broker) (push) Has been skipped
User restored the original review.md (it had been lost mid-session).
Rewrites the plan with all three review layers fully captured, and
fixes a stale README claim the review surfaced.

planning/REVIEW_v0_6_4_DISTRIBUTION_PLAN.md
- Rewritten end-to-end. Three layers:
  - Layer 1 — five distribution must-haves (sublime-package +
    binaries, safe-by-default, experimental gating, cross-platform
    smoke + signing, release discipline).
  - Layer 2 — feature priority before adding breadth: #29
    diff-centric review MVP, #32 large-file streaming, sync-mode
    product feature, probe TTL cache + hash-based self-write
    suppression, .sublime-project LSP command leakage, Windows W1
    PersistentBroker, slow-link timeout/backoff.
  - Layer 3 — Python ↔ Rust ownership migration (review's strongest
    architectural critique: "Rust로 많이 옮겼다"가 아니라 "Rust를
    많이 호출한다"). Stops helper-level FFI growth; lays out four
    ownership stages (broker, materialization, envelope, agent
    state) + non-LOC success metrics + concrete migration order.
- Each item has [done @ commit] / [partial] / [plan] /
  [needs-input] status, acceptance criteria, file/code pointers.
- "Items DEPRIORITIZED" section: more agent types, more palette
  commands, big LSP redesign, wrapper-level migration that doesn't
  move ownership. Pause-check list before starting new work.
- "Already shipped from this batch" cross-links the four batch-3
  fixes from 7793879 + the two immediate fixes from 280d105 + this
  README correction.
- "Open questions" surfaces three decision points needing maintainer
  input.

README.md
- Remote LSP track (#34/#35/#36/#37) all closed: move from "Open
  milestones" / "next implementation" to "Completed milestones".
  Phase 9 (#10, #29 diff-centric, #32 large-file streaming) now the
  sole open milestone, with explicit issue annotations.
- P0.5 stabilization is closed; collapse from "Done / In progress"
  bullets into a single "closed" line.
- Cross-link the new distribution plan from the execution-order line
  so contributors find the prioritization reasoning.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 16:46:21 +09:00
99f9076af8 chore(release): v0.6.5 — macOS batch-3 + repro doc
All checks were successful
ci / mutation test (broker) (push) Has been skipped
ci / test-health gate (push) Successful in 17s
Release Publish (Gitea session_helper) / verify-release-tag (push) Successful in 17s
ci / rust debug (push) Successful in 2m15s
ci / rust release (push) Successful in 2m22s
Release Publish (Gitea session_helper) / publish-linux-x86_64 (push) Successful in 3m9s
ci / python (push) Successful in 1m21s
Bumps workspace 0.6.4 → 0.6.5. Bundles the four batch-3 fixes from the
v0.6.4 macOS test pass (commits 7793879, 280d105) into a release tag
plus a focused repro checklist for the next test pass.

Includes
- 7793879  fix(0.6.5): macOS batch-3 — agent tmux, palette, hover URL,
           status doc
- 280d105  chore: distribution-readiness review — plan + immediate fixes

planning/V0_6_5_REPRO.md (new)
- Narrow checklist (vs the full TEST_CHECKLIST.md): four "verify the
  fix landed" steps for the batch-3 items + four "capture diag"
  steps for the still-open issues (mirror-sync deep hang at
  awaiting_response_dispatch, hover absolute path open silent,
  Jupyter open silent launch). Includes the exact log fragments to
  paste back so the next debug round starts with concrete signal.

CI on tag v0.6.5 will produce the signed bundle via the dedicated
signing subkey (master never touches the runner) and upload assets to
the release page + musl session_helper to the generic registry.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 16:36:12 +09:00