Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 13 additions & 1 deletion Documentation/Providers.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# AI grading providers — 1.3
# AI grading providers — 1.4

Defaults verified on 2026-09-13:

Expand All @@ -10,6 +10,18 @@ Defaults verified on 2026-09-13:

Each role has its own provider selector. A submission freezes the selected configuration, sends identical original evidence to three independent requests, validates every response and aggregates the median locally. An error identifies the judge and provider. There is no fallback to another provider, partial aggregate, or mock result. Test fixtures are compiled only into WriteBenchTests. Legacy demo reviews remain stored but are hidden from History and excluded from analytics.

## Background lifecycle and structured streaming

`WritingStore` is owned by the application, independently of the current page and immersive editor. Hand-in snapshots the question, essay, duration, images, configuration and optional source-review ID. One grading job may run at a time; another draft can be edited while it runs. Completion, cancellation or failure never restores over that newer draft. Closing the window keeps the app running; explicit quit cancels and awaits the job, including its Codex subprocess.

`GradingCoordinator` uses a throwing task group and reports actual reviewer completions. The progress bar is completed judges / 3, not an estimated token or time percentage. A judge failure cancels remaining requests. Results are persisted only after all three structured responses pass validation. The user explicitly opens the finished review; it never steals focus.

`StreamingEssayGradingService` adds provisional preview events without changing the provider-independent result contract. DeepSeek uses URLSession async bytes and official SSE (`stream: true`). The bounded byte framer supports UTF-8, CRLF and SSE data lines; finalization requires `[DONE]` and `finish_reason: stop`. A small JSON string tokenizer previews only the root `summary` field, including incomplete strings and escaped Unicode. Full JSON decoding, required-field validation and exact correction-span checks still gate every score. Reasoning content is neither decoded nor displayed. This follows the [official DeepSeek streaming format](https://api-docs.deepseek.com/api/create-chat-completion/).

Codex continues to decode its final schema-constrained output file; no token-by-token Codex preview is claimed. Prompt version 1.2 requires `strengths`, `weaknesses` and `improvements` arrays alongside the existing fields. Old saved reviews decode these missing arrays as empty. The overview uses the median-score reviewer's conclusion and locally deduplicates feedback; it makes no extra summarization call.

History groups records at display time by task, normalized exact prompt and question-image digest. It does not mutate old records or infer their ancestry. The optional SwiftData `parentSessionID` records only an explicit rewrite source, captured when submitted. Original essays and previous scores remain immutable. Different diagrams with identical instruction text stay separate. Optional folder title, question year and custom label live in a separate `EssayFolderMetadata` model keyed by the stable group digest, so future attempts inherit the same organization without changing essay evidence.

## Direct key entry

A user pastes a DeepSeek key and clicks **使用此 Key**. It works immediately from process memory, without accessing an old Keychain item. **在这台 Mac 上记住 Key** is optional and defaults off. No key is stored in UserDefaults, SwiftData, source, logs or a release package. Submission reads memory only. Explicit persistence and opt-in startup restoration run off the UI thread using a new data-protection Keychain item, with authentication UI disallowed. Legacy development items are never queried. An inaccessible item leaves the user able to enter the API key again. A failed optional save leaves the in-memory key usable and reports that it could not be remembered.
Expand Down
11 changes: 11 additions & 0 deletions Documentation/Validation.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
# 1.4.0 validation · 2026-09-13

- 49 Swift tests pass on Xcode 26.6 / macOS 26.6.2. New coverage includes completion-order progress, concurrent independent inputs, immutable submission and rewrite-parent snapshots, editing a new draft during grading, duplicate-submit rejection, cancellation, judge-specific failure, Codex preflight failure, and no partial score persistence.
- SSE fixtures cover UTF-8 split across byte boundaries, CRLF, comments, multiline data, escaped/incomplete JSON summaries, first readable previews, required final `[DONE]`, malformed/truncated completion rejection, and backward-compatible saved feedback. Production streaming uses URLSession async bytes; no paid live DeepSeek streaming call was made for this release.
- History tests cover chronological ordering, old same-question grouping without invented ancestry, branching from an earlier draft, search retaining all versions, exam/prompt/image isolation, demo exclusion, revision parent persistence across a disk-store reopen, optional year validation, folder metadata persistence/filtering and inheritance by future versions. Review export includes scores, comments and corrections while leaving both full essay bodies out; old saved reports still copy correctly.
- A separate fixture executable built the original v1.3.1 SwiftData schema and wrote two synthetic reviews (4.5 and 7.5). An isolated copy of the new app successfully opened and migrated that store, displayed one question folder and reopened both complete legacy reviews. No user database was used for this upgrade check.
- Native interactive checks in isolated app copies: hand-in returns to preparation with a 0/3 status bar; Settings remains accessible during grading; word count defaults off and becomes visible in immersion when enabled; a completed review is opened explicitly. The history folder and its two child rows were visually inspected. Clicking the new review Copy showed Copied; the existing improved-essay Copy remains separate. The question-info menu saved a custom title, 2024 year and label in the isolated store, exposed year/label filters, and kept exam/task badges visible. The left review outline was checked with actual section jumps.
- Real-time partial-preview timing and cancellation are covered by gated service tests. Codex still displays its final structured feedback on completion, rather than token streaming. An end-to-end live mixed-provider paid review is not claimed.
- The hosted test app uses an in-memory container and does not restore credentials. Credential preference tests use a temporary defaults suite. QA copies have separate identities and synthetic data; the user's running app and current writing session were left untouched.
- macOS version 1.4.0 (7), Universal arm64 + x86_64. SwiftData adds optional `EssaySession.parentSessionID` and the separate `EssayFolderMetadata` model; grouping is computed without rewriting old records. Windows and Android remain 0.1.0.

# 1.3.1 validation · 2026-09-13

- 33 Swift tests pass. New coverage includes Chinese/English UTF-8 (with/without BOM), UTF-16 LE/BE, paragraph normalization, rejected empty/binary/oversized input, a real text-file read, per-task SwiftData persistence, answer/image/time preservation, and detachment from a historical rewrite even when the new question text is identical.
Expand Down
14 changes: 7 additions & 7 deletions Documentation/macOS-guide.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# WriteBench 1.3
# WriteBench 1.4

A real native macOS exam-writing workstation, built with Swift 6, SwiftUI, AppKit, SwiftData, Vision and Swift Charts. No web wrapper, external runtime or third-party app dependencies.

Expand All @@ -17,11 +17,11 @@ Double-click **WriteBench.app** in this folder, or open **WriteBench.xcodeproj**
1. Choose 考研英语 (英语一小作文/大作文、英语一/二翻译), CET-6 writing/translation, or IELTS Academic Task 1 / Task 2.
2. Use the supplied **original practice question**, edit/paste your own, or import an image. The pencil beside the question toggles its plain-text editor. 真题库 saves your own labelled question sources; bundled exercises are not presented as past papers.
3. Click **开始答题** (or **⌘Return**) to enter the only answering workspace: native full-screen immersion. Preparation has no essay editor or grading button. The sidebar, exam tabs and decorative cards disappear. The question stays on the left and your answer on the right.
4. The timer starts when you start answering. Kaoyan and CET-6 use a ruled answer area with **no live word count**; IELTS retains a small word count. This is a practice writing surface, not a claim of exact official answer-card dimensions. Native undo/redo and copy/paste remain available through standard shortcuts, without a formatting toolbar. **保存并离开** saves the draft and pauses its timer; continuing requires **开始答题** again. Switching away from the app during an active session does not stop the exam timer. Leaving macOS full screen through the system controls still leaves you in the same minimal answering workspace.
5. Click **交卷** or press **⌘Return** while answering. Three independent graders run concurrently. Complete reviews are saved before opening, and word count is available after submission. A failed or cancelled grade returns to the same immersive answer with the draft intact. There is no non-immersive submission route.
6. In History / Review, **开始重写** or **继续重写** enters the same immersive workspace. Rewrites automatically save back to the source review, including after closing/reopening the app. The review page itself has no alternate editable essay field. Each completed regrading is its own history record.
4. The timer starts when you start answering. Kaoyan and CET-6 use a ruled answer area. All tasks default to **no live word count**. Enable **答题时显示词数** in Settings if wanted; Chinese translations show characters. This is a practice writing surface, not a claim of exact official answer-card dimensions. Native undo/redo and copy/paste remain available through standard shortcuts, without a formatting toolbar. **保存并离开** saves the draft and pauses its timer; continuing requires **开始答题** again. Switching away from the app during an active session does not stop the exam timer. Leaving macOS full screen through the system controls still leaves you in the same minimal answering workspace.
5. Click **交卷** or press **⌘Return** while answering. The app immediately returns to preparation while three independent graders run in the background. The status strip shows actual completed reviewers, elapsed time and submitted word count. Switch pages, edit another draft or minimize the window; use **查看进度** for streamed DeepSeek comments. Codex feedback arrives when its structured result completes. Open the final review yourself when ready. Failure or cancellation never overwrites the current draft or produces a partial total. Quitting the app interrupts unfinished grading. There is no non-immersive submission route.
6. In History / Review, **开始重写** or **继续重写** enters the same immersive workspace. Rewrites automatically save back to the source review, including after closing/reopening the app. The review page itself has no alternate editable essay field. Each completed regrading retains its own review inside the same question folder. New rewrites record their source version, including branches from an older draft. Old same-question records are grouped without inventing parent links. The narrow left outline jumps to conclusions, feedback, examiner comments, corrections, the improved essay and rewrite. The review header **Copy** copies the assessment; the existing improved-essay **Copy** still copies only that essay.

Drafts are kept separately for all five task types, with debounced local saves and periodic timer saves. History reopens complete reviews, including the original question, essay and imported source images. Search and exam filtering are available in History.
Drafts are kept separately for all eight task types, with debounced local saves and periodic timer saves. Expand a question folder in History to reopen complete reviews, including the original question, essay and imported source images. Use the folder’s **… → 编辑题目信息** menu to set an optional name, question year and custom label. Year and label filters appear when those fields are used, alongside search and exam filtering. Exam and task badges always remain visible, even after renaming a folder.

## Handwritten essays and OCR

Expand All @@ -41,7 +41,7 @@ For **ChatGPT · via Codex**, install the official CLI and run `codex login` onc

The app has no automatic provider fallback. If any judge fails, its name and provider are shown and no total score is saved. Missing DeepSeek keys preserve the draft and offer **前往设置** or **继续作答**. Codex-only configurations do not require a DeepSeek key.

See [provider architecture and validation](Documentation/Providers.md). Real Codex/MAX structured grading has been validated using a synthetic essay; DeepSeek model-list connection has been validated. This native direct-distribution build is not App Sandboxed because it launches the independently installed CLI. No system security setting is changed. Existing local essays are preserved.
See [provider architecture and validation](Providers.md). Real Codex/MAX structured grading has been validated using a synthetic essay; DeepSeek model-list connection has been validated. This native direct-distribution build is not App Sandboxed because it launches the independently installed CLI. No system security setting is changed. Existing local essays are preserved.

## Scoring and statistics

Expand Down Expand Up @@ -75,7 +75,7 @@ WriteBench/
DeepSeek/ URLSession, in-memory credentials, optional Keychain
Codex/ Official CLI discovery, subprocess, JSON Schema
Grading/ Provider protocol, orchestration, validation, median
Rubrics/ Five bundled, versioned Markdown resources
Rubrics/ Eight bundled, versioned Markdown resources
WriteBenchTests/ Domain, concurrency, transport, persistence and real OCR tests
```

Expand Down
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ WriteBench 把练习收敛为一条清晰的路径:选题、作答、评阅、

| 专注写作 | 认真评阅 | 留下进步 |
| :--- | :--- | :--- |
| 原生全屏作答,自动保存草稿 | 三位评审独立阅读同一份原文 | 完整历史与重写记录 |
| 考研、六级使用横线答题区,不显示实时字数 | 原题要求、语言、组织与语域分别反馈 | 错误归类、趋势与练习统计 |
| 原生全屏作答,自动保存草稿 | 三位评审独立阅读同一份原文 | 同题目录与逐稿修改路径 |
| 考研、六级使用横线答题区,默认关闭实时词数 | 原题要求、语言、组织与语域分别反馈 | 错误归类、趋势与练习统计 |
| 手写稿识别后先校对,再提交 | 本机取中位数,显示评审分歧 | 所有原稿保留在自己的设备上 |

### 支持的考试
Expand All @@ -35,7 +35,7 @@ WriteBench 把练习收敛为一条清晰的路径:选题、作答、评阅、
| **CET-6 六级** | 写作 · 汉译英 | 各 15 分练习尺度 |
| **IELTS 雅思 Academic** | Task 1 · Task 2 | 单项任务 Band 9 |

翻译练习重点检查译义、完整性、逻辑关系和目标语言表达;英语一与英语二使用独立 rubric。考研、六级作答期间不显示计词器。
翻译练习重点检查译义、完整性、逻辑关系和目标语言表达;英语一与英语二使用独立 rubric。macOS 各题型默认不显示实时词数;可在 Settings 开启,交卷后始终显示提交词数。

练习分数用于反馈与自查;内置 rubric 是版本化的实践摘要,不是官方阅卷系统。CET-6 不虚构总分换算,IELTS 不把单篇任务分数当作完整 Writing 成绩。

Expand All @@ -57,7 +57,9 @@ macOS 当前是本地 ad-hoc 签名版本,尚未经过 Apple Developer ID 公
2. 使用 Codex 评审时,先安装[官方 Codex CLI](https://learn.chatgpt.com/docs/codex-cli),在终端运行 `codex login`。已登录的用户直接点击 **Check Connection**,无需再走浏览器。
3. 选择考试与题型。macOS 点击题目卡片的 **导入文字**,粘贴完整题目,或选择 `.txt` / `.md` 文件(UTF-8 / UTF-16),编辑确认后自动保存;也可以导入题目图片。
4. 点击 **开始答题**,在沉浸式界面完成作文,然后 **交卷**。
5. 阅读三位评审的分数与修改建议,点击 **开始重写** 完成下一稿。
5. 交卷后评阅在后台继续,进度条显示实际完成的评审人数。可切换页面或最小化窗口;点击 **查看进度** 阅读 DeepSeek 实时评语,完成后自行打开结果。退出应用会中断未完成评阅。
6. 阅读结论、给分、优点、不足和下一稿建议。评阅左侧小目录可快速跳到结论、逐句修改和重写。顶部 **Copy** 复制评审结果,改进作文旁的 **Copy** 单独复制作文。
7. 在 History 展开题目目录,查看每一稿的时间与分数;点击 **开始重写** 继续修改,新稿会记录基于哪一稿。同题旧记录自动归组,全部原文与评分保留。目录右侧 **… → 编辑题目信息** 可填写自定义名称、题目年份和标签,并按年份 / 标签筛选;考试及题型始终突出标注。

**没有 Key 就提示配置,不会给出假评分。** 演示评分代码只存在于测试目标;任何评审失败都会说明是哪一位,保留草稿,不自动改用另一个服务。

Expand Down Expand Up @@ -134,7 +136,7 @@ platforms/android/ Android Studio 项目与手机界面
scripts/ 构建、图标生成与显式联调脚本
```

macOS 自动化测试覆盖 29 个案例,Android 有 5 个领域测试与 2 个实际设备服务测试;Windows 通过评分、持久化、字段校验及实际 OCR 自检。GPT-6 Astra/MAX 已通过实际 Swift 子进程完成样例评卷;DeepSeek 已验证官方模型接口连接。完整混合三评需用户填入有效 Key 后使用。
macOS 自动化测试覆盖 49 个案例,Android 有 5 个领域测试与 2 个实际设备服务测试;Windows 通过评分、持久化、字段校验及实际 OCR 自检。GPT-6 Astra/MAX 已通过实际 Swift 子进程完成样例评卷;DeepSeek 已验证官方模型接口连接。后台、流式输出及版本路径已用隔离测试验证;本次未执行完整付费三评,需用户填入有效 Key 后使用。

<details>
<summary><strong>更多文档</strong></summary>
Expand Down
Loading
Loading