Skip to content

CLI вокруг цикла урока и MCP-сервер softpractice mcp - #5

Merged
arthur-s merged 14 commits into
mainfrom
claude/bold-turing-w3b9bz
Sep 30, 2026
Merged

arthur-s merged 14 commits into
mainfrom
claude/bold-turing-w3b9bz

Conversation

@arthur-s

@arthur-s arthur-s commented Sep 29, 2026 •

Copy link
Copy Markdown
Owner

Что сделано

CLI перестроен вокруг цикла урока, чтобы по нему было удобно работать и из терминала, и из Claude Code / Codex. Поверх общего слоя use case добавлен локальный MCP-сервер softpractice mcp. Обратная совместимость не сохраняется: у CLI ещё нет выпущенных версий с внешними потребителями.

Команды

Команда Что делает
status [--json] состояние проекта и следующий шаг; в JSON — next_actions: [{code, command?, url?}]
task [--json] задание той версии урока, которая лежит в папке
material [--lesson ID] [--json] теоретический материал этого урока или урока --lesson
hint [--json] уже открытые подсказки; следующая открывается на странице задания (ссылка в выводе)
submit [--wait] [--timeout] [--json] отправка; с --wait сразу ждёт результат
result [--id] [--wait] [--timeout] [--directions] [--json] результат проверки в Markdown
submissions [--json], submissions download --id последние отправки урока (/iteration), скачивание ревизии
open [task|material|result] страница в браузере; без аргумента — результат, до первой отправки — задание
mcp [--project DIR] MCP-сервер на stdio для агентов (см. ниже)

Эти команды заменяют assignment show, evaluation, submission show, submission download и --format json. Для json-v2 и contract_version замены нет.

Следующий шаг в status

Возможные шаги: apply_update → update; commit_changes; submit → submit --wait; wait_result → result --wait; read_result → result; answer_questions → ссылка на страницу результата; update_lesson_version. Новый коммит после отправки определяется сравнением HEAD с revision.commit_sha из GET /v1/submissions/{id}.

Что выводит result

  • Всегда: автоматические проверки с контрпримерами (сценарий, вход, ожидание), summary ревью, рубрика с обоснованиями, замечания (заголовок, наблюдение, риск, где), причина неуверенности.
  • Только с --directions: direction замечаний и next_step контрпримеров. Без флага выводится directions_hidden: true и строка с подсказкой про флаг.
  • Вопросы рецензента CLI не показывает, только их число.
    • Если на них нужно ответить (authoritative uncertain, questions_answerable: true), result и status пишут «Ответьте на странице результата: <ссылка>».
    • Остальные вопросы названы вопросами для самопроверки на странице результата.
  • Уровень по умолчанию задаёт одна константа defaultReviewFeedback в cmd/softpractice/feedback.go.

Ожидание, ошибки, коды выхода

  • Ожидание: при 202 пауза ровно по Retry-After; 429 с Retry-After во время ожидания тоже выжидается. 409 evaluation_superseded — отдельный исход.
  • Коды выхода: 0 — готово, 1 — ошибка, 2 — неверные аргументы, 3 — проверка ещё не готова (в том числе истекло время ожидания), 4 — проверка заменена.
  • Ошибки в JSON-режиме: stdout содержит только JSON; при ошибке это документ softpractice.error с кодом usage, login_required, project_not_linked, not_git_repository, checks_not_published, api_error, cancelled или error. У submit --json всё, что адресовано человеку (превью, проверки, подтверждение), идёт в stderr. Процедуру входа (device flow) CLI сам никогда не запускает.
  • Исправлено попутно: open без отправок вёл на /workspaces/{id} — такой страницы в вебе нет.

Общий слой use case

cmd/softpractice/usecase.go, learnerUseCases: Status, Lesson, Task, Material, Hints, Submissions, SubmittedCommit, PrepareSubmission/SendSubmission, PrepareUpdate/ApplyUpdate, CheckPublished, Evaluation, LatestSubmission. Use case ничего не пишут в stdout. В learnercli добавлены ErrLoginRequired, ErrProjectNotLinked и AuthorizedPoll, который возвращает Retry-After.

  • Evaluation сам строит проекцию на запрошенном уровне детализации; сырая проекция API (с direction, next_step, defense_questions) из use case не выходит.
  • PrepareUpdate делает все проверки update и возвращает план (вид обновления, откуда → куда, операции add/replace/delete), ничего не меняя. ApplyUpdate отказывает, если HEAD или рабочее дерево сдвинулись после подготовки. update_plan.go.
  • CheckPublished запускает публичные проверки, только если .softpractice/checks.json совпадает с конфигурацией, которую сервер публикует для урока папки. CLI-команда check не изменилась.

