Skip to content

[codex] Persist project SSH terminal sessions#300

Draft
rikohomeless wants to merge 4 commits into
ProverCoderAI:mainfrom
rikohomeless:issue-286
Draft

[codex] Persist project SSH terminal sessions#300
rikohomeless wants to merge 4 commits into
ProverCoderAI:mainfrom
rikohomeless:issue-286

Conversation

@rikohomeless
Copy link
Copy Markdown
Contributor

Summary

  • persist project SSH terminal sessions under each project .orch/state/terminal-sessions.json
  • hydrate durable sessions for list/get/global lookup and websocket attach after API restart
  • run project SSH terminals through deterministic tmux sessions and install tmux in generated project images
  • update API/web/template tests for multiple durable sessions and stable request IDs

Why

Project SSH terminals were held in API memory and browser-local workspace state, so session URLs and lists disappeared after API/browser restart. This change makes the backend the durable source of truth for project terminal sessions while preserving the existing route and response shapes.

Closes #286

Validation

  • bun run --filter @effect-template/api test
  • bun run --filter @prover-coder-ai/docker-git test
  • bun run --filter @effect-template/lib test
  • bun run typecheck
  • git diff --check

Not run: browser-context Docker e2e restart test; the repo does not currently have a browser e2e harness for that exact scenario.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 14, 2026

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: cd5b5b05-88fd-4780-bbc0-a95b0ecb0ab1

📥 Commits

Reviewing files that changed from the base of the PR and between 9894c39 and e320503.

📒 Files selected for processing (5)
  • packages/api/src/api/schema.ts
  • packages/api/src/services/terminal-sessions.ts
  • packages/api/tests/terminal-sessions.test.ts
  • packages/app/src/web/actions-projects.ts
  • packages/app/tests/docker-git/actions-projects.test.ts
