diff --git a/.project-docs/30-worklog/tasks/20260928-teacher-protocol-boundary-f2c8a601.md b/.project-docs/30-worklog/tasks/20260928-teacher-protocol-boundary-f2c8a601.md
new file mode 100644
index 00000000..601f1778
--- /dev/null
+++ b/.project-docs/30-worklog/tasks/20260928-teacher-protocol-boundary-f2c8a601.md
@@ -0,0 +1,62 @@
+# Task: Keep teacher runtime instructions scoped to entry facts and UI protocol
+
+## Identity
+
+- Task ID: 20260928-teacher-protocol-boundary-f2c8a601
+- Mode: Feature
+- Branch: codex/20260928-teacher-protocol-boundary-f2c8a601-teacher-protocol-boundary
+- Worktree: /Users/chillishark/Makelore 麦洛/.codex-worktrees/makelore/20260928-teacher-protocol-boundary-f2c8a601
+- Base commit: 998796d77cba146ae2a007cbebfdaa1255a3413e
+- Owner: codex
+- Status: Ready for Integration
+
+## Scope
+
+- Continue the accepted teacher/client boundary on top of completed legacy-component cleanup commit `998796d77cba146ae2a007cbebfdaa1255a3413e`.
+- Keep entry markers factual and the reply protocol technical. Remove locally appended teaching strategy from active help, guided help, background checks, and reply-format instructions.
+- Enforce the current 0–3 shortcut capacity for newly generated answers while preserving archived replies and exact diagnostics.
+- Update focused tests and README; preserve card styling and existing interaction/scheduling machinery.
+
+## Intent And Constraints
+
+- User assigned main merge and push to another conversation. This task stays in its isolated feature branch and prepares a tested commit for integration.
+- Cloud configuration owns Alice's teaching purpose, focus, tone, length, and shortcut prefixes; the host owns invocation facts, available context/tools, and interface protocol.
+- Keep old component cleanup intact. Do not merge the older `89c913b` patch wholesale because it modifies a superseded protocol.
+- No cloud configuration publishing, live app restart, scheduler redesign, or teacher-to-operator consensus handoff in this task.
+- Gate passed before edits: task ownership/status verified; entry, task-relevant memory, and peer scopes reviewed. Relevant canonical documents are unchanged between the previously read baseline and `998796d`; shared memory remains read-only.
+
+## Outcome
+
+- Active-help and guided-help messages now identify the student's entry intent; background checks identify a program-triggered check without a fabricated student question. They no longer mandate a teaching strategy.
+- Reply instructions now declare only the two fields, Markdown body support, optional 0–3 nonempty shortcut strings, and delegation of content/style to published configuration.
+- Main rejects excess newly generated shortcuts without silently truncating or rewriting them: prose is retained, cards are withheld, and the exact raw answer and format diagnostic are saved. 0/1/3-card replies preserve prefixes and duplicates. The shared compatibility parser and historical store remain unchanged.
+- The UI now shows recorded reply-format errors beside the preserved answer; raw content stays collapsed and inert. Existing colors, layout, click handling and drafts are unchanged.
+- Regression coverage includes local/cloud service persistence, history read/list/save, all entry intents, current/legacy envelopes, and UI behavior. README reflects the responsibility boundary and current capacity.
+- No merge, push, running-app restart, cloud config change, or canonical memory edit performed.
+
+## Verification
+
+- Dependency installation: pinned pnpm 10.33.4, frozen lockfile, offline cache; no tracked dependency change.
+- Focused final suite: 269/269 passed across teacher-guidance, teacher-reply, teacher-reply-history, coding-teacher, coding-teacher-cloud, coding-teacher-model and coding-teacher-ui.
+- `corepack pnpm run typecheck`: passed.
+- ESLint for all changed TypeScript/TSX files, including the Electron E2E spec: passed.
+- `corepack pnpm run build:vite`: passed after the final UI change.
+- `git diff --check` and task-aware project-doc drift check: passed.
+- Independent read-only review found no remaining teaching-policy directives in the scoped runtime files and confirmed lossless historical handling.
+- Added a focused Electron E2E case for visible format errors, absent rejected shortcut buttons, collapsed exact raw text, and preserved drafts. Playwright test collection passed using an existing Electron distribution override; the native test was not executed to avoid launching another app window during concurrent work. Component/service assertions covering these behaviors passed.
+- Real published-model responses were not tested. Existing topics retain their pinned configuration versions; code tests do not establish that an updated cloud prompt is active.
+
+## Follow-ups
+
+- Integrate this commit after the legacy-component cleanup; merging/pushing belongs to the user's other conversation.
+- Validate actual Alice output with a new topic using the published cloud version. Local tests cannot prove model behavior or that existing pinned topics picked up a new prompt.
+- Three-operation-round checks and shared confirmed context remain separate work.
+
+## Promotion Candidates
+
+- Target: canonical teacher decision/current-state documents during integration.
+ - Proposal: runtime help/check-in markers convey entry facts only; teaching strategy is entirely in the selected published agent configuration. Current newly generated replies accept 0–3 shortcuts; historical card counts remain lossless.
+ - Evidence: context/reply code and focused tests in this task; user's accepted client/agent boundary in this conversation.
+ - Future impact: future personas can use the same interface without inheriting Alice's teaching policy; prompt changes are evaluated against the actual published topic version.
+ - Semantic conflicts: supersedes cleanup task's remaining locally mandated single discussion focus and unrestricted new-card count; retains historical compatibility and UI cleanup.
+ - Human confirmation: not additionally required for these already accepted boundaries; no canonical edits in this feature task.
diff --git a/README.md b/README.md
index 4aa2b4f1..b291f5eb 100644
--- a/README.md
+++ b/README.md
@@ -172,10 +172,10 @@ Pi 正式包必须继续运行 `pnpm run verify:artifact:pi`、`pnpm run smoke:p
- 主动发言采用紧贴小头像的短气泡;只有存在真实主动消息或运营欢迎语时才显示这组浮层,收起消息时头像和气泡一起消失,顶部入口仍保留。长消息在气泡中最多显示三行,点击接回原智能体对话查看全文。不提供“智能体偶尔来看看”开关或本地模拟巡看控制,既有自动跟进与真实消息接收逻辑保留。作品原生预览对整组气泡和头像测量避让,避免图片被原生页面遮住。
- 学生在当前项目创作时,前端每 5 分钟请求一次智能体跟进;窗口隐藏、来源归档、主对话正在执行、智能体正在回复或学生正在智能体栏写草稿时延后。Main 再检查在线启停、来源归属、项目级冷却和已完成文本指纹,未变上下文按下述十五分钟冷却处理。跟进沿用运营模型、已发布 Skills 与当前智能体话题,生成符合所选配置的具体建议或引导;以主动智能体发言持久化,不伪造学生消息。模型调用沿用智能体计费规则。
- 进入项目时,智能体头像旁先显示运营发布的欢迎语气泡,不调用模型、不声称已检查项目;每个账号/项目主动收起后不重复弹出。真实的未读主动建议优先替换欢迎语。主动发言在头像旁直接显示气泡,不抢焦点、不盖住作品;作品区按气泡与原生预览的实际交叠范围避让,已有工具栏高度计入计算,气泡位于独立智能体栏时不额外留白,窗口/分栏尺寸变化和收起气泡时实时归还空间;“和智能体聊聊”接回同一段咨询,“等会儿聊”收起并保留历史。已有未处理气泡时不堆叠新邀请;咨询已展开时也显示气泡,直到学生主动点击查看或收起,期间的其他回复不会吞掉未读邀请。五分钟检查计时在刷新和项目切换后保留;智能体咨询里的新讨论也算进展,即使操作对话没有变化也会继续跟进;上下文完全未变时放缓到十五分钟一次,不永久停止。事件连接丢失时,每十五秒只读同步未完成回复,收到结果后停止轮询,不重复调用模型。失败如实提示,不以本地文案替代模型回复。已接受请求恢复使用原身份,退出账号和来源删除仍沿用 Main 中止边界。
-- 智能体同时支持自由提问与输入框底部的提示词按钮“帮我看看”:学生点击后才结合当前操作对话与咨询历史推荐一个具体切入点,并生成围绕它的快捷回复。点击快捷回复会主动开始讨论;“我也说不清,你带我看看”让智能体从一个具体、容易回答的交流起点带着聊。这些快捷求助保留已有输入草稿,网络结果不确定时复用请求身份,已确认终态的请求不重复执行;解析失败可重新求助,不用固定问题伪装模型结果。服务端下发的推荐问题只填入草稿,由用户确认发送。界面不提供独立示范页面、“记一下”、共识或自动待办,智能体的回复下不再展示“我去试一试”和“复制”按钮,学生通过已有的操作对话/作品入口继续创作。
+- 智能体同时支持自由提问与输入框底部的提示词按钮“帮我看看”:点击后程序说明学生主动求助且尚未提出具体问题;“我也说不清,你带我看看”说明学生暂时说不清想问什么。智能体结合可用上下文,按云端配置回应。点击快捷回复会主动开始讨论。这些快捷求助保留已有输入草稿,网络结果不确定时复用请求身份,已确认终态的请求不重复执行;解析失败可重新求助,不用固定问题伪装模型结果。服务端下发的推荐问题只填入草稿,由用户确认发送。界面不提供独立示范页面、“记一下”、共识或自动待办,智能体的回复下不再展示“我去试一试”和“复制”按钮,学生通过已有的操作对话/作品入口继续创作。
- 老师咨询只展示老师正文和可点击的快捷回复卡片,学生也可在“和老师聊聊”输入框自由输入。点击卡片原样发送该回复,保留当前输入草稿;旧话题的数据不会恢复想法板、结构图、流程图或对照表及其操作。
-- “帮我看看”和引导开口都由老师结合项目推荐一个具体切入点,快捷回复围绕这个切入点帮助学生接话;客户端不要求多个独立话题,也不固定正文长度、选项数量、前缀或表达风格。老师继续依据已配置职责帮助孩子整理想法、理解关系和承接已确认的共识,区分建议与已确认内容。
-- Main 对咨询回复统一使用 `{reply, quickReplies}`,本地读取工具的前言和云端非最终片段不当作最终回答;完整解析后展示正文和卡片,主动进展提醒仍直接展示正文。普通 Markdown、JSON 数据和代码示例保持正文。兼容读取旧 `{intro, questions}` 和带 `tool` 的回复,只提取正文与快捷回复,不恢复或更新组件。资源上限只用于防护,不做旧式字数截断;格式错误保留可读正文及原始回答,原文默认折叠、字面显示,不再次送进模型。
+- 客户端只提供入口事实、可用上下文和界面协议。讨论切入点、教学方式、是否提问、正文长度、前缀和表达风格由所选智能体的云端配置决定;不在“帮我看看”、引导开口或后台检查时附加教学策略。当前新回复允许 0–3 条快捷回复,没有时用空数组,不要求凑满。
+- Main 对咨询回复统一使用 `{reply, quickReplies}`,本地读取工具的前言和云端非最终片段不当作最终回答;完整解析后展示正文和卡片,主动进展提醒仍直接展示正文。普通 Markdown、JSON 数据和代码示例保持正文。兼容读取旧 `{intro, questions}` 和带 `tool` 的回复,只提取正文与快捷回复,不恢复或更新组件。新输出超过 3 条快捷回复时保留完整正文、提示格式错误并保留原始回答,不展示超额卡片或静默截取;历史中已有的更多卡片在读取与后续保存时仍原样保留。独立资源上限只用于防护,不做旧式字数截断;格式错误的原文默认折叠、字面显示,不再次送进模型。
- 咨询正文支持 Markdown 标题、列表、表格、代码围栏、HTTP(S) 链接/图片和数学公式,长代码与表格在栏内横向滚动,不执行 HTML。云端正文维持 string 合同,未知对象不会猜测转成回答。工具活动只从云端主线程的类型化工具事件及 Main 本地读取过程获得,按本次问题/工具调用身份合并名称和状态,跨云端暂停、续接仍只计一次;暂停读取不算失败,状态以实际读取结果或问题终态为准,单独折叠显示;参数、结果原文和内部错误不混入回复,思考内容不作为正文。客户端仅保存自己实际收到的工具状态,不能补回旧历史或断线期间已经过期的事件。
- 云端咨询在 local_context 声明 read_protocol=2;需先部署配套 Yuxi API 和 worker,再升级客户端。云端持久累计读取字节及批次,每轮告知模型剩余额度;预算耗尽后消费最后一批结果,并以 tool_choice=none 要求根据现有证据形成答案和说明缺口。Main 限制实际返回量并拒绝第十三批读取,区分读取达到上限、上下文失效及格式无效。服务端对未声明协议的已安装旧客户端保留原工具参数与六批边界。
- 智能体人设、职责、提示词和 Skills 由发布配置决定。Main 不追加固定教学基线、不生成“小麦”、不清空所选智能体的 Skills,只添加真实工具能力、上下文边界和本轮界面协议。智能体名称叫“老师”“朋友”或“代码顾问”不改变调用路径或权限。
diff --git a/electron/coding-teacher/context.ts b/electron/coding-teacher/context.ts
index bc90a65c..3f94c81c 100644
--- a/electron/coding-teacher/context.ts
+++ b/electron/coding-teacher/context.ts
@@ -112,7 +112,7 @@ export function compileTeacherContext(
const current: TeacherModelMessage = {
role: intent === 'check-in' ? 'system' : 'user',
content: intent === 'check-in'
- ? '本轮是一次项目进展提醒,不是用户提问。按照已配置的人设和职责,结合来源操作对话中已完成的文字和咨询历史回应,表达方式遵循已配置的要求,不要求用户立即回答。仅依据已有证据,不重复上次提醒,不声称实际运行或试玩过作品。'
+ ? '本轮入口:程序触发的项目进展检查,学生没有在本轮主动提问。回应方式遵循当前智能体的云端配置。'
: [
...references.map(
(ref) =>
@@ -122,10 +122,10 @@ export function compileTeacherContext(
ref.text
),
'当前问题:\n' + question,
- ...(intent === 'suggestions' || intent === 'guided-help'
- ? [
- '本轮学生希望你帮忙找到交流起点。依据当前项目、来源操作对话和咨询历史,推荐一个具体切入点,并说明为何值得从这里聊;快捷回复围绕这个切入点帮助学生接话。没有可用上下文时,坦诚从构思切入,不假定学生已完成功能,不编造作品表现或项目进展。',
- ]
+ ...(intent === 'suggestions'
+ ? ['本轮入口:学生通过“帮我看看”主动请求帮助,尚未提出具体问题。']
+ : intent === 'guided-help'
+ ? ['本轮入口:学生主动求助,表示暂时说不清想问什么。']
: []),
].join('\n\n'),
};
diff --git a/electron/coding-teacher/reply.ts b/electron/coding-teacher/reply.ts
index 0356e4f8..44e25b38 100644
--- a/electron/coding-teacher/reply.ts
+++ b/electron/coding-teacher/reply.ts
@@ -1,13 +1,21 @@
import type { TeacherRequest } from '../../shared/coding-teacher';
import { parseTeacherReply } from '../../shared/teacher-reply';
+const MAX_CURRENT_QUICK_REPLIES = 3;
+
export function teacherReplyInstructions(): string {
- return '本轮界面接收一个 JSON 对象 {"reply":string,"quickReplies":string[]}。reply 是回复正文,可使用 Markdown;quickReplies 是可选的后续回复建议,没有建议时用空数组。只使用这两个字段。依据已配置职责帮助学生整理想法、理解关系、承接已确认的共识;区分学生已确认内容与智能体建议、待定想法。回复的语言、风格、长度、前缀和建议内容遵循当前智能体的云端配置。';
+ return `本轮界面接收一个 JSON 对象 {"reply":string,"quickReplies":string[]}。reply 是回复正文,可使用 Markdown;quickReplies 是可选的快捷回复,最多 ${MAX_CURRENT_QUICK_REPLIES} 条,每条为非空字符串,没有快捷回复时用空数组。只使用这两个字段。回复的内容、语言、风格、长度和前缀遵循当前智能体的云端配置。`;
}
/** The original response remains available for diagnosis when parsing is incomplete. */
export function applyTeacherReply(request: TeacherRequest, raw: string): void {
const parsed = parseTeacherReply(raw);
+ // Apply the current UI contract only to new replies; archived replies keep
+ // their original cards through the compatibility parser and topic store.
+ if (parsed.quickReplies.length > MAX_CURRENT_QUICK_REPLIES) {
+ parsed.quickReplies = [];
+ parsed.parseError ??= '快捷回复超过当前界面最多 3 条的限制,正文已保留,快捷回复未展示。';
+ }
request.response = parsed.reply;
request.suggestedQuestions = parsed.quickReplies;
if (parsed.parseError) {
diff --git a/src/pages/Chat/TeacherChatPanel.tsx b/src/pages/Chat/TeacherChatPanel.tsx
index 2b4b5678..ff88a4f3 100644
--- a/src/pages/Chat/TeacherChatPanel.tsx
+++ b/src/pages/Chat/TeacherChatPanel.tsx
@@ -386,6 +386,7 @@ export function TeacherChatPanel({
{request.intent !== 'suggestions' && request.status === 'completed' && Boolean(request.suggestedQuestions?.length) &&