softpractice mcp

cmd/softpractice/mcp.go, SDK github.com/modelcontextprotocol/go-sdk v1.8.0 (Apache-2.0 с переходом с MIT, go 1.25). stdio через IOTransport поверх stdin/stdout из run().

Инструмент Вход Что вызывает
status — Status; next_actions содержат tool/arguments вместо CLI-команд
task — Task
material lesson_id? Material
hints — Hints (только открытые)
check — CheckPublished; {passed, failure, output, truncated}
submit wait_seconds 0–600 PrepareSubmission → подтверждение → SendSubmission → Evaluation
result submission_id?, wait_seconds LatestSubmission + Evaluation
submissions — Submissions
update — PrepareUpdate → подтверждение → ApplyUpdate

Resources: softpractice://lesson/task, softpractice://lesson/material (Markdown). Prompts нет.

  • Язык. Имена, описания, схемы, instructions сервера и коды ошибок — на английском (их читает модель). Сообщения для человека (подтверждение, ошибки, progress) — на языке CLI; если язык нигде не задан (--lang, SOFTPRACTICE_LANGUAGE, set lang, локаль), MCP-сервер говорит по-русски, на языке курса. instructions сообщают модели, что материалы курса на русском.
  • Подтверждение submit и update. В протоколе 2026-07-28 сервер не может слать elicitation/create посреди вызова инструмента, поэтому подтверждение сделано как multi round-trip результат (SEP-2322): первый вызов возвращает InputRequests.confirm с превью (урок, commit, файлы и размер, предупреждения о чувствительных файлах / список add/replace/delete), клиент спрашивает человека и повторяет вызов. Старым клиентам SDK сам отправляет elicitation/create. Состояние подписано HMAC-ключом процесса; при повторе действие готовится заново и отклоняется с changed_since_confirmation, если отличается от показанного. Клиент без elicitation получает confirmation_unavailable — ничего не делается.
  • Границы. Проекция результата закреплена константой mcpReviewFeedback = feedbackWithoutDirections, параметра нет; текст вопросов рецензента не отдаётся; подсказки не открываются; login/logout, retry, starter/restore, set, open не входят. Локальные проверки внутри MCP-submit не запускаются (local_checks: "not_run"), для этого есть check.
  • Ожидание. wait_seconds ограничивает ожидание, паузы — по Retry-After; на каждом pending-ответе отправляется progress-уведомление, если клиент передал токен.
  • Ошибки — isError с JSON {"error": {code, message, action?, http_status?, api_code?, support_id?}}; action говорит человеку, что сделать (softpractice login, --project, softpractice check).
  • stdout принадлежит JSON-RPC, логи SDK — в stderr.

Ограничение: это границы инструментов MCP. Агент с доступом к shell может вызвать softpractice result --directions или submit --yes сам; настоящая граница возможна только на сервере (см. ниже).

Тесты

Покрыто фейковым API (httptest):

  • ожидание: 202+Retry-After (проверяются фактические паузы), 429, 409, истечение таймаута;
  • ошибки: 401 и отсутствие входа — без токенов в выводе; нет связанного проекта или git-репозитория — для всех новых команд; неверные аргументы дают usage-ошибку;
  • вывод result: что показывается без --directions и с ним; текст вопросов не выводится никогда; ссылка для ответа на вопросы;
  • остальные команды: task и material берут версию урока из папки, material --lesson, hint, submit --wait --json, open task;
  • правила следующего шага (8 случаев) и фиксированные наборы ключей JSON;
  • update: подготовка ничего не меняет, применение отказывает после сдвига HEAD;
  • CheckPublished: изменённая конфигурация не запускается;
  • MCP (in-process клиент): состав и аннотации инструментов; маркеры direction, next_step, defense_questions не встречаются ни в result, ни в submit с ожиданием; подтверждение, отказ и принятие на протоколах 2026-07-28 и 2025-11-25; отказ без elicitation; changed_since_confirmation; превью update; login_required без токенов и без device flow; run(["mcp"]) через stdio пишет в stdout только JSON-RPC.

Прогнаны go test ./... (и -race для новых тестов), go vet, gofmt, сборка под linux/windows/darwin. Два теста границ проверены мутациями: включённые directions и снятая проверка elicitation их валят.

