diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 518ff57..2ac379d 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -207,4 +207,4 @@ RUN echo "gopls ${GOPLS_VERSION}, dlv ${DELVE_VERSION}, claude code ${CLAUDE_COD # once it is built, and the one thing that does — the docker daemon started by # the docker-in-docker feature — asks for root through sudo itself. USER vscode -RUN mkdir -p /home/vscode/.claude /home/vscode/.config/gcloud /home/vscode/.config/gh +RUN mkdir -p /home/vscode/.claude /home/vscode/.codex /home/vscode/.config/gcloud /home/vscode/.config/gh diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index d23ffd6..c0476a9 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -31,6 +31,8 @@ "mounts": [ // Claude Code login and settings survive container rebuilds. "source=canvas-api-claude,target=/home/vscode/.claude,type=volume", + // Codex's ChatGPT sign-in, settings and chat history: its default home, kept the same way. + "source=canvas-api-codex,target=/home/vscode/.codex,type=volume", // The Google sign-in survives them too: the credentials Terraform reads live here. "source=canvas-api-gcloud,target=/home/vscode/.config/gcloud,type=volume", // And the GitHub one, so that `gh auth login` is answered once rather than once @@ -38,7 +40,8 @@ "source=canvas-api-gh,target=/home/vscode/.config/gh,type=volume" ], "containerEnv": { - // Tells a Claude session it runs in here, the one place it may work. + // Tells an agent's session, Claude's or Codex's, that it runs in here, the one place + // it may work. "MATHTRAIL_DEVCONTAINER": "1", "CLAUDE_CONFIG_DIR": "/home/vscode/.claude", "DISABLE_AUTOUPDATER": "1" @@ -57,6 +60,10 @@ // Exact versions; the Claude Code extension matches CLAUDE_CODE_VERSION. "extensions": [ "anthropic.claude-code@2.1.280", + // OpenAI's coding agent, signed in with a ChatGPT plan. It reads AGENTS.md, + // which sends it to the same rules as Claude. The extension brings its own + // CLI and sandbox, so the image installs nothing for it. + "openai.chatgpt@26.1007.21434", "golang.go@0.56.1", "redhat.vscode-yaml@1.24.0", "nefrob.vscode-just-syntax@0.10.2", diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..9696942 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,9 @@ +# AGENTS.md + +The working rules of this repository are in [CLAUDE.md](CLAUDE.md), and they bind every coding agent, not only Claude Code. Read it in full before any work and follow it as written: what it asks of Claude, it asks of you. It stays the one copy of the rules; this file only says what stands in for the parts that are Claude Code's own: + +- **The cloud-session exception to "Devcontainer only"** is for a cloud session of Claude Code. A Codex session that does not see `MATHTRAIL_DEVCONTAINER=1` stops before its first edit, as that rule says. +- **The review before a change is called done.** `/code-review high` is a Claude Code command, and you cannot run it; Codex's `/review` stands in for it, started by the author. Everything else in that rule holds as written: prepare the clean copy and run the checks in it, then stop and ask the author to run `/review` there; verify and handle its findings as the rule says, with at most the one second round it allows. Until a review has run, the report says that it did not. +- **A live run's narrow permission.** Claude Code gets it in `.claude/settings.local.json`. Under Codex the author approves each of the run's commands once, as it comes, never for the rest of the session, so nothing approved outlives the run; do not ask for a looser sandbox or approval mode for it. +- **A live run's report** names its roles as they were: what the author did and what Codex did. +- **Memory.** Claude Code's memory is not shared with you. What has to outlive a session goes into the repository's documents, as CLAUDE.md says. diff --git a/CLAUDE.md b/CLAUDE.md index 411980d..895a6eb 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -100,7 +100,7 @@ Project context lives in these files, not in chat history. Each tool's exact version is pinned directly where it is installed — as an `ARG` in `.devcontainer/Dockerfile`, as a literal in `devcontainer.json`. No separate versions file, no checksum verification. -- **Language.** Code, every comment in code and config files (Go, TS, SQL, protobuf, YAML, HCL, Dockerfile, justfile, `.env.example`, …), `CLAUDE.md`, `README.md`, `CONTRIBUTING.md` and `SECURITY.md`, the texts of the UI, the hints the child sees and the model's prompts are in English (R02). +- **Language.** Code, every comment in code and config files (Go, TS, SQL, protobuf, YAML, HCL, Dockerfile, justfile, `.env.example`, …), `CLAUDE.md`, `AGENTS.md`, `README.md`, `CONTRIBUTING.md` and `SECURITY.md`, the texts of the UI, the hints the child sees and the model's prompts are in English (R02). - RUN.md and every document under `docs/` are in Russian (R25). - Reports to the author in chat are in Russian. - A comment, and any text a person reads from code or config — a description, an error message, a test's name — gives its reason in its own words. It cites no decision, task or section of the documents: no `(R58)`, `(T11)`, `(spec 15.3)`, `(RUN.md, rule 13)`. The documents do not outlive the code. @@ -317,6 +317,7 @@ Inside the container: - To change a version: edit the literal everywhere it appears (Dockerfile, `devcontainer.json` for Docker or the Claude Code extension), rebuild the container. The Claude Code CLI and its editor extension are one version in two files, so `just claude-update` raises both at once — with a version, or to the newest published one — and prints what it changed. Terraform is pinned twice as well: `ARG TERRAFORM_VERSION` and `required_version` in `infra/terraform/versions.tf`, which refuses any other. Its providers and `.terraform.lock.hcl` are raised by hand too, `terraform providers lock -platform=linux_amd64 -platform=linux_arm64`, since Dependabot would lock them with a Terraform of its own (R56). - **Container marker.** `MATHTRAIL_DEVCONTAINER=1` is set only inside the container. A session that does not see it is on the host and must stop, unless it is a cloud session the author started (see "Devcontainer only"). - **Claude Code.** Its config lives in the `canvas-api-claude` volume, so the login survives rebuilds. Auto-update is off; the version is pinned. +- **Codex.** OpenAI's coding agent works here too, as a VS Code extension pinned in `devcontainer.json`, signed in with the author's ChatGPT plan; the version is pinned, so VS Code does not update it. The extension carries its own CLI and sandbox, which keep a command's writes to the workspace and the command off the network until the author approves more. Reading is not confined: like Claude, Codex can read the whole container, the credential volumes and a local `.env` included, and what it reads goes to OpenAI. Its home, `~/.codex`, lives in the `canvas-api-codex` volume. It reads `AGENTS.md`, which sends it to this file: the rules are kept once, here (R69). - **`gh`.** What a change looks like on github.com is otherwise invisible from in here, and two parts of it are asked for by name: the alerts of the code scan, which a task closes before the next one starts, and whether the delivery that carried the change ran. `gh auth login` is answered once: the credentials live in the `canvas-api-gh` volume, as gcloud's live in `canvas-api-gcloud`, where Terraform reads them. - **Ports.** 8080 (the API) and 5173 (Vite's dev server for the SPA) are forwarded to the host. - **Just recipes.** `just --list` shows all of them. diff --git a/RUN.md b/RUN.md index df481c2..2ef79e4 100644 --- a/RUN.md +++ b/RUN.md @@ -252,7 +252,7 @@ ## Таблица 3. Вопросы автору без ответа -Собраны 2026-10-14 из «Открыто» задач T00–T11. Открытые вопросы документов — Т- (spec 20), О- (product 15.2) и П- (privacy 12) — живут в своих разделах. Те, что касаются E2, решают его задачи: Т-1 — T61, Т-16 — T67, Т-5 — T76; остальные — при разбиении E15. +Собраны 2026-10-14 из «Открыто» задач T00–T11. Открытые вопросы документов — Т- (spec 20), О- (product 15.2) и П- (privacy 12) — живут в своих разделах. Те, что касаются E2, решают его задачи: Т-1 — T61, Т-16 — T67, Т-5 — T76, П-11 — T72, до первого образца в `samples/`; остальные — при разбиении E15. | ID | Вопрос | Откуда | Предлагаю | |---|---|---|---| diff --git a/docs/decisions.md b/docs/decisions.md index 991049a..b409e1d 100644 --- a/docs/decisions.md +++ b/docs/decisions.md @@ -8,7 +8,7 @@ > > Записи R37–R43 внесены 2026-10-11 при повороте к LLM-first. Записи, которые они заменили, остались на месте с пометкой «Заменено»: их текст не правится, а их «Отвергнуто» больше не действует. Записи, которые поворот только задел, поправлены на месте, и в конце каждой стоит «Поправка 2026-10-11». Запись R44 о плане эпиками внесена тогда же, по решению автора. Запись R45 о выкладке после релиза — тоже, при сверке с mathtrail-standalone. Запись R46 о намеренных отличиях devcontainer от образца внесена 2026-10-12 при сверке с ним (T60). Записи R47 о тулчейне SPA и R48 о лицензиях npm внесены тогда же, в T05. Запись R49 о CI и его отличиях от образца внесена 2026-10-13 в T06. Записи R50 о том, что держит мерж, и R51 о находках, которые остаются открытыми, внесены тогда же, в T07. > -> Записи R60–R67 внесены 2026-10-16 (T61) по решениям автора 2026-10-15, когда E2 стал минимальным срезом с замером Gemini, а закрытая бета — эпиком E15. Запись R40 они заменили, и она осталась на месте с пометкой «Заменено». Записи R22–R24, R35, R38, R39, R42, R44, R56 и R59 поправлены на месте, и в конце каждой стоит «Поправка 2026-10-15». Запись R68 внесена 2026-10-16, в T16, по решению автора того же дня. +> Записи R60–R67 внесены 2026-10-16 (T61) по решениям автора 2026-10-15, когда E2 стал минимальным срезом с замером Gemini, а закрытая бета — эпиком E15. Запись R40 они заменили, и она осталась на месте с пометкой «Заменено». Записи R22–R24, R35, R38, R39, R42, R44, R56 и R59 поправлены на месте, и в конце каждой стоит «Поправка 2026-10-15». Запись R68 внесена 2026-10-16, в T16, по решению автора того же дня. Запись R69 о втором агенте, Codex, внесена 2026-10-16 по решению автора, вне задач плана; R46 поправлена на месте. ## Рамки и продукт @@ -203,15 +203,16 @@ Centrifugo сохраняет роль из R23. Событие `verdict` ухо - `just claude-update` получает версию через окружение, а не вставкой в текст скрипта, и до правки проверяет, что оба пина на месте. - Python и uv образца не ставятся, расширений Python нет: в canvas-api Python нет (T03). Поэтому gcloud на arm, как и прежде, идёт из образа Google (R28 standalone). - Firebase CLI, которого в образце нет, ставится автономным бинарником и, как gcloud, только на x86_64: другой сборки нет (R58). +- Расширение Codex, том `canvas-api-codex` и папка `~/.codex` под него, которых в образце нет (R69). Остальные отличия — имена проекта, сети и томов, порт 5173 для Vite (T03) и комментарии, которые описывают canvas-api. Временных отличий не осталось. Их было три, и все закрыты: - `.dockerignore` был короче образца — его список дополнил T04; - в `justfile` было два рецепта — остальные приносят задачи E1, начиная с T04; - в комментарии к SonarQube for IDE в `devcontainer.json` не было фразы о `.sonarlint/connectedMode.json`, пока не было самого файла. Фраза вернулась вместе с ним в T07. -Версии инструментов, features, расширения VS Code, кроме пакета Python, и digest базового образа совпадают с образцом: сверено 2026-10-11 и ещё раз в T60. +Версии инструментов, features, расширения VS Code, кроме пакета Python и Codex, и digest базового образа совпадают с образцом: сверено 2026-10-11 и ещё раз в T60. -— *Почему:* сверка с образцом берёт его файлы и меняет в них только то, что названо в задаче. Без этой записи следующая сверка вернула бы то, что здесь исправлено или решено иначе. — *Отвергнуто:* вернуть в этих местах файлы образца как есть — их недостатки названы выше. — `.devcontainer/`, `.gitattributes`, `.dockerignore`, `justfile`; R11; standalone: R28, R36. — *Поправка 2026-10-13:* временные отличия закрыты, список выше поправлен на месте (T07). — *Поправка 2026-10-14:* добавлен Firebase CLI (T11). +— *Почему:* сверка с образцом берёт его файлы и меняет в них только то, что названо в задаче. Без этой записи следующая сверка вернула бы то, что здесь исправлено или решено иначе. — *Отвергнуто:* вернуть в этих местах файлы образца как есть — их недостатки названы выше. — `.devcontainer/`, `.gitattributes`, `.dockerignore`, `justfile`; R11; standalone: R28, R36. — *Поправка 2026-10-13:* временные отличия закрыты, список выше поправлен на месте (T07). — *Поправка 2026-10-14:* добавлен Firebase CLI (T11). — *Поправка 2026-10-16:* добавлено расширение Codex (R69). **R47. SPA собирает Vite 8 на React 19 с `@vitejs/plugin-react`; TypeScript только проверяет типы, Biome форматирует и линтует, Vitest тестирует в happy-dom.** Тулчейн — тот же, что у виджета standalone (R84 standalone), с тремя отличиями: два — ниже, третье — в абзаце о типах. - **JSX собирает `@vitejs/plugin-react`.** В dev-сервере правка компонента обновляет его на месте, без перезагрузки страницы (Fast Refresh). Образец собирает Preact по настройкам tsconfig, без плагина. @@ -660,6 +661,23 @@ Centrifugo сохраняет роль из R23. Событие `verdict` ухо — `internal/transport/http/middleware/requestid.go`, `route.go`, `router.go`; spec 8.1; R67; standalone: R111. +**R69. Второй агент в devcontainer — Codex от OpenAI; правила для него — те же, из CLAUDE.md.** Решение автора 2026-10-16: работать в этом проекте и с Codex, по подписке ChatGPT. +- Codex ставится как расширение VS Code `openai.chatgpt` с точной версией в `devcontainer.json`, как остальные расширения. Сам CLI и песочницу (`bwrap`) расширение несёт с собой, под x86_64 и arm64, поэтому образ ничего для Codex не ставит. +- Вход в ChatGPT, настройки и история чатов — в `~/.codex`, папке Codex по умолчанию. Она живёт в томе `canvas-api-codex`, как `~/.claude` — в `canvas-api-claude`, и переживает пересборку контейнера. Dockerfile создаёт эту папку заранее, чтобы том принадлежал `vscode`. +- Песочница остаётся настройкой по умолчанию: команды пишут только в рабочую папку и не ходят в сеть, пока автор не одобрит большее. В контейнере она работает: проверено 2026-10-16 на встроенном CLI 0.162.0-alpha.17.2 — запись в рабочую папку прошла, в `~/` отказ «Read-only file system», DNS не разрешается. Чтение песочница не ограничивает: как и Claude, Codex читает весь контейнер, тома с учётными данными gcloud и gh и локальный `.env` тоже. +- Codex читает `AGENTS.md`, а не `CLAUDE.md`. `AGENTS.md` правил не повторяет: он отправляет к `CLAUDE.md` и называет, что заменяет три механизма Claude Code — `/code-review high` (ревью запускает автор, через `/review` Codex), разрешение живого прогона в `.claude/settings.local.json` (автор одобряет каждую команду) и память Claude. + +— *Почему:* правила живут в одном файле, и второй агент не может разойтись с первым. Расширение со встроенным CLI — одна точная версия в одном месте, без второй установки в образе. + +— *Отвергнуто:* +- копия правил в `AGENTS.md` — две копии разойдутся с первой же правкой; +- `project_doc_fallback_filenames = ["CLAUDE.md"]` в `.codex/config.toml` проекта — Codex читает настройки проекта, только если тот отмечен доверенным, а без `AGENTS.md` не узнал бы, чем заменить механизмы Claude Code; +- Codex CLI отдельным пакетом npm в образе — вторая точная версия того же CLI рядом с той, что несёт расширение. + +— *Открыто:* всё, что Codex прочтёт, уходит в OpenAI, как в Anthropic — у Claude Code. Репозиторий публичный, но `samples/` с образцами (R65), когда папка появится, — обработка у внешнего сервиса, которую privacy 4 и 5 не описывают ни для одного из агентов: вопрос П-11. + +— `.devcontainer/devcontainer.json`, `.devcontainer/Dockerfile`, `AGENTS.md`, `CLAUDE.md`; privacy 12 (П-11); R46. Расширение и CLI проверены по пакету из Marketplace 2026-10-16: `openai.chatgpt` 26.1007.21434 — стабильная версия от 2026-10-09, `engines.vscode ^1.96.2`, `CODEX_HOME` по умолчанию `~/.codex`. + ## Идентификация и доступ **R14. Для Firebase используем Admin SDK (`firebase.google.com/go/v4`).** Через него проверяем ID token и App Check и удаляем пользователей. `golang-jwt/jwt/v5` нужен только для подписи токенов Centrifugo. `keyfunc` не используется. diff --git a/docs/privacy.md b/docs/privacy.md index b93c72b..f30a556 100644 --- a/docs/privacy.md +++ b/docs/privacy.md @@ -1,6 +1,6 @@ # Приватность и защита данных детей -> Статус: черновик для обсуждения, 2026-09-27. Собран из блока о приватности в исходном черновике автора (в репозиторий не входит), с поправками, и исправлен по итогам ревью документов. Пересмотрен 2026-10-11 при повороте к LLM-first (R37–R43) и 2026-10-16 под эпик E2 (R60–R67). +> Статус: черновик для обсуждения, 2026-09-27. Собран из блока о приватности в исходном черновике автора (в репозиторий не входит), с поправками, и исправлен по итогам ревью документов. Пересмотрен 2026-10-11 при повороте к LLM-first (R37–R43) и 2026-10-16 под эпик E2 (R60–R67); тогда же добавлен П-11 об агентах разработки (R69). > > Документ описывает, какие данные мы собираем, где их храним, кому передаём, как удаляем и какие законы на это влияют. Обещания родителям — в [product.md](product.md), раздел 11. Технические меры — в [spec.md](spec.md), разделы 12–13. Открытые вопросы (П-…) — в разделе 12. > @@ -244,7 +244,7 @@ ## 12. Открытые вопросы -Каждый вопрос: суть, варианты, рекомендация и что он блокирует. Следующий номер — П-11. П-4 решён 2026-10-14: данные беты хранятся и обрабатываются в США, в `us-east4` и рядом с ним; семьи из ЕС и Великобритании — отдельным решением со SCC/DPF (R54). +Каждый вопрос: суть, варианты, рекомендация и что он блокирует. Следующий номер — П-12. П-4 решён 2026-10-14: данные беты хранятся и обрабатываются в США, в `us-east4` и рядом с ним; семьи из ЕС и Великобритании — отдельным решением со SCC/DPF (R54). | № | Вопрос | Варианты | Рекомендация | Блокирует | |---|---|---|---|---| @@ -257,6 +257,7 @@ | П-8 | Может ли почерк считаться биометрией (поправки COPPA 2025 расширили понятие персональной информации) | нет — почерк не используется для идентификации; да — нужны дополнительные меры | наша оценка «нет»: почерк не используется для идентификации; картинки почерка уходят в Gemini, но не хранятся, а время и нажим штрихов не покидают устройство (R42). Если исследование E2 выберет для беты штрихи вместо картинки, оценка пересматривается (R66). Подтверждает юрист | уведомление | | П-9 | Журналы запросов Cloud Run с IP-адресами | оставить 30 дней; исключить часть журналов; сократить срок | сократить срок хранения журналов запросов до минимума, нужного для безопасности | Terraform (Logging) | | П-10 | Детские образцы в eval-наборах | только образцы взрослых, в том числе нарочно «детский» почерк; детские образцы с отдельным письменным согласием родителя | отдельное согласие. В нём сказано, что образцы хранятся у оператора не больше 12 месяцев, уходят в Gemini при каждом прогоне eval-набора и удаляются по запросу. Решается при разбиении беты (E15): в E2 eval идёт только на образцах автора (R65) | eval-наборы на детском почерке, текст согласия | +| П-11 | Образцы почерка и агенты разработки | Claude Code и Codex (R69) читают весь контейнер, и прочитанное уходит в Anthropic и OpenAI; `samples/` лежит в рабочей папке. Варианты: агенты не открывают `samples/`; образцы — вне рабочей папки; у тарифа агента выключено обучение на данных пользователя — для ChatGPT это проверить в его настройках управления данными | в E2 образцы — самого автора, взрослого: агенты `samples/` не открывают; до первого образца автор выключает обучение у обоих тарифов и сверяет это в их настройках. Детские образцы (П-10) в рабочую папку не попадают | `samples/` (R65, T72), детские образцы в eval (П-10) | ## 13. Источники diff --git a/docs/stack.html b/docs/stack.html index 6e4d350..3eed827 100644 --- a/docs/stack.html +++ b/docs/stack.html @@ -411,7 +411,7 @@

После MVP: платформа MathTrai @@ -566,7 +566,8 @@

После MVP: платформа MathTrai { n: "Docker Compose", v: "5.5.1", r: "Postgres, OpenFGA, Centrifugo и Redis для локального запуска", s: "repo", ref: "Dockerfile" }, { n: "cloudflared", v: "2026.9.1", r: "Туннель: SPA из контейнера на настоящем планшете", s: "repo", ref: "Dockerfile · E15" }, { n: "gh", v: "2.101.0", r: "Алерты сканеров и статус доставки с github.com", s: "repo", ref: "Dockerfile" }, - { n: "Claude Code", v: "2.1.280", r: "CLI и расширение одной версии; автообновление выключено", s: "repo", ref: "Dockerfile · devcontainer.json" } + { n: "Claude Code", v: "2.1.280", r: "CLI и расширение одной версии; автообновление выключено", s: "repo", ref: "Dockerfile · devcontainer.json" }, + { n: "Codex", p: "расширение openai.chatgpt", v: "26.1007.21434", r: "Второй агент по подписке ChatGPT; CLI и песочница — в самом расширении; правила — CLAUDE.md через AGENTS.md", s: "repo", ref: "R69 · devcontainer.json" } ]} ];