feat: software-engineering skill v0.3 (P1-1..P1-4 + P2)
- P1-1 directory-isolation executable check: git diff --name-only against merge-base, output recorded in merge record - P1-2 derivation checklist table (8 required items, missing one blocks dispatch) - P1-3 QA split into single-client review and cross-client E2E with fixed deliverables and agent-test attribution - P1-4 sub-task slim-pack trimming rules (listed chapters only, never full copies) - P2: inter-thread Q&A default policy, docs/decisions.md append-only, per-client onboarding docs (web first at G4a), v1.1 handover checklist - Body and appendix kept in sync; new lightweight keyword test
This commit is contained in:
1 parent
ef0052d15a
commit
34ed438d0e
2 files changed
+163
-27
No files matched your search
@@ -5,8 +5,13 @@ description: 用户要从零做一款软件、尤其是要同时做 web、macOS
|
||||
|
||||
# software-engineering:多客户端软件工程编排
|
||||
|
||||
版本 v0.2,2026-10-30。v0.1 纸面推演找出的 5 个 P0 缺口已在本版修掉,
|
||||
见同目录 `CHANGELOG.md`。仍是草稿:未提 PR、未部署生产、未改任何生产代码。
|
||||
版本 v0.3,2026-10-30。上一版是 v0.2 + PR #193 GitHub 保证
|
||||
(已合并、已部署生产)。v0.3 改了什么:P1-1 目录隔离自动化检查
|
||||
(可执行命令 + 检查输出记入合并记录)、P1-2 派生占位符必填表
|
||||
(缺一项就不发任务)、P1-3 QA 正式分单端复核与跨端 E2E 两部分
|
||||
(各自交付物写死)、P1-4 子任务精简包裁剪规则(只给本端相关章节,
|
||||
提示词里列出所含章节);P2:任务间问答默认策略、决策记录落盘
|
||||
`docs/decisions.md`、每端上手文档、v1.1 交接清单。
|
||||
装机路径见同目录 `README.md`:源码位置是 `cloud/skills/software-engineering/SKILL.md`,
|
||||
播种后租户内位置是 `<codex-home>/skills/software-engineering/SKILL.md`。
|
||||
|
||||
@@ -155,12 +160,29 @@ G0 的六问缺一问,G0 就不算过。用户没回复时,协调员等,
|
||||
不把“本地能跑”写成“已发布”。
|
||||
7. 结尾必须带 TODO-UPDATE 小节(见“待办机制”),缺这节视为交付不完整。
|
||||
|
||||
派生前校验清单(协调员自查,漏填就停):客户端名填了吗?
|
||||
目录填了吗?分支填了吗?契约版本填了吗?PRD 版本填了吗?
|
||||
完成定义填了吗?有一项没填就不发任务。
|
||||
派生前校验表(必填表,协调员必须把填好的表原样贴进派生提示词,
|
||||
不能只在心里过一遍;缺一项就不发任务):
|
||||
|
||||
子任务上下文要精简:只给本端相关的契约章节和设计页,
|
||||
不要把 PRD、契约、设计全文复制给 5 个线程各一份。
|
||||
| 必填项 | 填什么 |
|
||||
|--------|--------|
|
||||
| 客户端名 | 本客户端,例如 web / macOS / Windows / iOS / Android |
|
||||
| 拥有目录 | 只能写这一个目录,例如 `apps/web` |
|
||||
| 分支名 | `feat/swe/<阶段>-<角色>-<简称>`,例如 `feat/swe/client-web` |
|
||||
| 契约版本 | 冻结的契约版本号,例如 v1 |
|
||||
| PRD 版本 | 冻结的 PRD 版本号 |
|
||||
| 设计版本 | 冻结的设计版本(设计页清单见精简包) |
|
||||
| 完成定义 | 产出文件清单 + 必须跑过的自测命令 + 必须提交的证据 |
|
||||
| TODO-UPDATE 要求 | 结尾报告必须带 TODO-UPDATE 小节,格式见“待办机制” |
|
||||
|
||||
子任务精简包(裁剪规则写死,不是“尽量精简”):只给本端相关的
|
||||
契约章节、设计页、PRD 故事和冻结决策摘要,绝不把 PRD、契约、
|
||||
设计全文复制给每个线程。精简包由四部分组成,缺一部分就补齐再发:
|
||||
① 本端相关的契约章节(列出章节号 / 标题);② 本端相关的设计页
|
||||
(列出页名);③ 本端相关的 PRD 故事(列出故事编号);④ 冻结决策摘要
|
||||
(不超过 10 条,列出决策标题)。派生提示词里必须列出精简包所含章节
|
||||
清单,例如“契约 §任务状态机、§错误码;设计 p2 审批页;PRD 故事 S1、S3;
|
||||
决策 D1、D4”——只写“已精简”不算数。线程发现缺章节时向协调员要补发,
|
||||
不自己猜、不去全文里翻旧版。
|
||||
|
||||
## 待办机制(不依赖提示词注入)
|
||||
|
||||
@@ -200,6 +222,18 @@ TODO-UPDATE
|
||||
受影响的客户端各收到一条变更通知和一个回归任务。
|
||||
- 协调员不批准:提议线程按旧契约做,差异记入已知问题,不悄悄分叉。
|
||||
|
||||
## 任务间问答默认策略与决策记录(v0.3 新增)
|
||||
|
||||
- 任务间问答默认策略:被问题卡住的线程先按冻结契约做,把假设写成
|
||||
开放问题记入结尾报告,不无限等协调员答复、不停工等、不按猜测改契约。
|
||||
协调员在下一轮答复:确认假设、纠正假设(发回归任务)、或升级为契约变更;
|
||||
答复本身也要记入决策记录。等答复期间线程不阻塞在原地,先做契约内
|
||||
无争议的部分。
|
||||
- 决策记录落盘路径:`docs/decisions.md`,由协调员维护,子线程只在报告里
|
||||
提议、不直接改。只追加不改写历史:一条决策写清日期、议题、决定、理由、
|
||||
影响端;推翻旧决策时追加新条写明“取代 D-n”,绝不改旧条原文、不删旧条。
|
||||
决策记录是合并记录和验收清单之外的第三份事实来源,口头决定不算数。
|
||||
|
||||
## 目录与分支规则
|
||||
|
||||
推荐的单仓布局(单客户端项目可删掉不需要的 `apps/*`):
|
||||
@@ -224,9 +258,17 @@ tests/ # QA 线程专属,含跨端一致性用例
|
||||
- 合并顺序:共享核心先合,客户端按“自测通过 -> 协调员评审 -> CI 通过
|
||||
-> 人 / 宿主执行合并”依次合,冲突由协调员判定,
|
||||
不由子线程自行解决后静默推主线。
|
||||
- 目录隔离要有自动化检查,不能只靠人工评审:合并前由人 / 宿主跑一次
|
||||
检查,确认本分支只改了拥有目录(契约变更经批准的除外)。
|
||||
在 Docker 任务共享工作目录的环境里,这一步尤其不能省。
|
||||
- 目录隔离必须有自动化检查,不能只靠人工评审、不能只写“人工评审”。
|
||||
合并前由人 / 宿主跑(以拥有目录 `apps/web` 为例,基线以合并目标分支
|
||||
的最新 HEAD 为准):
|
||||
```
|
||||
git fetch origin
|
||||
git diff --name-only $(git merge-base origin/main HEAD)...HEAD
|
||||
```
|
||||
逐项核对输出的每个文件是否落在拥有目录内;契约变更经协调员批准的
|
||||
文件除外,但批准记录必须能对应到具体文件。检查命令、输出文件清单、
|
||||
例外批准记录必须原样记入合并记录,缺检查输出的合并记录视为不合格,
|
||||
不给合并决定。在 Docker 任务共享工作目录的环境里,这一步尤其不能省。
|
||||
|
||||
## GitHub 保证(每线程一分支、一 PR、一合并门)
|
||||
|
||||
@@ -255,7 +297,8 @@ tests/ # QA 线程专属,含跨端一致性用例
|
||||
### 合并门:缺一不可,与主分支保护
|
||||
|
||||
- 合并前四项全过:① GitHub Checks 全绿;② 目录归属检查通过
|
||||
(只改拥有目录,契约变更经批准除外);③ 契约版本是冻结版;
|
||||
(只改拥有目录,契约变更经批准除外;检查命令与输出文件清单必须
|
||||
记入合并记录,见“目录与分支规则”);③ 契约版本是冻结版;
|
||||
④ 协调员评审通过,且无未解决评审意见。有一项不过就不合并。
|
||||
- CI 怎么查:用只读方式查,例如本平台的 GET /tasks/{id}/pr/checks。
|
||||
它是只读诊断,绝不重跑检查、不批准、不合并;还没建 PR 时它会 409
|
||||
@@ -295,13 +338,15 @@ tests/ # QA 线程专属,含跨端一致性用例
|
||||
|
||||
- 每个客户端线程交付前必须自测:单元测试 + 对接契约的集成测试
|
||||
+ 一份手工冒烟清单(启动、登录、核心 agent 任务、异常提示)。
|
||||
- QA 线程分两部分:单端复核(复核各客户端自测证据)和跨端 E2E
|
||||
(QA 主做,出一份跨端一致性矩阵:同一个 agent 任务、同一份数据,
|
||||
在各端的结果、错误提示、同步状态是否一致)。
|
||||
- agent 专属测试必须列入 QA 模板,不能只测 happy path:
|
||||
沙箱越界、危险动作审批、长任务超时和取消、断网续跑、模型 503 重试、
|
||||
token / 配额超限。安全检查点在 G5 单独签字,检查权限边界和密钥处理,
|
||||
不算 QA 的附带项。
|
||||
- QA 线程正式分两部分,各自交付物写死,缺一不算 G5 通过:
|
||||
① 单端复核:逐端复核各客户端的自测证据是否属实(命令、结果、冒烟清单),
|
||||
交付物是逐端复核报告(每端通过 / 不通过 + 证据 + 未复核项)。
|
||||
② 跨端 E2E:同一 agent 任务、同一份数据跑遍各端,交付物是跨端一致性矩阵
|
||||
(各端结果、错误提示、同步状态是否一致)+ 缺陷清单 + 回归清单。
|
||||
- agent 专属测试清单保留,归属如下,不能只测 happy path:
|
||||
归单端复核(每端都要过):沙箱越界、危险动作审批、token / 配额超限;
|
||||
归跨端 E2E(同一任务跨端行为一致):长任务超时和取消、断网续跑、模型 503 重试。
|
||||
安全检查点在 G5 单独签字,检查权限边界和密钥处理,不算 QA 的附带项。
|
||||
- 未编译端的 QA 口径要写死:iOS / Android / Mac 在 Linux 主机上编不出来时,
|
||||
QA 结论写“代码评审 + Web 核心验证”,不写“构建通过”。
|
||||
- 验收清单模板在 `templates/artifacts/acceptance-checklist.md`。
|
||||
@@ -309,6 +354,11 @@ tests/ # QA 线程专属,含跨端一致性用例
|
||||
不能代替用户勾选通过。验收和发布按端分别做:
|
||||
web 演示可真跑,未编译端审代码和构建步骤,两者效力不同,分开标注;
|
||||
web 部署、桌面安装包分发、应用商店审核是三条流水线,分开记录回滚方式。
|
||||
- 每端上手文档是 G6 交付物之一:每端一份,写清启动、登录、核心流程、
|
||||
常见问题;G4 阶段先写 web 版(G4a 交付时随 web 客户端一起交),
|
||||
桌面、移动版在各自 G4b / G4c 交付时补齐,不拖到 G6 才现写。
|
||||
- v1.1 交接清单是 G6 收尾必备,三项缺一不算收尾完成:未修缺陷
|
||||
(带归属和严重程度)、开放问题(含任务间问答里未关闭的假设)、下轮待办。
|
||||
|
||||
## 失败、续跑与成本
|
||||
|
||||
@@ -342,12 +392,17 @@ tests/ # QA 线程专属,含跨端一致性用例
|
||||
- 待办文件是不是协调员唯一维护的最新状态?有没有子线程直接改了待办?
|
||||
- 合并记录里执行人写了吗?是协调员评审 + 人 / 宿主执行,还是冒充成协调员自己合的?
|
||||
- GitHub 门全过了吗?每线程交付有 PR 号吗?Checks 全绿吗?目录归属过了吗?
|
||||
目录检查命令和输出记入合并记录了吗?
|
||||
主分支保护开了吗,没开有没有记为风险?合并是 merge commit 吗?
|
||||
合并记录里 PR 号、合并提交 hash、CI 运行链接齐了吗?合并后删远程分支了吗?
|
||||
发布打 tag 了吗,回滚方案是 revert 或重部署上一 tag、而不是 force-push 吗?
|
||||
密钥进代码、commit、命令行了吗?.gitignore 在首 commit 前检查了吗?
|
||||
- 验收清单是不是用户亲手确认的?有没有一项是协调员替用户勾的?
|
||||
- 分阶段走了吗?G3.5 没过就开了 G4b / G4c 吗?
|
||||
- v0.3 新增项:派生校验表 8 项全填且贴进了提示词吗?精简包所含章节
|
||||
在提示词里列出了吗?QA 单端复核报告和跨端一致性矩阵都齐了吗?
|
||||
agent 测试清单归属对吗?决策记录是只追加的 `docs/decisions.md` 吗?
|
||||
web 版上手文档在 G4a 交了吗?v1.1 交接清单三项齐了吗?
|
||||
有一项为否,就停在当前门禁,不进入下一阶段。
|
||||
|
||||
## 附录:角色模板(播种随附版)
|
||||
@@ -355,6 +410,9 @@ tests/ # QA 线程专属,含跨端一致性用例
|
||||
下面是 `templates/roles/` 的随附全文。租户任务内只能看到本文件时,
|
||||
以本附录为准。主聊协调员派生子任务时,贴本附录对应章节全文,
|
||||
不要只贴文件名。发布前核对附录与 `templates/roles/` 一致。
|
||||
本附录是 v0.3 版的一部分:正文的 v0.3 改动(目录隔离自动化检查、
|
||||
派生必填表、精简包、QA 拆分、决策记录、上手文档、v1.1 交接清单)
|
||||
已同步进对应角色章节,正文与附录不一致就是不合格。
|
||||
(附录正文由生成脚本从 `templates/roles/` 原样拼接,见 CHANGELOG。)
|
||||
|
||||
---
|
||||
@@ -379,8 +437,13 @@ tests/ # QA 线程专属,含跨端一致性用例
|
||||
3. 每个子任务提示词必须包含:角色模板全文(来自 SKILL.md 附录)、
|
||||
本阶段输入的版本号、拥有目录、只读目录、分支名、完成定义、
|
||||
禁止事项、TODO-UPDATE 要求。缺一项不派生。
|
||||
派生前校验:客户端名、目录、分支、契约版本、PRD 版本、完成定义全填了吗?
|
||||
子任务上下文要精简,只给本端相关的契约章节和设计页。
|
||||
派生前校验表(必填表,填好的表原样贴进提示词,缺一项就不发任务):
|
||||
客户端名、拥有目录、分支名、契约版本、PRD 版本、设计版本、
|
||||
完成定义、TODO-UPDATE 要求,共 8 项。
|
||||
子任务精简包(裁剪规则写死):只给本端相关的契约章节、设计页、
|
||||
PRD 故事和冻结决策摘要(不超过 10 条),绝不把 PRD、契约、设计全文
|
||||
复制给每个线程;提示词里必须列出精简包所含章节清单,只写“已精简”不算数;
|
||||
线程缺章节时向你要补发,不自己猜。
|
||||
|
||||
## 派生顺序(分阶段,默认不 5 端齐开)
|
||||
|
||||
@@ -397,13 +460,21 @@ tests/ # QA 线程专属,含跨端一致性用例
|
||||
G4c:移动端 iOS / Android 各 1 个线程。用户明确签字接受并行成本和风险时,
|
||||
才可以在记录里写明例外原因后并行开 G4b / G4c。
|
||||
- G5:QA 分单端复核和跨端 E2E,安全检查点单独签字。
|
||||
单端复核交付物是逐端复核报告(每端通过 / 不通过 + 证据 + 未复核项),
|
||||
归单端复核的 agent 测试:沙箱越界、危险动作审批、token / 配额超限;
|
||||
跨端 E2E 交付物是跨端一致性矩阵 + 缺陷清单 + 回归清单,
|
||||
归跨端 E2E 的 agent 测试:长任务超时和取消、断网续跑、模型 503 重试。
|
||||
两部分交付物缺一不算 G5 通过。
|
||||
- G6:你按端分别准备验收清单和演示产物,交用户逐项确认;发布和回滚也按端分开记。
|
||||
|
||||
## 你的合并规则(评审与执行拆分)
|
||||
|
||||
- 子线程交付物 = 分支 + 产出文件清单 + 自测命令和结果 + 已知问题 + TODO-UPDATE。
|
||||
缺证据或缺 TODO-UPDATE 的不给合并决定,先打回补齐。
|
||||
- 合并前检查:只改了拥有目录吗(合并前由人 / 宿主跑目录检查)?
|
||||
- 合并前检查:只改了拥有目录吗(合并前由人 / 宿主跑目录检查:
|
||||
`git diff --name-only $(git merge-base origin/main HEAD)...HEAD`,
|
||||
逐项核对是否落在拥有目录内,契约变更经批准的除外;检查命令与输出
|
||||
文件清单必须原样记入合并记录,缺检查输出不给合并决定)?
|
||||
契约是冻结版本吗?CI 通过吗?有没有把“未验证”写成“已完成”?
|
||||
有一项不过就不给合并决定。
|
||||
- 你的决定写成合并记录:哪个分支、谁评审、CI 结果、执行人是谁
|
||||
@@ -415,6 +486,15 @@ tests/ # QA 线程专属,含跨端一致性用例
|
||||
契约版本号加一,通知所有受影响客户端线程做回归;不批准的:
|
||||
提议线程按旧契约做,差异记入已知问题。
|
||||
|
||||
## 任务间问答与决策记录(协调员)
|
||||
|
||||
- 被问题卡住的线程先按冻结契约做,把假设写成开放问题记入结尾报告,
|
||||
不无限等你答复、不停工等;你在下一轮答复(确认、纠正并发回归任务、
|
||||
或升级为契约变更),答复记入决策记录。
|
||||
- 决策记录落盘 `docs/decisions.md`,由你维护,只追加不改写历史;
|
||||
一条决策写清日期、议题、决定、理由、影响端;推翻旧决策追加新条
|
||||
写明“取代 D-n”,绝不改旧条原文、不删旧条。
|
||||
|
||||
## 失败处理
|
||||
|
||||
- 有进度的中断任务:断点续跑。
|
||||
@@ -591,7 +671,11 @@ tests/ # QA 线程专属,含跨端一致性用例
|
||||
# 角色模板:客户端开发(每客户端 1 个线程)
|
||||
|
||||
协调员派生你时,必须把下面占位符填好再发给你。
|
||||
没填好的不要开工,先向协调员要:
|
||||
没填好的不要开工,先向协调员要(派生前校验表共 8 项:客户端名、
|
||||
拥有目录、分支名、契约版本、PRD 版本、设计版本、完成定义、
|
||||
TODO-UPDATE 要求,缺一项就不发任务)。派生提示词里的精简包必须
|
||||
列出所含章节(契约章节、设计页、PRD 故事、冻结决策摘要),只写
|
||||
“已精简”时先向协调员要清单再开工;缺章节不自己猜:
|
||||
|
||||
- 本客户端:{{CLIENT}},例如 web / macOS / Windows / iOS / Android
|
||||
- 拥有目录:{{APPS_DIR}},例如 `apps/web`
|
||||
@@ -706,11 +790,15 @@ agent 类产品还要参与 G3.5 可行性 spike:任务能创建、能跑出
|
||||
## 产出
|
||||
|
||||
1. 测试用例:按 PRD 验收标准写,每个用例写清前置条件、步骤、期望结果。
|
||||
单端复核先复核各客户端的自测证据,跨端 E2E 再做下面的矩阵,不要一个人包全端。
|
||||
2. 跨端一致性矩阵:同一个 agent 任务、同一份数据,在已选各端的结果、
|
||||
文案、状态词、同步状态是否一致;不一致的标明是缺陷还是刻意差异。
|
||||
3. agent 专属测试(缺一项不算测完):沙箱越界、危险动作审批、
|
||||
长任务超时和取消、断网续跑、模型 503 重试、token / 配额超限。
|
||||
QA 正式分两部分,各自交付物写死,缺一不算 G5 通过:
|
||||
① 单端复核:逐端复核各客户端的自测证据是否属实,交付物是逐端复核报告
|
||||
(每端通过 / 不通过 + 证据 + 未复核项);
|
||||
② 跨端 E2E:同一 agent 任务、同一份数据跑遍各端,交付物是跨端一致性矩阵
|
||||
(各端结果、文案、状态词、同步状态是否一致;不一致的标明是缺陷还是刻意差异)
|
||||
+ 缺陷清单 + 回归清单。
|
||||
2. agent 专属测试(缺一项不算测完),归属如下:
|
||||
归单端复核(每端都要过):沙箱越界、危险动作审批、token / 配额超限;
|
||||
归跨端 E2E(同一任务跨端行为一致):长任务超时和取消、断网续跑、模型 503 重试。
|
||||
4. 安全检查点(G5 单独签字):权限边界是否按契约实现、密钥是否外泄、
|
||||
agent 是否能读工作目录外文件。安全检查不算 QA 的附带项。
|
||||
5. 缺陷清单:每个缺陷写清复现步骤、影响客户端、严重程度、归属线程。
|
||||
@@ -752,6 +840,10 @@ iOS / Android / Mac 在 Linux 主机上编不出来时,你的结论写
|
||||
3. 发布说明草稿,按端分别写:新能力、已知问题、回滚方式
|
||||
(至少写清“出问题先停哪个端”)。web 部署、桌面安装包分发、
|
||||
应用商店审核是三条流水线,分开记录,不要写成一次全上。
|
||||
4. 每端上手文档:每端一份,写清启动、登录、核心流程、常见问题;
|
||||
web 版在 G4a 交付时先交,桌面、移动版在各自 G4b / G4c 交付时补齐。
|
||||
5. v1.1 交接清单:未修缺陷(带归属和严重程度)、开放问题
|
||||
(含任务间问答里未关闭的假设)、下轮待办;三项缺一不算 G6 收尾完成。
|
||||
|
||||
## 用户确认规则
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
"""software-engineering 技能 v0.3:P1/P2 关键词断言(轻量)。
|
||||
|
||||
只断言正文与附录都含有 v0.3 的关键概念,不逐字背诵全文:
|
||||
版本号与上一版、目录隔离可执行检查、派生必填表、精简包所含章节、
|
||||
QA 两部分交付物与 agent 测试归属、决策记录落盘与只追加、
|
||||
上手文档与 v1.1 交接清单。正文改了附录没同步时,附录部分就会失败。
|
||||
"""
|
||||
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
SKILL = Path(__file__).resolve().parents[1] / "skills" / "software-engineering" / "SKILL.md"
|
||||
|
||||
# 正文与附录都要有的关键词
|
||||
BOTH = ("v0.3", "git diff --name-only", "精简包", "所含章节",
|
||||
"单端复核", "跨端一致性矩阵", "沙箱越界", "模型 503",
|
||||
"docs/decisions.md", "只追加", "上手文档", "v1.1 交接清单")
|
||||
|
||||
# 派生必填表 8 项:正文表格与协调员附录都要出现
|
||||
CHECKLIST = ("客户端名", "拥有目录", "分支名", "契约版本",
|
||||
"PRD 版本", "设计版本", "完成定义", "TODO-UPDATE")
|
||||
|
||||
|
||||
class SoftwareEngineeringV03TestCase(unittest.TestCase):
|
||||
def test_v03_in_body_and_appendix(self):
|
||||
text = SKILL.read_text(encoding="utf-8")
|
||||
main, sep, appendix = text.partition("## 附录:角色模板")
|
||||
self.assertTrue(sep, "appendix marker missing")
|
||||
self.assertIn("v0.2", main, "header must name the previous version")
|
||||
self.assertIn("GitHub 保证", main, "header must name the previous GitHub guarantees")
|
||||
for kw in BOTH:
|
||||
self.assertIn(kw, main, kw)
|
||||
self.assertIn(kw, appendix, "appendix: " + kw)
|
||||
for kw in CHECKLIST:
|
||||
self.assertIn(kw, main, kw)
|
||||
self.assertIn(kw, appendix, "appendix: " + kw)
|
||||
|
||||
def test_v03_no_full_copy_rule(self):
|
||||
text = SKILL.read_text(encoding="utf-8")
|
||||
self.assertIn("绝不把 PRD、契约", text)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user