Решения

  • Доспрашивание остаётся в вебе. CLI и MCP сообщают, что рецензенту нужны ответы, и дают ссылку на страницу результата. Бэкенд для этого менять не нужно, и ответ по устройству остаётся за человеком (веб-сессия). Команды explain нет.
  • Подсказки: показываются только уже открытые. Следующую открывают на странице задания — это действие студента, которое фиксируется как evidence.
  • Файлы AGENTS.md/CLAUDE.md в стартовых проектах — отдельной итерацией.

Пробелы и расхождения в API (monorepo не менялся)

  • Похоже на баг: в OpenAPI POST /evaluation/retry помечен как только-веб, а обработчик retryEvaluation использует authenticate(..., ScopeWorkspaceRead) и принимает CLI-токен; защищает только requireOrigin, а заголовок Origin вне браузера подделывается. Токен с правом чтения может запустить изменяющее действие.
  • В current-assignment у latest_submission нет evaluation_status и commit_sha, поэтому status делает до двух дополнительных запросов.
  • Ссылки на страницы веба (/assignments/{id}?version=N, …/material, /submissions/{id}/result) CLI собирает сам; в API их нет.
  • По 404 для материала нельзя отличить «материала нет» от «не найдено». У ReviewFeedbackItem нет категории.
  • Фильтр direction работает только на стороне клиента и не является границей безопасности: API отдаёт полную проекцию CLI-токену. Настоящая граница — не отдавать direction/defense_questions токену CLI или вынести их в отдельный scope.

Не проверено

Реальный device flow, локальный gateway https://local.softpractice.ru/, ответы реального API: устаревшая версия урока, отсутствующий материал, material --lesson для прошлого урока, prepared-hints и /iteration по CLI-токену. MCP-сервер проверен in-process клиентом SDK и сырым JSON-RPC через stdio, но не в самих Claude Code, Codex и Claude Desktop: поддержка elicitation в каждом из них не проверялась.

🤖 Generated with Claude Code

https://claude.ai/code/session_01XKQjpw1FmuSnc8k4EtFspp

ErrLoginRequired and ErrProjectNotLinked let a caller tell a state a person
must fix from other failures without matching messages; the messages are
unchanged. AuthorizedPoll returns the status and Retry-After of a pending
202 answer, and of an error answer such as 429.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K9oUWMHw3ZCHpfmoBGcgeF
status, submit, submission show, and update now call learnerUseCases,
which return results instead of printing them, so a later
`softpractice mcp` can call the same operations. Command output is
unchanged.

status --format json gains additive fields: assignment.title,
lesson_version_update while a newer lesson version is published, and
latest_submission.evaluation_status once the evaluation is terminal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K9oUWMHw3ZCHpfmoBGcgeF
`softpractice evaluation [--wait] [--timeout]` reads
/v1/submissions/{id}/evaluation, waits at the Retry-After interval the
server sets, and reports a superseded evaluation and a wait that ends
before the result as separate outcomes with exit codes 4 and 3.
`softpractice assignment show` prints the assignment text of the lesson
version the folder holds and its theory material.

Both print JSON in the existing contract_version/kind format and, in JSON
mode, a softpractice.error document that says when a login or a linked
project is required. machineReviewFeedback keeps the reviewer's
recommendations and other free text out of this output by default.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K9oUWMHw3ZCHpfmoBGcgeF
The CLI is not released yet, so there is no v2 consumer to keep. v2
passed the whole evaluation projection through, including the reviewer's
recommendations and the defense questions; `--format json` is now the
only machine format.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K9oUWMHw3ZCHpfmoBGcgeF
…version

`softpractice evaluation` now prints what the checks and the reviewer
found and why: counterexamples, the review summary, the rubric with
rationales, and each finding's observation and risk. The reviewer's
directions and the counterexamples' next steps are printed only with
--directions; defaultReviewFeedback keeps them out otherwise. The
reviewer's questions are never printed, only their number.

The text output is Markdown so it reads in a terminal and renders in an
agent's chat. The JSON documents lose contract_version, since the CLI has
no released consumers to version for, and the evaluation document carries
one `result` object instead of two overlapping ones.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K9oUWMHw3ZCHpfmoBGcgeF
@arthur-s arthur-s changed the title CLI для ИИ-агентов: evaluation --wait, assignment show, общий слой use case CLI для работы из ИИ-агентов: evaluation, assignment show, общий слой use case Sep 30, 2026
Commands follow what a learner does: task, material (--lesson for an
earlier lesson), hint, submit --wait, result, submissions, and
open task|material|result. They replace assignment show, evaluation,
submission show, and submission download (now submissions download).

status ends with the next step, and lists next_actions in JSON: apply
the update, commit, submit, wait, read the result, or answer the
reviewer's questions on the result page. A new commit after a submission
is detected from the submission's commit_sha.