📜 Recent review details
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: E2E (Clone cache)
  • GitHub Check: E2E (Runtime volumes + SSH)
  • GitHub Check: E2E (Clone auto-open SSH)
  • GitHub Check: E2E (Login context)
  • GitHub Check: Lint
  • GitHub Check: E2E (OpenCode)
  • GitHub Check: E2E (Browser command)
  • GitHub Check: Test
  • GitHub Check: Final build (windows-latest)
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Implement Functional Core, Imperative Shell (FCIS) pattern: CORE layer contains only pure functions with immutable data and mathematical operations; SHELL layer isolates all effects (IO, network, database). Strict dependency direction: SHELL → CORE (never reverse).
Never use any, unknown, eslint-disable, ts-ignore, or as type assertions (except in rigorously justified cases with documentation). Always use exhaustive union type analysis through .exhaustive() pattern matching.
All external dependencies must be wrapped through typed interfaces and injected via Effect-TS Layer pattern. Never call external services directly from CORE functions.
Use monadic composition with Effect-TS for all effects: Effect<Success, Error, Requirements>. Compose effects through pipe() and Effect.flatMap(). Implement dependency injection via Layer pattern. Handle errors without try/catch blocks.
All functions must be pure in the CORE layer: no side effects (logging, console output, IO operations, mutations). Separate all side effects into the SHELL layer.
Use exhaustive pattern matching with Effect.Match instead of switch statements. Example: Match.value(item).pipe(Match.when(...), Match.exhaustive).
Document all functions with comprehensive TSDoc including: @pure (true/false), @effect (required services), @invariant (mathematical invariants), @precondition, @postcondition, @complexity (time and space), @throws Never (errors must be typed in Effect).
Use functional comment markers for code clarity: CHANGE (brief description), WHY (mathematical/architectural justification), QUOTE(ТЗ) (requirement citation), REF (RTM or message ID), SOURCE (external source with quote), FORMAT THEOREM (∀x ∈ Domain: P(x) → Q(f(x))), PURITY (CORE|SHELL), EFFECT (Effect type signature), INVARIANT (mathematical invariant), COMPLEXITY (time/space).
Define all external service dependencies as Context.Tag classes with fully typed methods returning Effect types. Example: `class Da...

Files:

  • packages/api/src/api/schema.ts
  • packages/app/src/web/actions-projects.ts
  • packages/app/tests/docker-git/actions-projects.test.ts
  • packages/api/tests/terminal-sessions.test.ts
  • packages/api/src/services/terminal-sessions.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Forbidden constructs in CORE code: any, eslint-disable, ts-ignore, async/await, raw Promise chains (then/catch), Promise.all, try/catch for logic control, console.*, switch statements (use Match with .exhaustive() instead)
All functions must use Effect-TS for composing effects: Effect<Success, Error, Requirements>. No direct async/await, Promise chains, or try/catch in product logic.
Functional comments must include: CHANGE, WHY, QUOTE(ТЗ) or n/a, REF, SOURCE or n/a, FORMAT THEOREM, PURITY (CORE|SHELL), EFFECT signature for SHELL functions, INVARIANT, and COMPLEXITY.
All data mutations must use immutable patterns (ReadonlyArray, readonly properties, Object.freeze); mutation in SHELL only when absolutely necessary and documented.

Files:

  • packages/api/src/api/schema.ts
  • packages/app/src/web/actions-projects.ts
  • packages/app/tests/docker-git/actions-projects.test.ts
  • packages/api/tests/terminal-sessions.test.ts
  • packages/api/src/services/terminal-sessions.ts
**/*.{sh,bash,py,js,ts,jsx,tsx,go,java,rb,php}

📄 CodeRabbit inference engine (Custom checks)

Fail if changed files introduce command injection or unsafe shell/process execution with user-controlled input

Files:

  • packages/api/src/api/schema.ts
  • packages/app/src/web/actions-projects.ts
  • packages/app/tests/docker-git/actions-projects.test.ts
  • packages/api/tests/terminal-sessions.test.ts
  • packages/api/src/services/terminal-sessions.ts
**/*.{py,js,ts,jsx,tsx,go,java,rb,php,sh,bash,c,cpp}

📄 CodeRabbit inference engine (Custom checks)

Fail if changed files introduce path traversal or writes outside intended project/container state directories

Files:

  • packages/api/src/api/schema.ts
  • packages/app/src/web/actions-projects.ts
  • packages/app/tests/docker-git/actions-projects.test.ts
  • packages/api/tests/terminal-sessions.test.ts
  • packages/api/src/services/terminal-sessions.ts
**/*.{js,ts,jsx,tsx,py,java,go,rb,php,sh,bash,yml,yaml,json,env*,toml,cfg,config,dockerfile,dockerignore}

📄 CodeRabbit inference engine (Custom checks)

Fail if changed files expose credentials, tokens, private-keys, or PII in source, generated config, logs, or CI output

Files:

  • packages/api/src/api/schema.ts
  • packages/app/src/web/actions-projects.ts
  • packages/app/tests/docker-git/actions-projects.test.ts
  • packages/api/tests/terminal-sessions.test.ts
  • packages/api/src/services/terminal-sessions.ts
**/*

⚙️ CodeRabbit configuration file

**/*: Ты строгий ревьюер SPEC DRIVEN DEVELOPMENT.

Перед выводами изучи README.md, другие *.md файлы, linked issues,
PR description, PR comments/discussion и релевантную кодовую базу.

Сверь изменения с исходным ТЗ/спекой и обсуждением. Флагай любой уход
от спеки, недокументированное изменение поведения, отсутствие тестов
для заявленного поведения и security-риск. Если спека не видна,
попроси автора добавить ее в issue или PR description.

Проверь решение с точки зрения формальной верификации: какие инварианты,
предусловия и постусловия можно доказать математически, а где доказуемость
слабая. Оцени решение с точки зрения теории игр: устойчивы ли стимулы,
нет ли выгодного обхода правил, и какое решение было бы сильнее.

Files:

  • packages/api/src/api/schema.ts
  • packages/app/src/web/actions-projects.ts
  • packages/app/tests/docker-git/actions-projects.test.ts
  • packages/api/tests/terminal-sessions.test.ts
  • packages/api/src/services/terminal-sessions.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.test.{ts,tsx}: Implement property-based testing using fast-check for mathematical properties and invariants. Example: fc.property(fc.array(messageArbitrary), (messages) => isChronologicallySorted(sortMessagesByTimestamp(messages))).
Mock external dependencies in unit tests using Effect's testing utilities. Run tests without Effect runtime for speed. Example: Effect.provide(MockService), Effect.runPromise.

Files:

  • packages/app/tests/docker-git/actions-projects.test.ts
  • packages/api/tests/terminal-sessions.test.ts
**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Property-based tests (fast-check) must verify mathematical invariants; unit tests must use Effect test utilities without async/await.

Files:

  • packages/app/tests/docker-git/actions-projects.test.ts
  • packages/api/tests/terminal-sessions.test.ts
🔇 Additional comments (25)
packages/app/src/web/actions-projects.ts (2)

94-107: LGTM!


109-115: LGTM!

packages/api/src/api/schema.ts (1)

164-164: LGTM!

packages/app/tests/docker-git/actions-projects.test.ts (2)

148-216: LGTM!


218-241: LGTM!

packages/api/src/services/terminal-sessions.ts (16)

92-164: LGTM!


182-213: LGTM!


214-374: LGTM!


379-405: LGTM!


407-450: LGTM!


628-677: LGTM!


908-933: LGTM!


935-973: LGTM!


975-1023: LGTM!


1024-1122: LGTM!


1155-1247: LGTM!


1249-1312: LGTM!


1344-1379: LGTM!


1381-1405: LGTM!


1490-1532: LGTM!


81-91: ⚡ Quick win

Комментарий содержит неправильное утверждение о паттерне Effect-TS.

Проверка документации Effect-TS v3.x подтверждает, что требования сервисов действительно объединяются через union (|), а не пересечения (&). При компоновке независимых слоёв через Layer.mergeAll() получается Layer<Config | Logger | Database, errors, never> — объединение требований. Для сервисов с зависимостями используется тот же паттерн: Layer<Database, never, Config | Logger>.

Код на строках 82–90 и комментарий на строке 81 соответствуют стандартной практике Effect-TS и не требуют изменений.

			> Likely an incorrect or invalid review comment.
packages/api/tests/terminal-sessions.test.ts (4)

1-197: LGTM!


220-301: LGTM!


199-218: LGTM!


303-362: LGTM!


📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Надёжное (durable) сохранение терминальных сессий с восстановлением из персистентного состояния.
  • Improvements

    • Переработан жизненный цикл сессий и синхронизация при подключениях/отключениях, с более предсказуемым освобождением ресурсов.
    • Автоматическая гидратация сессий при подключении и через API.
    • Новый формат идентификаторов ожидающих сессий (UUIDv4-подобный).
    • Образ Docker теперь включает tmux; маршруты и снимки задач возвращают актуальные списки сессий.
  • Bug Fixes

    • Защита от гонок при завершении PTY и улучшенная последовательность операций удаления.
  • Tests

    • Расширены тесты для проверки персистентного состояния, создания/удаления и гидратации сессий.

Walkthrough

Добавлена durable-архитектура для терминальных сессий: схемы и операции чтения/записи terminal-sessions.json, очередь персистенции, durable-first регистрации/удаления, гидратация runtime-записей при API/WebSocket, переработанный lifecycle/PTY и tmux-интеграция; обновлены HTTP-роуты, snapshot-логика и тесты/ Dockerfile.

Changes

Persistent Terminal Sessions

Layer / File(s) Summary
Durable contract and I/O
packages/api/src/services/terminal-sessions.ts
Добавлены импорт NodeContext, runtime/durable типы и JSON-схемы, чтение/декодирование/запись terminal-sessions.json, преобразования session↔durable и операции upsert/patch/delete/find; добавлен механизм очереди персистенции и экспорт clearTerminalSessionRuntimeForTest.
Session lifecycle and persistence sync
packages/api/src/services/terminal-sessions.ts
Выделены cleanupRecord и detachRecordPty; finalizeRecord переработан (пересчёт статуса, порядок освобождения ресурсов); добавлена защита гонки в onExit; updateSession ставит durable-патч; добавлены нормализаторы ошибок.
tmux command rendering and PTY start
packages/api/src/services/terminal-sessions.ts
Экспортированы renderTmuxAttachCommand, добавлены renderRemoteTmuxCommand и preparedArgsForTmuxSession; startTerminalPty сбрасывает outputBuffer и использует подготовленные args при spawn.
Registration and hydration
packages/api/src/services/terminal-sessions.ts
registerRecord стал durable-first (upsert, tmuxName, опциональный sessionId); добавлены registerHydratedRecord, prepareRuntimeRecord, resolveParsedTerminalRecord и durable-гидратация при WebSocket upgrade; добавлены проверки доступности tmux.
Lookup, list, delete with durable fallback
packages/api/src/services/terminal-sessions.ts
deleteTerminalSession делает durable-first удаление перед runtime-cleanup; listProjectTerminalSessions/getProjectTerminalSession читают durable-данные; lookupTerminalSessionById перебирает проекты при отсутствии runtime; detachSocketFromRecord вызывает detach при нуле клиентов.
HTTP and container snapshot integration
packages/api/src/http.ts, packages/api/src/services/container-tasks.ts, packages/api/src/api/schema.ts
HTTP маршруты для получения списков сессий теперь используют эффект listProjectTerminalSessions(...).map({ sessions }); readContainerTaskSnapshot явно резолвит terminalSessions через эффект; в схеме StartProjectTerminalSessionRequestSchema поле requestId стало Schema.UUID.
Tests: persisted-state helpers and fixtures
packages/api/tests/terminal-sessions.test.ts
Тесты переведены на фабрики с path.join, используют временный каталог, добавлены helpers для чтения persisted terminal-sessions.json, и добавлены сценарии для проверки persisted IDs, гидратации и удаления persisted-сессий.
Docker images and integration tests
packages/app/src/lib/core/templates/dockerfile.ts, packages/lib/src/core/templates/dockerfile.ts, packages/lib/tests/core/templates.test.ts, packages/app/tests/docker-git/actions-projects.test.ts
Добавлен пакет tmux в apt-get install в Dockerfile-шаблонах; тесты обновлены для проверки sudo tmux; интеграционные тесты адаптированы к новому формату pending-session id.

Sequence Diagrams

sequenceDiagram
  participant App as Application
  participant Runtime as Session Runtime
  participant Durable as Durable Storage
  participant PTY as PTY/tmux Process

  App->>Runtime: createTerminalSession(projectId, options)
  Runtime->>Runtime: registerRecord(sessionId, requestId)
  Runtime->>Durable: upsertDurableSession (with tmuxName)
  Runtime->>PTY: startTerminalPty (renderRemoteTmuxCommand)
  PTY->>PTY: execute tmux session

  Note over Runtime,Durable: During runtime changes
  Runtime->>Durable: updateSession → patchDurableSession

  Note over Runtime,Durable: On WebSocket connection
  App->>Runtime: WebSocket upgrade (sessionId from URL)
  Runtime->>Runtime: resolveParsedTerminalRecord
  alt Session in runtime memory
    Runtime->>App: found TerminalRecord
  else Session not in memory
    Runtime->>Durable: hydrateTerminalRecordByProjectId
    Durable->>Runtime: TerminalRecord from disk
    Runtime->>App: hydrated TerminalRecord
  end
  App->>Runtime: handleUpgrade (now verified)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 6 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Security Regression ⚠️ Warning SSH ключи утекают через sshCommand в JSON файл (.orch/state/terminal-sessions.json) без защиты и распределяются через API/UI. Файл записывается без chmod. Приватные ключи раскрыты. 1) Не сохранять sshCommand с путем ключа в дурабл-состояние 2) Установить chmod 0600 на terminal-sessions.json 3) Переосмыслить архитектуру сохранения приватных ключей 4) Добавить авторизацию на endpoints терминальных сессий
✅ Passed checks (6 passed)
Check name Status Explanation
Title check ✅ Passed PR title clearly and concisely summarizes the main change: persisting project SSH terminal sessions. It is specific and directly related to the primary objective.
Description check ✅ Passed PR description includes all essential sections: Summary lists key changes, Why explains motivation and closes issue #286, and Validation documents testing performed. Matches template structure with implemented details.
Linked Issues check ✅ Passed Changes fully implement requirements from issue #286: sessions persist in .orch/state/terminal-sessions.json [#286], hydrate after restart [#286], provide stable URLs [#286], and install tmux in containers [#286].
Out of Scope Changes check ✅ Passed All code changes align with scope: terminal session persistence, tmux integration, durable session hydration, and related test updates directly support closing #286. No unrelated changes detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Requirements Alignment ✅ Passed All issue #286 requirements met: persistent storage, API restart hydration, stable UUIDs, tmux support, and comprehensive tests. No contradictions with spec or PR objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/api/src/services/terminal-sessions.ts`:
- Around line 810-819: The current renderRemoteTmuxCommand uses a hardcoded
tmuxMissingMessage and only detects tmux at connection time; add a proactive
tmux availability probe during project startup (e.g., in prepareProjectSsh or
probeProjectSshReady) that runs a container exec "command -v tmux" (see
suggested probeTmuxAvailable behavior) and returns a boolean so the UI can warn
early when tmux is absent; also soften or contextualize tmuxMissingMessage used
by renderRemoteTmuxCommand (or make it configurable) to avoid confusing “rebuild
image” instructions for freshly started projects.
- Line 23: Remove direct synchronous node:fs imports and calls in
readTerminalSessionFile, writeTerminalSessionFile, upsertDurableSession,
patchDurableSession, and deleteDurableSession; instead make these persistence
functions return Effect types and call the FileSystem API from `@effect/platform`
(FileSystem.FileSystem) via the Effect-TS Layer pattern (inject the FileSystem
dependency), using the asynchronous/read/write/mkdir equivalents provided by
that interface, and compose errors into the returned Effect; ensure no direct
fs.*Sync usage remains and that the functions' signatures reflect Effect<...> so
they can be provided the FileSystem layer by callers.
- Around line 171-172: The terminal session handlers are vulnerable to path
traversal because terminalSessionStatePath concatenates an unvalidated
projectId; fix by validating the project before any filesystem use: call
getProject(projectId) (as other /projects/* routes do) at the start of
createTerminalSession, listProjectTerminalSessions, and deleteTerminalSession to
ensure the project exists and reject invalid IDs, and additionally harden
terminalSessionStatePath/writeTerminalSessionFile by resolving the final
statePath with path.resolve(projectRoot, ".orch", "state",
"terminal-sessions.json") (or verify path.resolve(projectRoot,
terminalSessionStateRelativePath) startsWith the expected projectRoot) so writes
cannot escape the project directory.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 8bd27f68-2a9b-4a30-8cfd-87fb112e4e30

📥 Commits

Reviewing files that changed from the base of the PR and between 837ba27 and 1bdb7a3.

📒 Files selected for processing (6)
  • packages/api/src/services/terminal-sessions.ts
  • packages/api/tests/terminal-sessions.test.ts
  • packages/app/src/lib/core/templates/dockerfile.ts
  • packages/app/tests/docker-git/actions-projects.test.ts
  • packages/lib/src/core/templates/dockerfile.ts
  • packages/lib/tests/core/templates.test.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Implement Functional Core, Imperative Shell (FCIS) pattern: CORE layer contains only pure functions with immutable data and mathematical operations; SHELL layer isolates all effects (IO, network, database). Strict dependency direction: SHELL → CORE (never reverse).
Never use any, unknown, eslint-disable, ts-ignore, or as type assertions (except in rigorously justified cases with documentation). Always use exhaustive union type analysis through .exhaustive() pattern matching.
All external dependencies must be wrapped through typed interfaces and injected via Effect-TS Layer pattern. Never call external services directly from CORE functions.
Use monadic composition with Effect-TS for all effects: Effect<Success, Error, Requirements>. Compose effects through pipe() and Effect.flatMap(). Implement dependency injection via Layer pattern. Handle errors without try/catch blocks.
All functions must be pure in the CORE layer: no side effects (logging, console output, IO operations, mutations). Separate all side effects into the SHELL layer.
Use exhaustive pattern matching with Effect.Match instead of switch statements. Example: Match.value(item).pipe(Match.when(...), Match.exhaustive).
Document all functions with comprehensive TSDoc including: @pure (true/false), @effect (required services), @invariant (mathematical invariants), @precondition, @postcondition, @complexity (time and space), @throws Never (errors must be typed in Effect).
Use functional comment markers for code clarity: CHANGE (brief description), WHY (mathematical/architectural justification), QUOTE(ТЗ) (requirement citation), REF (RTM or message ID), SOURCE (external source with quote), FORMAT THEOREM (∀x ∈ Domain: P(x) → Q(f(x))), PURITY (CORE|SHELL), EFFECT (Effect type signature), INVARIANT (mathematical invariant), COMPLEXITY (time/space).
Define all external service dependencies as Context.Tag classes with fully typed methods returning Effect types. Example: `class Da...

Files:

  • packages/lib/tests/core/templates.test.ts
  • packages/lib/src/core/templates/dockerfile.ts
  • packages/app/src/lib/core/templates/dockerfile.ts
  • packages/app/tests/docker-git/actions-projects.test.ts
  • packages/api/tests/terminal-sessions.test.ts
  • packages/api/src/services/terminal-sessions.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.test.{ts,tsx}: Implement property-based testing using fast-check for mathematical properties and invariants. Example: fc.property(fc.array(messageArbitrary), (messages) => isChronologicallySorted(sortMessagesByTimestamp(messages))).
Mock external dependencies in unit tests using Effect's testing utilities. Run tests without Effect runtime for speed. Example: Effect.provide(MockService), Effect.runPromise.

Files:

  • packages/lib/tests/core/templates.test.ts
  • packages/app/tests/docker-git/actions-projects.test.ts
  • packages/api/tests/terminal-sessions.test.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Forbidden constructs in CORE code: any, eslint-disable, ts-ignore, async/await, raw Promise chains (then/catch), Promise.all, try/catch for logic control, console.*, switch statements (use Match with .exhaustive() instead)
All functions must use Effect-TS for composing effects: Effect<Success, Error, Requirements>. No direct async/await, Promise chains, or try/catch in product logic.
Functional comments must include: CHANGE, WHY, QUOTE(ТЗ) or n/a, REF, SOURCE or n/a, FORMAT THEOREM, PURITY (CORE|SHELL), EFFECT signature for SHELL functions, INVARIANT, and COMPLEXITY.
All data mutations must use immutable patterns (ReadonlyArray, readonly properties, Object.freeze); mutation in SHELL only when absolutely necessary and documented.

Files:

  • packages/lib/tests/core/templates.test.ts
  • packages/lib/src/core/templates/dockerfile.ts
  • packages/app/src/lib/core/templates/dockerfile.ts
  • packages/app/tests/docker-git/actions-projects.test.ts
  • packages/api/tests/terminal-sessions.test.ts
  • packages/api/src/services/terminal-sessions.ts
**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Property-based tests (fast-check) must verify mathematical invariants; unit tests must use Effect test utilities without async/await.

Files:

  • packages/lib/tests/core/templates.test.ts
  • packages/app/tests/docker-git/actions-projects.test.ts
  • packages/api/tests/terminal-sessions.test.ts
**/*.{sh,bash,py,js,ts,jsx,tsx,go,java,rb,php}

📄 CodeRabbit inference engine (Custom checks)

Fail if changed files introduce command injection or unsafe shell/process execution with user-controlled input

Files:

  • packages/lib/tests/core/templates.test.ts
  • packages/lib/src/core/templates/dockerfile.ts
  • packages/app/src/lib/core/templates/dockerfile.ts
  • packages/app/tests/docker-git/actions-projects.test.ts
  • packages/api/tests/terminal-sessions.test.ts
  • packages/api/src/services/terminal-sessions.ts
**/*.{py,js,ts,jsx,tsx,go,java,rb,php,sh,bash,c,cpp}

📄 CodeRabbit inference engine (Custom checks)

Fail if changed files introduce path traversal or writes outside intended project/container state directories

Files:

  • packages/lib/tests/core/templates.test.ts
  • packages/lib/src/core/templates/dockerfile.ts
  • packages/app/src/lib/core/templates/dockerfile.ts
  • packages/app/tests/docker-git/actions-projects.test.ts
  • packages/api/tests/terminal-sessions.test.ts
  • packages/api/src/services/terminal-sessions.ts
**/*.{js,ts,jsx,tsx,py,java,go,rb,php,sh,bash,yml,yaml,json,env*,toml,cfg,config,dockerfile,dockerignore}

📄 CodeRabbit inference engine (Custom checks)

Fail if changed files expose credentials, tokens, private-keys, or PII in source, generated config, logs, or CI output

Files:

  • packages/lib/tests/core/templates.test.ts
  • packages/lib/src/core/templates/dockerfile.ts
  • packages/app/src/lib/core/templates/dockerfile.ts
  • packages/app/tests/docker-git/actions-projects.test.ts
  • packages/api/tests/terminal-sessions.test.ts
  • packages/api/src/services/terminal-sessions.ts
**/*

⚙️ CodeRabbit configuration file

**/*: Ты строгий ревьюер SPEC DRIVEN DEVELOPMENT.

Перед выводами изучи README.md, другие *.md файлы, linked issues,
PR description, PR comments/discussion и релевантную кодовую базу.

Сверь изменения с исходным ТЗ/спекой и обсуждением. Флагай любой уход
от спеки, недокументированное изменение поведения, отсутствие тестов
для заявленного поведения и security-риск. Если спека не видна,
попроси автора добавить ее в issue или PR description.

Проверь решение с точки зрения формальной верификации: какие инварианты,
предусловия и постусловия можно доказать математически, а где доказуемость
слабая. Оцени решение с точки зрения теории игр: устойчивы ли стимулы,
нет ли выгодного обхода правил, и какое решение было бы сильнее.

Files:

  • packages/lib/tests/core/templates.test.ts
  • packages/lib/src/core/templates/dockerfile.ts
  • packages/app/src/lib/core/templates/dockerfile.ts
  • packages/app/tests/docker-git/actions-projects.test.ts
  • packages/api/tests/terminal-sessions.test.ts
  • packages/api/src/services/terminal-sessions.ts
🔇 Additional comments (19)
packages/lib/src/core/templates/dockerfile.ts (1)

33-33: LGTM!

packages/lib/tests/core/templates.test.ts (1)

68-68: LGTM!

packages/app/src/lib/core/templates/dockerfile.ts (1)

33-33: LGTM!

packages/api/src/services/terminal-sessions.ts (14)

134-153: LGTM!


207-210: LGTM!


212-250: LGTM!


252-310: LGTM!


837-839: LGTM!

Also applies to: 854-856


873-894: LGTM!


913-978: LGTM!


1028-1036: LGTM!

Also applies to: 1049-1057


1107-1115: LGTM!


1127-1133: LGTM!


1142-1152: LGTM!


1190-1220: LGTM!


1223-1242: LGTM!


1328-1361: LGTM!

packages/app/tests/docker-git/actions-projects.test.ts (1)

152-153: LGTM!

Also applies to: 168-168, 184-184, 201-201, 208-211

packages/api/tests/terminal-sessions.test.ts (1)

2-4: LGTM!

Also applies to: 21-21, 28-28, 32-32, 46-46, 53-108, 127-144, 147-183, 202-246, 285-305

Comment thread packages/api/src/services/terminal-sessions.ts Outdated
Comment thread packages/api/src/services/terminal-sessions.ts Outdated
Comment thread packages/api/src/services/terminal-sessions.ts Outdated
@rikohomeless
Copy link
Copy Markdown
Contributor Author

rikohomeless commented May 14, 2026

AI Session Backup

Commit: 28291db
Status: success
Files: 10 (7.19 MB)
Links: README | Manifest

git status

On branch issue-286
Your branch is up to date with 'fork/issue-286'.

nothing to commit, working tree clean

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/api/src/services/terminal-sessions.ts (2)

374-407: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Рассмотрите добавление observability для ошибок персистентности.

runTerminalSessionPersistence глушит все ошибки через catchAll(() => Effect.void) (строка 385). Это приемлемо для best-effort фоновой операции, но усложняет диагностику проблем с записью state на диск.

Рекомендация: добавьте структурированное логирование ошибок перед Effect.void, чтобы операторы видели проблемы с FileSystem (например, заполненный диск, проблемы с правами).

♻️ Предлагаемое улучшение observability
 const runTerminalSessionPersistence = (
   projectId: string,
   effect: Effect.Effect<void, ApiInternalError, FileSystem.FileSystem>
 ): void => {
   const previous = terminalSessionPersistenceQueues.get(projectId) ?? Promise.resolve()
   const next = previous
     .catch(() => undefined)
     .then(() =>
       Effect.runPromise(
         effect.pipe(
           Effect.provide(NodeContext.layer),
+          Effect.tapError((error) =>
+            Effect.sync(() => {
+              console.error(`[terminal-sessions] Failed to persist state for project ${projectId}:`, error)
+            })
+          ),
           Effect.catchAll(() => Effect.void)
         )
       )
     )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/api/src/services/terminal-sessions.ts` around lines 374 - 407,
runTerminalSessionPersistence currently swallows all persistence errors via
Effect.catchAll(() => Effect.void), which hides FileSystem failures; change it
to log structured error details before returning Effect.void so operators can
diagnose issues: inside the Effect.catchAll handler for
Effect.runPromise(effect.pipe(...)), capture the error and call the module's
logger (or inject/utilize an existing logger) with context (projectId, session
id from the provided effect or record) and the error object, then return
Effect.void; ensure updateSession still calls
runTerminalSessionPersistence(record.projectId, patchDurableSession(record,
patch)) so the same logging path runs for session persists.

619-667: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Уточните семантику status="ready" после выхода PTY с кодом 0.

В строке 654 finalizeRecord устанавливает nextStatus = "ready" для exitCode === 0 || exitCode === 130. Это отличается от традиционной семантики, где завершение с кодом 0 означает status="exited".

В контексте tmux-backed сессий это, вероятно, правильно: выход из PTY не означает завершение сессии в tmux (пользователь может reattach). Однако это меняет поведение API — после detach сессия остаётся в статусе "ready", а не переходит в финальное состояние.

Рекомендация: убедитесь, что клиенты API понимают новую семантику, и добавьте inline комментарий, объясняющий, почему exitCode=0 не переводит сессию в "exited" (например, "tmux session survives PTY exit and remains reattachable").

📝 Предлагаемое улучшение документации
 const finalizeRecord = (
   record: TerminalRecord,
   status: Extract<TerminalSessionStatus, "exited" | "failed">,
   exitCode: number | null,
   signal: number | null
 ): void => {
+  // CHANGE: tmux-backed sessions remain reattachable after PTY exit (status="ready")
+  // WHY: PTY exit does not terminate the underlying tmux session
+  // exitCode 0 = clean shell exit, 130 = SIGINT (Ctrl+C), both indicate intentional detach
   const nextStatus = exitCode === 0 || exitCode === 130 ? "ready" : status
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/api/src/services/terminal-sessions.ts` around lines 619 - 667, The
finalizeRecord logic sets nextStatus = "ready" when exitCode === 0 || exitCode
=== 130 which deviates from typical "exited" semantics; update the code by
adding a concise inline comment next to the nextStatus calculation inside
finalizeRecord explaining the intended semantics (e.g., "tmux-backed session:
PTY exit with code 0 or 130 does not terminate the tmux session; session remains
reattachable so we mark it 'ready' instead of 'exited'") and, if applicable, add
a short note near detachRecordPty or session API docs to ensure API clients
understand that "ready" can represent a reattachable session after a clean PTY
exit.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@packages/api/src/services/terminal-sessions.ts`:
- Around line 374-407: runTerminalSessionPersistence currently swallows all
persistence errors via Effect.catchAll(() => Effect.void), which hides
FileSystem failures; change it to log structured error details before returning
Effect.void so operators can diagnose issues: inside the Effect.catchAll handler
for Effect.runPromise(effect.pipe(...)), capture the error and call the module's
logger (or inject/utilize an existing logger) with context (projectId, session
id from the provided effect or record) and the error object, then return
Effect.void; ensure updateSession still calls
runTerminalSessionPersistence(record.projectId, patchDurableSession(record,
patch)) so the same logging path runs for session persists.
- Around line 619-667: The finalizeRecord logic sets nextStatus = "ready" when
exitCode === 0 || exitCode === 130 which deviates from typical "exited"
semantics; update the code by adding a concise inline comment next to the
nextStatus calculation inside finalizeRecord explaining the intended semantics
(e.g., "tmux-backed session: PTY exit with code 0 or 130 does not terminate the
tmux session; session remains reattachable so we mark it 'ready' instead of
'exited'") and, if applicable, add a short note near detachRecordPty or session
API docs to ensure API clients understand that "ready" can represent a
reattachable session after a clean PTY exit.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7e8acdfe-26fc-46f1-ae41-a7326de5bde7

📥 Commits

Reviewing files that changed from the base of the PR and between 1bdb7a3 and 28291db.

📒 Files selected for processing (4)
  • packages/api/src/http.ts
  • packages/api/src/services/container-tasks.ts
  • packages/api/src/services/terminal-sessions.ts
  • packages/api/tests/terminal-sessions.test.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Implement Functional Core, Imperative Shell (FCIS) pattern: CORE layer contains only pure functions with immutable data and mathematical operations; SHELL layer isolates all effects (IO, network, database). Strict dependency direction: SHELL → CORE (never reverse).
Never use any, unknown, eslint-disable, ts-ignore, or as type assertions (except in rigorously justified cases with documentation). Always use exhaustive union type analysis through .exhaustive() pattern matching.
All external dependencies must be wrapped through typed interfaces and injected via Effect-TS Layer pattern. Never call external services directly from CORE functions.
Use monadic composition with Effect-TS for all effects: Effect<Success, Error, Requirements>. Compose effects through pipe() and Effect.flatMap(). Implement dependency injection via Layer pattern. Handle errors without try/catch blocks.
All functions must be pure in the CORE layer: no side effects (logging, console output, IO operations, mutations). Separate all side effects into the SHELL layer.
Use exhaustive pattern matching with Effect.Match instead of switch statements. Example: Match.value(item).pipe(Match.when(...), Match.exhaustive).
Document all functions with comprehensive TSDoc including: @pure (true/false), @effect (required services), @invariant (mathematical invariants), @precondition, @postcondition, @complexity (time and space), @throws Never (errors must be typed in Effect).
Use functional comment markers for code clarity: CHANGE (brief description), WHY (mathematical/architectural justification), QUOTE(ТЗ) (requirement citation), REF (RTM or message ID), SOURCE (external source with quote), FORMAT THEOREM (∀x ∈ Domain: P(x) → Q(f(x))), PURITY (CORE|SHELL), EFFECT (Effect type signature), INVARIANT (mathematical invariant), COMPLEXITY (time/space).
Define all external service dependencies as Context.Tag classes with fully typed methods returning Effect types. Example: `class Da...

Files:

  • packages/api/src/services/container-tasks.ts
  • packages/api/src/http.ts
  • packages/api/tests/terminal-sessions.test.ts
  • packages/api/src/services/terminal-sessions.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Forbidden constructs in CORE code: any, eslint-disable, ts-ignore, async/await, raw Promise chains (then/catch), Promise.all, try/catch for logic control, console.*, switch statements (use Match with .exhaustive() instead)
All functions must use Effect-TS for composing effects: Effect<Success, Error, Requirements>. No direct async/await, Promise chains, or try/catch in product logic.
Functional comments must include: CHANGE, WHY, QUOTE(ТЗ) or n/a, REF, SOURCE or n/a, FORMAT THEOREM, PURITY (CORE|SHELL), EFFECT signature for SHELL functions, INVARIANT, and COMPLEXITY.
All data mutations must use immutable patterns (ReadonlyArray, readonly properties, Object.freeze); mutation in SHELL only when absolutely necessary and documented.

Files:

  • packages/api/src/services/container-tasks.ts
  • packages/api/src/http.ts
  • packages/api/tests/terminal-sessions.test.ts
  • packages/api/src/services/terminal-sessions.ts
**/*.{sh,bash,py,js,ts,jsx,tsx,go,java,rb,php}

📄 CodeRabbit inference engine (Custom checks)

Fail if changed files introduce command injection or unsafe shell/process execution with user-controlled input

Files:

  • packages/api/src/services/container-tasks.ts
  • packages/api/src/http.ts
  • packages/api/tests/terminal-sessions.test.ts
  • packages/api/src/services/terminal-sessions.ts
**/*.{py,js,ts,jsx,tsx,go,java,rb,php,sh,bash,c,cpp}

📄 CodeRabbit inference engine (Custom checks)

Fail if changed files introduce path traversal or writes outside intended project/container state directories

Files:

  • packages/api/src/services/container-tasks.ts
  • packages/api/src/http.ts
  • packages/api/tests/terminal-sessions.test.ts
  • packages/api/src/services/terminal-sessions.ts
**/*.{js,ts,jsx,tsx,py,java,go,rb,php,sh,bash,yml,yaml,json,env*,toml,cfg,config,dockerfile,dockerignore}

📄 CodeRabbit inference engine (Custom checks)

Fail if changed files expose credentials, tokens, private-keys, or PII in source, generated config, logs, or CI output

Files:

  • packages/api/src/services/container-tasks.ts
  • packages/api/src/http.ts
  • packages/api/tests/terminal-sessions.test.ts
  • packages/api/src/services/terminal-sessions.ts
**/*

⚙️ CodeRabbit configuration file

**/*: Ты строгий ревьюер SPEC DRIVEN DEVELOPMENT.

Перед выводами изучи README.md, другие *.md файлы, linked issues,
PR description, PR comments/discussion и релевантную кодовую базу.

Сверь изменения с исходным ТЗ/спекой и обсуждением. Флагай любой уход
от спеки, недокументированное изменение поведения, отсутствие тестов
для заявленного поведения и security-риск. Если спека не видна,
попроси автора добавить ее в issue или PR description.

Проверь решение с точки зрения формальной верификации: какие инварианты,
предусловия и постусловия можно доказать математически, а где доказуемость
слабая. Оцени решение с точки зрения теории игр: устойчивы ли стимулы,
нет ли выгодного обхода правил, и какое решение было бы сильнее.

Files:

  • packages/api/src/services/container-tasks.ts
  • packages/api/src/http.ts
  • packages/api/tests/terminal-sessions.test.ts
  • packages/api/src/services/terminal-sessions.ts
**/*.test.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.test.{ts,tsx}: Implement property-based testing using fast-check for mathematical properties and invariants. Example: fc.property(fc.array(messageArbitrary), (messages) => isChronologicallySorted(sortMessagesByTimestamp(messages))).
Mock external dependencies in unit tests using Effect's testing utilities. Run tests without Effect runtime for speed. Example: Effect.provide(MockService), Effect.runPromise.

Files:

  • packages/api/tests/terminal-sessions.test.ts
**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Property-based tests (fast-check) must verify mathematical invariants; unit tests must use Effect test utilities without async/await.

Files:

  • packages/api/tests/terminal-sessions.test.ts
🔇 Additional comments (29)
packages/api/src/services/terminal-sessions.ts (18)

1-52: LGTM!


81-90: LGTM!


91-176: LGTM!


180-196: LGTM!


198-228: LGTM!


230-260: LGTM!


261-304: LGTM!


306-370: LGTM!


423-448: LGTM!


898-923: LGTM!


925-963: LGTM!


965-1036: LGTM!


1038-1112: LGTM!


1145-1198: LGTM!


1238-1280: LGTM!


1282-1371: LGTM!


1373-1397: LGTM!


1482-1523: LGTM!

packages/api/src/services/container-tasks.ts (1)

369-376: LGTM!

packages/api/src/http.ts (2)

1441-1450: LGTM!


1491-1498: LGTM!

packages/api/tests/terminal-sessions.test.ts (8)

1-52: LGTM!


56-122: LGTM!


133-153: LGTM!


155-218: LGTM!


220-260: LGTM!


262-301: LGTM!


303-345: LGTM!


347-360: LGTM!

@rikohomeless
Copy link
Copy Markdown
Contributor Author

rikohomeless commented May 14, 2026

AI Session Backup

Commit: 9894c39
Status: success
Files: 10 (7.42 MB)
Links: README | Manifest

git status

On branch issue-286
Your branch is up to date with 'fork/issue-286'.

nothing to commit, working tree clean

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/api/src/services/terminal-sessions.ts`:
- Around line 81-90: The types TerminalSessionRuntime and
TerminalSessionStateRuntime are currently declared as unions (|) but should be
intersections (&) because Effect dependencies require all services; change both
declarations from using | to using & so the types require
CommandExecutor.CommandExecutor & FileSystem.FileSystem & PlatformPath.Path,
ensuring callers must provide all services when using these runtimes.
- Around line 1369-1376: The current Effect.catchAll in
lookupTerminalSessionById converts every error (including ApiInternalError) into
ApiNotFoundError, hiding real internal failures; change the error handling so
that only genuine "not found" cases are mapped to ApiNotFoundError while other
errors (e.g., ApiInternalError or unknown errors) are rethrown/preserved.
Specifically, replace the broad Effect.catchAll with a selective catcher (e.g.,
Effect.catchSome or an if-check) that returns Effect.fail(error) when error is
an instance of ApiInternalError (or any non-not-found error), and only returns
Effect.fail(new ApiNotFoundError(...)) when the original error indicates a true
not-found condition; reference lookupTerminalSessionById, Effect.catchAll,
ApiNotFoundError, ApiInternalError, and sessionId when applying the fix.
- Around line 1175-1177: The requestId passed from the client
(options.requestId) is being used as sessionId in registerRecord and stored via
upsertDurableSession, which risks session collisions and predictable IDs; either
enforce UUID format validation on StartProjectTerminalSessionRequestSchema for
requestId (reject non-UUIDs) or stop trusting the client value: generate a
server-side sessionId (and use tmuxNameForSessionId to sanitize when creating
tmux names), store that generated ID in registerRecord/upsertDurableSession, and
return it to the client; update any call sites that currently pass
options.requestId (e.g., the registerRecord invocation and session creation
flow) to use the validated or generated server-side ID.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 34d31149-4c2b-4a50-a682-b10d2b198808

📥 Commits

Reviewing files that changed from the base of the PR and between 28291db and 9894c39.

📒 Files selected for processing (1)
  • packages/api/src/services/terminal-sessions.ts
📜 Review details
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Implement Functional Core, Imperative Shell (FCIS) pattern: CORE layer contains only pure functions with immutable data and mathematical operations; SHELL layer isolates all effects (IO, network, database). Strict dependency direction: SHELL → CORE (never reverse).
Never use any, unknown, eslint-disable, ts-ignore, or as type assertions (except in rigorously justified cases with documentation). Always use exhaustive union type analysis through .exhaustive() pattern matching.
All external dependencies must be wrapped through typed interfaces and injected via Effect-TS Layer pattern. Never call external services directly from CORE functions.
Use monadic composition with Effect-TS for all effects: Effect<Success, Error, Requirements>. Compose effects through pipe() and Effect.flatMap(). Implement dependency injection via Layer pattern. Handle errors without try/catch blocks.
All functions must be pure in the CORE layer: no side effects (logging, console output, IO operations, mutations). Separate all side effects into the SHELL layer.
Use exhaustive pattern matching with Effect.Match instead of switch statements. Example: Match.value(item).pipe(Match.when(...), Match.exhaustive).
Document all functions with comprehensive TSDoc including: @pure (true/false), @effect (required services), @invariant (mathematical invariants), @precondition, @postcondition, @complexity (time and space), @throws Never (errors must be typed in Effect).
Use functional comment markers for code clarity: CHANGE (brief description), WHY (mathematical/architectural justification), QUOTE(ТЗ) (requirement citation), REF (RTM or message ID), SOURCE (external source with quote), FORMAT THEOREM (∀x ∈ Domain: P(x) → Q(f(x))), PURITY (CORE|SHELL), EFFECT (Effect type signature), INVARIANT (mathematical invariant), COMPLEXITY (time/space).
Define all external service dependencies as Context.Tag classes with fully typed methods returning Effect types. Example: `class Da...

Files:

  • packages/api/src/services/terminal-sessions.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Forbidden constructs in CORE code: any, eslint-disable, ts-ignore, async/await, raw Promise chains (then/catch), Promise.all, try/catch for logic control, console.*, switch statements (use Match with .exhaustive() instead)
All functions must use Effect-TS for composing effects: Effect<Success, Error, Requirements>. No direct async/await, Promise chains, or try/catch in product logic.
Functional comments must include: CHANGE, WHY, QUOTE(ТЗ) or n/a, REF, SOURCE or n/a, FORMAT THEOREM, PURITY (CORE|SHELL), EFFECT signature for SHELL functions, INVARIANT, and COMPLEXITY.
All data mutations must use immutable patterns (ReadonlyArray, readonly properties, Object.freeze); mutation in SHELL only when absolutely necessary and documented.

Files:

  • packages/api/src/services/terminal-sessions.ts
**/*.{sh,bash,py,js,ts,jsx,tsx,go,java,rb,php}

📄 CodeRabbit inference engine (Custom checks)

Fail if changed files introduce command injection or unsafe shell/process execution with user-controlled input

Files:

  • packages/api/src/services/terminal-sessions.ts
**/*.{py,js,ts,jsx,tsx,go,java,rb,php,sh,bash,c,cpp}

📄 CodeRabbit inference engine (Custom checks)

Fail if changed files introduce path traversal or writes outside intended project/container state directories

Files:

  • packages/api/src/services/terminal-sessions.ts
**/*.{js,ts,jsx,tsx,py,java,go,rb,php,sh,bash,yml,yaml,json,env*,toml,cfg,config,dockerfile,dockerignore}

📄 CodeRabbit inference engine (Custom checks)

Fail if changed files expose credentials, tokens, private-keys, or PII in source, generated config, logs, or CI output

Files:

  • packages/api/src/services/terminal-sessions.ts
**/*

⚙️ CodeRabbit configuration file

**/*: Ты строгий ревьюер SPEC DRIVEN DEVELOPMENT.

Перед выводами изучи README.md, другие *.md файлы, linked issues,
PR description, PR comments/discussion и релевантную кодовую базу.

Сверь изменения с исходным ТЗ/спекой и обсуждением. Флагай любой уход
от спеки, недокументированное изменение поведения, отсутствие тестов
для заявленного поведения и security-риск. Если спека не видна,
попроси автора добавить ее в issue или PR description.

Проверь решение с точки зрения формальной верификации: какие инварианты,
предусловия и постусловия можно доказать математически, а где доказуемость
слабая. Оцени решение с точки зрения теории игр: устойчивы ли стимулы,
нет ли выгодного обхода правил, и какое решение было бы сильнее.

Files:

  • packages/api/src/services/terminal-sessions.ts
🔇 Additional comments (9)
packages/api/src/services/terminal-sessions.ts (9)

1-51: LGTM!


91-176: LGTM!


261-370: LGTM!


374-412: LGTM!


623-672: LGTM!


903-968: LGTM!


970-1117: LGTM!


1378-1402: LGTM!

Also applies to: 1494-1522


190-196: 🏗️ Heavy lift

Валидация projectId против реальных проектов системы происходит в getProject()findProjectById() до передачи в дurable session функции. Произвольные пути вроде "../../etc" не пройдут whitelist-проверку в findProjectById() и не достигнут terminalSessionStatePath(). Функция isPathInsideDirectory служит дополнительной защитой, а не основной линией обороны.

			> Likely an incorrect or invalid review comment.

Comment on lines +81 to +90
type TerminalSessionRuntime =
| CommandExecutor.CommandExecutor
| FileSystem.FileSystem
| PlatformPath.Path

type TerminalSessionStateRuntime =
| CommandExecutor.CommandExecutor
| FileSystem.FileSystem
| PlatformPath.Path

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Неверный тип: объединение (|) вместо пересечения (&) для требований Effect.

Типы TerminalSessionRuntime и TerminalSessionStateRuntime используют union (|), что означает "один из сервисов". Для зависимостей Effect должно быть пересечение (&), означающее "все эти сервисы обязательны".

С текущей сигнатурой TypeScript позволит вызвать функцию, предоставив только один из сервисов, что приведёт к runtime-ошибке при попытке использовать отсутствующий сервис.

🐛 Исправление типов
 type TerminalSessionRuntime =
-  | CommandExecutor.CommandExecutor
-  | FileSystem.FileSystem
-  | PlatformPath.Path
+  & CommandExecutor.CommandExecutor
+  & FileSystem.FileSystem
+  & PlatformPath.Path

 type TerminalSessionStateRuntime =
-  | CommandExecutor.CommandExecutor
-  | FileSystem.FileSystem
-  | PlatformPath.Path
+  & CommandExecutor.CommandExecutor
+  & FileSystem.FileSystem
+  & PlatformPath.Path
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/api/src/services/terminal-sessions.ts` around lines 81 - 90, The
types TerminalSessionRuntime and TerminalSessionStateRuntime are currently
declared as unions (|) but should be intersections (&) because Effect
dependencies require all services; change both declarations from using | to
using & so the types require CommandExecutor.CommandExecutor &
FileSystem.FileSystem & PlatformPath.Path, ensuring callers must provide all
services when using these runtimes.

Comment thread packages/api/src/services/terminal-sessions.ts
Comment thread packages/api/src/services/terminal-sessions.ts
@rikohomeless
Copy link
Copy Markdown
Contributor Author

rikohomeless commented May 14, 2026

AI Session Backup

Commit: e320503
Status: success
Files: 10 (7.75 MB)
Links: README | Manifest

git status

On branch issue-286
Your branch is up to date with 'fork/issue-286'.

nothing to commit, working tree clean

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

Labels

None yet

Projects

None yet

1 participant