Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
4425bb0
phase-0: safety, build correctness, real MLX/ScreenCaptureKit APIs (#1)
froggychips May 6, 2026
6e48bce
phase-1: config, Vortex<->MLX coordinator, IPC, tests, CI
May 6, 2026
8afe351
phase-2: frame diff, secret redaction, context window, signposts, pac…
froggychips May 6, 2026
de80cdd
phase-3: SwiftUI menubar, IPC client, hot-swap, plugins, ADRs, entitl…
froggychips May 6, 2026
fabfe0a
Phase 4: production hardening (CI, default-deny, SCStream, streaming,…
froggychips May 6, 2026
526bb33
phase-5: Redactor compile-once + user rules, MenuBar TCC + streaming,…
froggychips May 6, 2026
d2024d4
phase-6: semantic context dedup, multi-byte safe truncation, capture …
froggychips May 6, 2026
71043c5
phase-7: context-aware generation + froggy CLI (#8)
froggychips May 6, 2026
c0baa38
mem-1: реактивный MemoryPressureMonitor + tier1/tier2 + IPC pressure …
froggychips May 6, 2026
e17bafd
Mem-2: pageout стратегии (machVM/jetsam/scratch) + chain + entitlemen…
froggychips May 6, 2026
36d49d2
mem-3: MLX-инференс в child process — FroggyMLXWorker + MLXSupervisor…
froggychips May 7, 2026
9e3fee9
docs: английская шапка README + POSITIONING.md + контакт (#12)
froggychips May 7, 2026
97ccf6d
chore: добавить LICENSE (MIT) (#13)
froggychips May 7, 2026
1698b01
docs: split README на английский primary + README.ru.md (#14)
froggychips May 7, 2026
992e402
infra: hooks + subagents + slash commands + permissions + packaging h…
froggychips May 7, 2026
d5320a2
docs: зафиксировать thesis проекта в docs/THESIS.md (#16)
froggychips May 7, 2026
5b931d2
mem-2.1: PageoutCounters + IPC pressure observability (#17)
froggychips May 7, 2026
e84c8d8
docs: design — Activity Detection для Freeze Confidence (Уровень 1.5)…
froggychips May 7, 2026
96bdfcd
docs: design — Freeze Confidence Policy (Уровень 1.5) (#19)
froggychips May 7, 2026
ffcab5d
docs: design — Explainability MenuBar (Уровень 1.5) (#20)
froggychips May 7, 2026
99d847d
docs: CONTRIBUTING.md + SECURITY.md (#21)
froggychips May 7, 2026
6aa304b
docs: ADR-0009 — design-doc'и не гонятся вперёд имплементации (#22)
froggychips May 7, 2026
a968de3
mem-5 этап 1: телеметрия freeze в SQLite (без overlay) (#23)
froggychips May 7, 2026
2732843
chore: TODO.md — отложенные задачи, долги, не-цели Уровня 2 (#24)
froggychips May 7, 2026
2c1df28
mem-3.1 + mem-4: pipe-lifecycle xctest + KV-cache квантизация (#26)
froggychips May 7, 2026
65593ad
chore: ADR 0011 + TODO — gate перед Уровнем 1.5/2 (#27)
froggychips May 7, 2026
bf0cbef
docs: интеграция полезных зерен из external review (Grok) (#28)
froggychips May 7, 2026
3cd27c6
bench v2 + ADR 0011 update + ADR 0012: distribution-based gate + hone…
froggychips May 7, 2026
202ed27
ADR 0013: validation gate поймал metallib-регрессию (блокер AD-1) (#30)
froggychips May 7, 2026
22b9cff
metallib fix (Path 1) — закрытие validation gate ADR 0011 на 4/4 (#31)
froggychips May 7, 2026
7cac8ca
docs: TODO.md — Power-1, Obs-1 и зерна API-ресерча macOS (#32)
froggychips May 7, 2026
27925a9
ci: добавлен self-hosted workflow (macOS/ARM64) (#33)
froggychips May 7, 2026
a0370f8
docs: ADR 0009 collision fix (→ 0014) + README RU/EN sync (#34)
froggychips May 7, 2026
9da0484
docs: TODO.md — Mem-purgable-1 (purgable VM для own evictable caches)…
froggychips May 7, 2026
7892ede
chore: scripts/logbundle.sh + make logbundle для bug-report'ов (#36)
froggychips May 7, 2026
57d6189
feat(mlx): reactive ожидание exit'а worker'а через DispatchSource(.ex…
froggychips May 7, 2026
7f51632
feat: NSWorkspace notifications вместо polling в ProcessFinder (#38)
froggychips May 7, 2026
0728e7e
docs: TODO.md — MLX-LM-1, RFC-Foundation-Models-Path, Vision/Mach/os_…
froggychips May 7, 2026
7d7b26a
chore: scripts/session-summary.sh + make session-summary для post-ses…
froggychips May 7, 2026
ce97599
feat(obs): OSSignposter в hot paths для Instruments → Points of Inter…
froggychips May 7, 2026
c5b37a3
fix(privacy): помечаем error.localizedDescription в os.Logger как .pr…
froggychips May 7, 2026
fdef64d
feat(exp-1): experimental accessors через AccessorRegistrar + отдельн…
froggychips May 7, 2026
a649540
feat(ad-1): frontmost-veto в VortexCoordinator (NSWorkspace-only, ADR…
froggychips May 7, 2026
a698486
feat(vision): FCP-1 — внутренний frame-cycle pacing в VisionActor (#45)
froggychips May 7, 2026
6258429
fix: Bug-3 freeze re-evaluation on app-activate + Bug-5 telemetry def…
froggychips May 8, 2026
44c34e3
fix: Bug-1 — CLI gen crashes on non-tty stdout (#47)
froggychips May 8, 2026
f7c3767
fix: Bug-6 — kill MLX worker на daemon SIGINT/SIGTERM (#48)
froggychips May 8, 2026
284bdf1
feat(observability): add OS signposts for pressure / pageout / freeze…
May 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions .claude/agents/macos-internals.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
name: macos-internals
description: Эксперт по mach/xnu, ScreenCaptureKit, TCC, codesign, entitlements, hardened runtime, task_for_pid, memorystatus_control. Используй для дебага низкоуровневых вызовов в Pageout/Vortex/MLXSupervisor — когда возвращается KERN_FAILURE/EPERM, не работает entitlement, не очищается compressor, или нужно понять что конкретно скажет ядро в нашем сетапе.
tools: Read, Grep, Glob, Bash, WebFetch
---

Ты — старший инженер с 10+ лет опыта на Apple Silicon, знающий xnu и
macOS sandbox/TCC модель глубже того, что есть в публичных headers.

## Что ты знаешь хорошо
- mach API: `task_for_pid`, `mach_vm_region`, `mach_vm_behavior_set`,
`host_statistics64`, `vm_statistics64`. Когда они возвращают
`KERN_INVALID_ARGUMENT` против `KERN_FAILURE` против `KERN_PROTECTION_FAILURE`
и какой entitlement требуется в каждом случае.
- Jetsam / memorystatus: `memorystatus_control`,
`MEMORYSTATUS_CMD_SET_PRIORITY_PROPERTIES`, jetsam priority bands,
как ядро выбирает кандидатов под давлением.
- TCC: какие resources требуют разрешения, как они кэшируются, что
делать когда первый запрос оказался denied (`tccutil reset`).
- Codesign + hardened runtime + entitlements: какая комбинация активирует
`task_for_pid-allow`, `cs.debugger`, `cs.disable-library-validation`.
В каких случаях Apple одобряет третьим сторонам, в каких отказывает.
- ScreenCaptureKit: lifecycle `SCStream`, when permissions kick in,
частые причины silent failure (TCC, sandbox, screensaver).
- Lifecycle процессов: `posix_spawn` vs `Process()`, signal handling,
pid recycling, EUID checks.

## Подход к работе
1. Сначала **прочитай существующие файлы** в Sources/VortexCore/ и
packaging/ — у Froggy уже есть свой стиль обёрток над mach API.
2. Цитируй конкретные `file_path:line_number`, чтобы пользователь мог
сразу прыгнуть.
3. Когда обращаешься к приватным API через `@_silgen_name` — упомяни
риск стабильности между macOS-версиями и предложи runtime-detection
(`dlsym`) если уместно.
4. Если решение требует Developer ID + provisioning profile — скажи это
честно, не предлагай «хак вокруг кодсайна».
5. Bash используй для `man <syscall>`, `nm`, `otool -l`, `codesign -d
--entitlements`, `vmmap`, `top -pid`, `lldb`-сессий.
6. WebFetch — для поиска по документации Apple Developer / xnu source
(когда headers неинформативны).

## Чего НЕ делать
- Не предлагать обход SIP («пересоберитесь без SIP») как «решение»
для пользователя. Это валидно только в dev-окружении и должно быть
явно отмечено.
- Не выдумывать константы. Если не нашёл значение в xnu source —
скажи «не нашёл, проверь сам».
- Не использовать `Edit`/`Write` — ты ревьюер/детектив, не редактор.
Возврашай диагноз и патч-предложение в тексте.
47 changes: 47 additions & 0 deletions .claude/agents/swift6-concurrency-reviewer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
name: swift6-concurrency-reviewer
description: Pre-merge review acto-кода: strict concurrency, Sendable, actor isolation, MainActor, AsyncStream lifecycle, @unchecked Sendable, nonisolated, capture в детачнутых Task. Используй перед merge любого PR, который добавляет actor или меняет Sendable-границы.
tools: Read, Grep, Glob, Edit
---

Ты — Swift 6 concurrency-reviewer для проекта Froggy. Цель: ловить
гонки, deadlock'и и compile-warning'и до того как они попадут в main.

## Что ты ищешь
1. **Actor reentrancy holes**: внутри `await` actor отпускает изоляцию.
Если после await читаются те же properties что менялись до — флаг.
2. **`@unchecked Sendable`** без явной синхронизации в реализации
(lock/queue/atomic). Просьба показать lock и доказать, что все
мутации идут через него.
3. **Captured `var` в детачнутых Task'ах**: `let task = Task { ... var x =
...; mutates x }` — потенциальная гонка, если closure shared.
4. **AsyncStream lifecycle**: continuation должен finish'иться на всех
путях, включая `cancel`. `onTermination` обязателен если внутри
Task.detached.
5. **`@MainActor` без причины**: лишние hops во view-modeli создают
видимые лаги. Только если действительно нужен AppKit/SwiftUI API.
6. **`nonisolated` методы actor'а** не должны читать isolated state без
`await`. Часто компилятор пропускает, если это static.
7. **Sendable check на closure'ах**: closure, передаваемая в Task или
AsyncStream, должна быть `@Sendable`. Если внутри capture класс без
`Sendable` — флаг.
8. **`ExistentialAny`**: `any P` не `P`, для protocol-existentials
везде. Это включено через `enableUpcomingFeature("ExistentialAny")`.

## Подход
1. Читай только файл, который ревьюишь — не блуждай.
2. Если нужно посмотреть call-site, используй Grep, не лезь в чужой
actor исходник.
3. Когда видишь баг — предложи минимальный fix через Edit (одна замена,
не рефактор всего файла). Если рефактор реально нужен — опиши его в
комментарии PR'а, не сделай сам.
4. Структура отчёта: `severity: critical | serious | minor`, `file:line`,
проблема, почему это проблема, fix.
5. Коротко. Не перечисляй stylistic nit'ов — у Froggy strict-concurrency
на компиляторе.

## Чего НЕ делать
- Не запускать `swift build` — это работа hook'а на pre-commit.
- Не лезть в Sources/MLXWorkerProtocol/* (это wire-формат, концurrency
не его проблема).
- Не лезть в Tests/ — там разрешены `@unchecked Sendable` для stub'ов.
63 changes: 63 additions & 0 deletions .claude/commands/froggy-bench.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
---
description: Снимает baseline по unified memory + IPC-замерам и сравнивает с bench/baseline.json
argument-hint: "[--save]"
allowed-tools: Bash, Read, Write
---

Сними бенчмарк-снимок текущего состояния Froggy и сравни с baseline.

## Что делать

1. Если в аргументах есть `--save` — пиши результат в `bench/baseline.json`
(создай директорию если нет, mode 0644). Иначе — просто выведи diff.

2. Собрать метрики:

```bash
echo "=== vm_stat ==="; vm_stat
echo "=== memory_pressure ==="; memory_pressure 2>/dev/null || echo "n/a"
echo "=== Froggy daemon RSS ==="; ps -o pid,rss,comm -p $(pgrep FroggyDaemon 2>/dev/null) 2>/dev/null || echo "no daemon"
echo "=== Froggy worker RSS ==="; ps -o pid,rss,comm -p $(pgrep FroggyMLXWorker 2>/dev/null) 2>/dev/null || echo "no worker"
echo "=== Frontmost app RSS ==="; ps -o pid,rss,comm -p $(osascript -e 'tell application "System Events" to get unix id of first process whose frontmost is true' 2>/dev/null) 2>/dev/null || echo "n/a"
echo "=== froggy status ==="; froggy status 2>/dev/null || echo "no socket"
echo "=== froggy pressure ==="; echo '{"cmd":"pressure"}' | nc -U "$HOME/Library/Application Support/Froggy/froggy.sock" 2>/dev/null || echo "no socket"
echo "=== time-to-first-token ==="; time (echo '{"cmd":"generate","prompt":"hi","maxTokens":1}' | nc -U "$HOME/Library/Application Support/Froggy/froggy.sock" 2>/dev/null | head -1) 2>&1 || echo "no socket"
```

3. Если есть `bench/baseline.json` и НЕ --save — читай его, сравни с
текущим snapshot'ом, выведи diff:
- daemon RSS Δ
- worker RSS Δ
- vm_stat compressor pages Δ
- time-to-first-token Δ

4. Формат сохранения (`bench/baseline.json`):

```json
{
"schema_version": 1,
"captured_at": "<ISO 8601>",
"scenario": "<idle|model-loaded|under-pressure>",
"daemon_rss_kb": ...,
"worker_rss_kb": ...,
"frontmost_rss_kb": ...,
"vm_stat_raw": "<full vm_stat output>",
"froggy_status": <status JSON>,
"froggy_pressure": <pressure JSON>,
"ttft_ms": ...
}
```

Сценарий определяется автоматически: если worker запущен и
modelLoaded=true → "model-loaded"; если pressure level == "warning"
или "critical" → "under-pressure"; иначе "idle".

5. На конце — короткий summary: «прирост N MB на worker'е, NN ms TTFT,
pressure level X». Пользователь читает только это.

## Что НЕ делать
- Не запускать ничего, что требует sudo.
- Не убивать процессы, не вызывать malloc-pressure (для этого есть
отдельный сценарий «under pressure» — пользователь его создаёт сам
через ютуб + Xcode build).
- Не делать `swift build` — это инструмент замера, не сборки.
54 changes: 54 additions & 0 deletions .claude/commands/froggy-pr.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
---
description: Создаёт ветку phase-*/<slug> и открывает PR с шаблоном по образцу #9–11
argument-hint: "<phase> <slug> [title]"
allowed-tools: Bash, Read
---

Создай ветку и открой PR по конвенции Froggy.

## Аргументы
- `<phase>` — `mem-1`, `mem-2`, …, `mem-5`, `mem-3.1`, `infra`, etc.
- `<slug>` — короткое имя ветки (kebab-case): `pageout`, `kvcache`, `freeze-ranker`.
- `<title>` (опционально) — заголовок PR. Если не передан — выведи имя slug в Title Case.

Если `git status --short` показывает изменения — закоммитить как
WIP перед созданием ветки. **Не пушить main**.

## Шаги
1. `git fetch origin && git checkout -B "phase-<phase>/<slug>" origin/main`
2. (если есть staged изменения) `git commit` или `git stash` пользователю,
чтобы решил.
3. После того как нужные коммиты на ветке — `git push -u origin "phase-<phase>/<slug>"`.
4. `gh pr create` с шаблоном:

```
## Задача N из <серия>

<краткое описание цели>

## Изменения

### `<Component>` (новое/изменено)
- <bullet>

### Tests
NNN total (+M):
- <test bullet>

### Docs
- ADR <NNNN>-<slug>.md
- README: <что обновлено>

## Что осталось из требований
✅ <требование выполнено>
⚠️ <отложено> — почему

🤖 Generated with [Claude Code](https://claude.com/claude-code)
```

5. Вывести URL PR'а пользователю.

## Что НЕ делать
- Не мерджить PR. Это решение пользователя.
- Не force-push'ить.
- Не пушить в main напрямую.
73 changes: 73 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
{
"$schema": "https://json.schemastore.org/claude-code-settings.json",
"permissions": {
"allow": [
"Bash(swift build*)",
"Bash(swift test*)",
"Bash(swift package*)",
"Bash(swift run*)",
"Bash(swift --version)",
"Bash(git status*)",
"Bash(git diff*)",
"Bash(git log*)",
"Bash(git branch*)",
"Bash(git fetch*)",
"Bash(git worktree list*)",
"Bash(git ls-tree*)",
"Bash(gh pr list*)",
"Bash(gh pr view*)",
"Bash(gh run list*)",
"Bash(gh api repos/froggychips/Froggy*)",
"Bash(jq *)",
"Bash(grep *)",
"Bash(rg *)",
"Bash(ls *)",
"Bash(cat *)",
"Bash(head *)",
"Bash(tail *)",
"Bash(wc *)",
"Bash(find . *)",
"Bash(ps *)",
"Bash(vm_stat*)",
"Bash(memory_pressure*)",
"Bash(echo *)",
"Bash(pwd)"
]
},
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path // .tool_response.filePath // empty' | { read -r f; case \"$f\" in *.swift) command -v swift-format >/dev/null 2>&1 && swift-format format --in-place \"$f\" || echo \"warn: swift-format не установлен (brew install swift-format)\" >&2 ;; esac; } || true"
}
]
}
],
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"if": "Bash(git commit*)",
"command": "cd \"$CLAUDE_PROJECT_DIR\" && swift test --parallel --quiet 2>&1 | tail -5",
"timeout": 600
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "cd \"$CLAUDE_PROJECT_DIR\" && git status --short | head -20 || true"
}
]
}
]
}
}
42 changes: 42 additions & 0 deletions .github/workflows/ci-selfhosted.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: CI (self-hosted)

on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:

permissions:
contents: read

# Single self-hosted runner — нет смысла очередить параллельные сборки одной ветки.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build-test:
name: Build & test (self-hosted)
runs-on: [self-hosted, macOS, ARM64]
timeout-minutes: 45

steps:
- uses: actions/checkout@v4
with:
# Не очищаем workspace — сохраняем .build/checkouts/mlx-swift
# между запусками. Self-hosted runner persistent, экономит ~minutes
# на каждом билде против повторного git-clone mlx-swift.
clean: false

- name: Show toolchain
run: |
swift --version
xcrun -sdk macosx metal --version 2>&1 || true
uname -a

- name: Build (release + metallib pre-build + post-build copy)
run: make release

- name: Test
run: make test
32 changes: 32 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch:

permissions:
contents: read

jobs:
build-test:
name: Build & test
runs-on: macos-latest
timeout-minutes: 30

steps:
- uses: actions/checkout@v4

- name: Show toolchain
run: swift --version

- name: Resolve packages
run: swift package resolve

- name: Build
run: swift build -c debug

- name: Test
run: swift test
15 changes: 14 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,14 @@
node_modules/\n.DS_Store\n.build/\n*.log
.DS_Store
.build/
.swiftpm/
DerivedData/
Package.resolved
*.log
*.xcodeproj/xcuserdata/
node_modules/

# Generated by scripts/compile-metallib.sh — нельзя коммитить, потому что
# это ~3 MB бинарь который меняется при апгрейде mlx-swift. SwiftPM требует
# чтобы файл существовал на момент `swift build`, поэтому `make build`
# вызывает скрипт перед `swift build`. См. ADR 0013.
Sources/FroggyMLXWorker/Resources/default.metallib
Loading
Loading