The reviewer's questions and opening the next hint stay on the site:
result, status, and hint give the page link. The CLI never prints the
questions' text.

Every JSON-capable command takes --json and prints a softpractice.error
document on failure. Invalid arguments exit with code 2. open no longer
links to /workspaces/{id}, which the web app does not serve.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K9oUWMHw3ZCHpfmoBGcgeF
@arthur-s arthur-s changed the title CLI для работы из ИИ-агентов: evaluation, assignment show, общий слой use case CLI вокруг цикла урока: task, material, result, submit --wait, общий слой use case Sep 30, 2026
- With --json, every error is a softpractice.error document, including an
  invalid command line (code usage): the commands now report errors in one
  deferred place and read --json from the raw arguments.
- Declining the submit confirmation is error code declined (exit 1) instead
  of a silent success with empty stdout.
- status next steps: practicum_completed with the completion page for a
  completed workspace, workspace_inactive for an archived one,
  retry_evaluation with the result page after a technical failure,
  resubmit after a superseded evaluation, and stash_changes before
  apply_update on a dirty tree.
- A malformed --id is a usage error (exit 2) for result and
  submissions download.
- open accepts the page after the flags, and open result falls back to the
  accepted predecessor like result does.
- submit --wait accepts --directions.
- Drop the unused evaluation projection built by
  normalizeSubmissionResponse.
- Tests for JSON usage errors, declined submit, submit --json receipt,
  rate limits without an answer, cancellation while waiting, token refresh
  during polling, and the new next steps.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Xxe94ZEJnVQm1pzvTR1aK
- Evaluation projects the evaluation at a requested detail itself; the raw
  API projection, with directions and reviewer questions, no longer leaves
  the use case.
- update is split into PrepareUpdate, which checks everything and lists the
  file operations without touching the folder, and ApplyUpdate, which
  refuses when HEAD or the working tree moved since the plan was made.
- CheckPublished runs the public checks only when .softpractice/checks.json
  matches the configuration the server publishes for the folder's lesson.

The CLI behaves as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XKQjpw1FmuSnc8k4EtFspp
`softpractice mcp [--project DIR]` serves the lesson loop to Claude Code,
Codex, Claude Desktop and other MCP clients through the same use cases
as the CLI: status, task, material, hints, check, submit, result,
submissions and update, plus the task and material as resources.

- Tool names, descriptions and schemas are in English for the model;
  messages for the person follow the CLI language, Russian when none is
  configured, since the course content is Russian.
- submit and update ask the person to confirm with a preview (commit and
  size; the add/replace/delete list). The confirmation is a multi
  round-trip tool result, so it works on protocol 2026-07-28 and, through
  the SDK, as elicitation/create on older clients. A client without
  elicitation gets confirmation_unavailable; nothing is done. The retry
  re-prepares the action and refuses when it changed after the person
  saw it; the state it carries is signed by the server process.
- Evaluations are always projected without directions, and the reviewer's
  questions are never returned; there is no parameter for either. Hints
  are read-only. check runs only the configuration the server publishes.
- Errors are isError results with a stable code and what the person has
  to do. The server never starts a sign-in, and only JSON-RPC reaches
  stdout.
- status next_actions name the tool to call instead of a CLI command.

Uses github.com/modelcontextprotocol/go-sdk v1.8.0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XKQjpw1FmuSnc8k4EtFspp
How to connect it to Claude Code, Codex and Claude Desktop, the tools and
resources it serves, what it leaves to the learner (confirmation,
directions, reviewer questions, hints, unpublished checks), the error
format, and the language of its messages. The npm README is kept equal to
README.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XKQjpw1FmuSnc8k4EtFspp
@arthur-s arthur-s changed the title CLI вокруг цикла урока: task, material, result, submit --wait, общий слой use case CLI вокруг цикла урока и MCP-сервер softpractice mcp Sep 30, 2026
claude and others added 4 commits September 30, 2026 09:26
On Windows the system locale reports the display language, so the MCP
server correctly answers in English there instead of the Russian default.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XKQjpw1FmuSnc8k4EtFspp
After a commit git may start a detached gc or maintenance process that
keeps writing to .git after the test returns, so t.TempDir cleanup fails
with "directory not empty" (seen on the ubuntu runner, git 2.55).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XKQjpw1FmuSnc8k4EtFspp
60 seconds equals Codex's default tool call timeout, so a suggested wait
could time out the call before the result arrives.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XKQjpw1FmuSnc8k4EtFspp
@arthur-s
arthur-s merged commit 35353a0 into main Sep 30, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants