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
56 changes: 56 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,16 @@ jobs:
with:
python-version: '3.12'

- name: Session tests
run: mcpp test

- name: Startup deadline and failure integration tests
shell: bash
run: |
D2X="$PWD/$(find target -type f \( -name d2x -o -name d2x.exe \) | head -1)"
if command -v cygpath >/dev/null; then D2X="$(cygpath -m "$D2X")"; fi
D2X="$D2X" python tests/startup_test.py

- name: Book port integration tests
shell: bash
run: |
Expand All @@ -69,6 +79,14 @@ jobs:
D2X="$PWD/$(find target -name d2x -type f | head -1)"
D2X="$D2X" bash tests/e2e.sh

- name: Stage candidate with provenance
shell: bash
run: python tests/stage_candidate.py
- uses: actions/upload-artifact@v4
with:
name: candidate-linux-x86_64
path: candidate/

build-macos:
name: build (${{ matrix.os }}, mcpp)
runs-on: ${{ matrix.os }}
Expand Down Expand Up @@ -104,13 +122,32 @@ jobs:
with:
python-version: '3.12'

- name: Session tests
run: mcpp test

- name: Startup deadline and failure integration tests
shell: bash
run: |
D2X="$PWD/$(find target -type f \( -name d2x -o -name d2x.exe \) | head -1)"
if command -v cygpath >/dev/null; then D2X="$(cygpath -m "$D2X")"; fi
D2X="$D2X" python tests/startup_test.py

- name: Book port integration tests
shell: bash
run: |
D2X="$PWD/$(find target -type f \( -name d2x -o -name d2x.exe \) | head -1)"
if command -v cygpath >/dev/null; then D2X="$(cygpath -m "$D2X")"; fi
D2X="$D2X" python tests/book_test.py


- name: Stage candidate with provenance
shell: bash
run: python tests/stage_candidate.py
- uses: actions/upload-artifact@v4
with:
name: candidate-${{ matrix.os }}
path: candidate/

# Windows 10 / 11 两条产品线。
#
# GitHub 不提供 Windows 客户端版的 x64 托管 runner,只有 Server 镜像;这里
Expand Down Expand Up @@ -163,6 +200,16 @@ jobs:
with:
python-version: '3.12'

- name: Session tests
run: mcpp test

- name: Startup deadline and failure integration tests
shell: bash
run: |
D2X="$PWD/$(find target -type f \( -name d2x -o -name d2x.exe \) | head -1)"
if command -v cygpath >/dev/null; then D2X="$(cygpath -m "$D2X")"; fi
D2X="$D2X" python tests/startup_test.py

- name: Book port integration tests
shell: bash
run: |
Expand Down Expand Up @@ -247,6 +294,15 @@ jobs:
exit 1
fi


- name: Stage candidate with provenance
shell: bash
run: python tests/stage_candidate.py
- uses: actions/upload-artifact@v4
with:
name: candidate-${{ matrix.os }}
path: candidate/

# Smoke test: a real `d2x checker` run against the d2mcpp course must reach
# and report the FIRST exercise's build error within a bounded window, rather
# than hanging on the init log (issue #24). The checker waits for file edits
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,9 @@ jobs:
PYCODE

- name: Create Release
uses: softprops/action-gh-release@v1
uses: softprops/action-gh-release@v2
with:
target_commitish: ${{ github.sha }}
tag_name: v${{ inputs.version }}
name: ${{ inputs.version }}
draft: false
Expand Down
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,31 @@ See the [architecture reference](.agents/docs/2026-07-20-d2x-architecture-refere
## Links

[Forum](https://forum.d2learn.org) · [Course index](https://github.com/d2learn/xim-pkgindex-d2x) · [Project template](https://d2learn.github.io/d2x-project-template) · [xlings](https://github.com/openxlings/xlings)

## Provider startup and diagnostics

The first `d2x checker` can build the course Provider and download its toolchain.
`provider_startup_timeout` bounds the entire initial `describe` command (default
900 seconds, positive integers only). `D2X_PROVIDER_STARTUP_TIMEOUT` overrides
project config, global config and the default, in that order. Output does not
extend this deadline. After startup, `provider_idle_timeout` still limits silence
in `exercises` / `check` (default 120 seconds; <= 0 disables that idle limit).

Preparation is visible in both built-in UIs. With `--emit-events`, a
`preparing` event precedes `session` and repeats every ten seconds:

```json
{"event":"preparing","phase":"describe","elapsed":0,"timeout":900}
```

Consumers must tolerate preparation and diagnostic `output` events before
`session`. Initialization and transport failures emit an `error` with `code`,
`phase`, and `message`, and exit nonzero. Expected exercise failures remain
`verdict` events and wait for edits. Provider stdout/stderr are saved separately
in `.d2x/provider.stdout.log` and `.d2x/provider.stderr.log` for the current
checker invocation; stderr is also displayed live. Logs may contain Provider
output and should be checked before sharing. Timeout/interrupt terminates the
child process tree. Ctrl+C during a Provider call returns 130.

See [the startup/transport contract](docs/provider-startup.md) for error codes,
compatibility and failure-injection commands.
19 changes: 19 additions & 0 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,22 @@ cd d2mcpp && d2x checker # 开始学习——编辑、保存,自动重测并
## 链接

[论坛](https://forum.d2learn.org) · [课程索引](https://github.com/d2learn/xim-pkgindex-d2x) · [项目模板](https://d2learn.github.io/d2x-project-template) · [xlings](https://github.com/openxlings/xlings)

## Provider 首次启动与诊断

首次 `d2x checker` 可能需要构建课程 Provider 和准备工具链。
`provider_startup_timeout` 控制初次 `describe` 的总期限,默认 900 秒,必须为正整数。
优先级为 `D2X_PROVIDER_STARTUP_TIMEOUT` 环境变量、本地配置、全局配置、默认值。
输出不会延长期限。后续 `exercises` / `check` 仍使用 `provider_idle_timeout`
限制静默,默认 120 秒;该 idle 值 <= 0 时关闭静默限制。

print/TUI 会显示准备信息。`--emit-events` 在 session 前每十秒发送一次
`preparing`,stderr 以 output 事件展示;外部前端应允许这些事件先于 session。
初始化或传输失败产生带 `code`、`phase`、`message` 的 error 事件并非零退出;
练习本身未通过仍产生 verdict 并等待修改。

本轮 checker 的 Provider 原始输出分别保存在 `.d2x/provider.stdout.log` 和
`.d2x/provider.stderr.log`,stderr 同时实时呈现。分享前请检查日志内容。
超时或调用期间取消会终止子进程树;调用期间 Ctrl+C 返回 130。

详见 [启动与传输契约](docs/provider-startup.md)。
51 changes: 51 additions & 0 deletions docs/provider-startup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Provider startup and transport contract

Since 2026.10.20.2, initial `describe` uses an absolute startup deadline (900s).
`exercises` and `check` use the existing idle limit (120s). The three Provider
verbs and protocol version 1 are unchanged. A valid describe must include
`protocol: 1` and a nonempty name. Old project configs need no changes.

Project `.d2x.json` example:

```json
{"buildtools":"my-provider","provider_startup_timeout":900,"provider_idle_timeout":120}
```

The startup field is a positive integer in seconds. Environment
`D2X_PROVIDER_STARTUP_TIMEOUT` overrides project, global and default values;
invalid effective settings fail before launching. `d2x config` preserves both
existing timeout fields when editing other settings.

Frontend additions (unknown events should be ignored by older clients):

```json
{"event":"preparing","phase":"describe","elapsed":0,"timeout":900}
{"event":"error","code":"startup_timeout","phase":"describe","message":"..."}
```

Preparation begins before session discovery, repeats every 10s, and does not
claim the child is making progress. It never refreshes the absolute deadline.
Only stdout is parsed as Provider NDJSON. Stderr cannot spoof protocol events.
Both streams drain independently, including large output without newlines.
Protocol lines above 1 MiB are discarded from parsing but retained in raw logs.
Unknown stdout lines are retained in logs; diagnostics never contaminate the
frontend stdout stream. Failure reports include bounded 8 KiB tails per stream,
command, cwd, elapsed time and child exit code when the command fails.

Error codes: `configuration`, `instance_lock`, `startup_timeout`, `idle_timeout`,
`provider_exit`, `provider_protocol`, `empty_course`, `cancelled`. Phases are
`configuration`, `describe`, `exercises`, `check`. Infrastructure errors return 1;
Ctrl+C during an active Provider call returns 130. A Provider exiting nonzero or
reporting error after partial success never advances learning progress.
Empty enumeration is a distinct fatal empty-course result. Normal completion
returns 0; an exercise verdict fail/blocked remains an interactive learning state.

Raw logs are overwritten when a new checker invocation begins; all three verbs
append to that invocation's logs. Timeouts/cancellation kill the process group on
POSIX and the Job Object on Windows, then reap the direct child. Startup fails
closed on Windows if process-tree containment cannot be established.

Run portable failure injection with `D2X=/absolute/path/to/d2x python tests/startup_test.py`.
`tests/e2e.sh` additionally covers save/advance, locking and print output flushing.
CI stages candidate binaries with `provenance.json` containing repository, checkout
SHA, binary version and SHA256. These are test artifacts, not stable releases.
2 changes: 1 addition & 1 deletion mcpp.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "d2x"
version = "2026.10.20.1"
version = "2026.10.20.2"
description = "AI-powered development assistant for C++ projects"
license = "Apache-2.0"
repo = "https://github.com/d2learn/d2x"
Expand Down
2 changes: 1 addition & 1 deletion protocol/mcpp.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "d2x-protocol"
version = "2026.08.02.2"
version = "2026.10.20.2"
description = "d2x Provider/Frontend 协议层:类型、编解码、传输(参考实现;规范性载体是协议文档,跨语言合规判据是 conformance 套件)"
license = "Apache-2.0"

Expand Down
Loading
Loading