From c03b3b3d973f6541f2f8e3f8214f08389fa8c2b4 Mon Sep 17 00:00:00 2001 From: DandreYang <13072547+Dandre126@users.noreply.github.com> Date: Wed, 19 Aug 2026 04:04:06 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat(=E4=BA=A4=E4=BB=98=E7=89=A9=E7=90=86):?= =?UTF-8?q?=20=E6=8A=8A=E4=BC=9A=E5=AE=A1=E6=96=9C=E6=9D=A0=E5=92=8C?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E5=90=88=E5=85=A5=E9=A2=84=E6=A3=80=E6=94=B6?= =?UTF-8?q?=E8=BF=9B=E7=AC=AC=E4=B8=80=E6=96=B9=20Skill?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在开发线仓增加 dyro-review-board 与 dyro-task-merge,可经 integration 安装,不自动触发,也不等于 merge 或发布。 --- .github/workflows/ci.yml | 4 +- .github/workflows/pypi-publish.yml | 4 +- CHANGELOG.md | 8 ++ .../2026-08-19-slash-review-and-task-merge.md | 92 +++++++++++++++++++ .../sections/01-task-merge.md | 17 ++++ .../sections/02-review-board.md | 16 ++++ pyproject.toml | 4 + .../assets/dyro-review-board/SKILL.md | 51 ++++++++++ .../dyro-review-board/agents/openai.yaml | 4 + .../assets/dyro-task-merge/SKILL.md | 89 ++++++++++++++++++ .../assets/dyro-task-merge/agents/openai.yaml | 4 + src/dyro/integrations/manager.py | 18 ++++ src/dyro/integrations/seats.py | 2 +- tests/test_integrations.py | 2 + tests/test_release_source.py | 4 + tests/test_seats.py | 22 ++++- 16 files changed, 335 insertions(+), 6 deletions(-) create mode 100644 docs/reviews/2026-08-19-slash-review-and-task-merge.md create mode 100644 docs/reviews/2026-08-19-slash-review-and-task-merge/sections/01-task-merge.md create mode 100644 docs/reviews/2026-08-19-slash-review-and-task-merge/sections/02-review-board.md create mode 100644 src/dyro/integrations/assets/dyro-review-board/SKILL.md create mode 100644 src/dyro/integrations/assets/dyro-review-board/agents/openai.yaml create mode 100644 src/dyro/integrations/assets/dyro-task-merge/SKILL.md create mode 100644 src/dyro/integrations/assets/dyro-task-merge/agents/openai.yaml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bffe574..9b08f48 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -84,7 +84,7 @@ jobs: "$smoke_root/wheel-venv/bin/python" -c "import experiments.local_agent_dispatch" "$smoke_root/wheel-venv/bin/python" -I -c "import dyro.continuation" "$smoke_root/wheel-venv/bin/python" -I -c "from dyro.console.assets import validate_assets; validate_assets()" - "$smoke_root/wheel-venv/bin/python" -I -c "from importlib.resources import files; root=files('dyro.integrations').joinpath('assets'); assert root.joinpath('dyro-control-plane','SKILL.md').is_file(); assert root.joinpath('dyro-control-plane','agents','openai.yaml').is_file(); assert root.joinpath('dyro-dispatch','SKILL.md').is_file(); assert root.joinpath('dyro-dispatch','agents','openai.yaml').is_file(); assert root.joinpath('dyro-executor','SKILL.md').is_file(); assert root.joinpath('dyro-executor','agents','openai.yaml').is_file(); assert root.joinpath('dyro-board','SKILL.md').is_file(); assert root.joinpath('dyro-board','agents','openai.yaml').is_file(); assert not root.joinpath('dyro-readonly').is_dir()" + "$smoke_root/wheel-venv/bin/python" -I -c "from importlib.resources import files; root=files('dyro.integrations').joinpath('assets'); assert root.joinpath('dyro-control-plane','SKILL.md').is_file(); assert root.joinpath('dyro-control-plane','agents','openai.yaml').is_file(); assert root.joinpath('dyro-dispatch','SKILL.md').is_file(); assert root.joinpath('dyro-dispatch','agents','openai.yaml').is_file(); assert root.joinpath('dyro-executor','SKILL.md').is_file(); assert root.joinpath('dyro-executor','agents','openai.yaml').is_file(); assert root.joinpath('dyro-board','SKILL.md').is_file(); assert root.joinpath('dyro-board','agents','openai.yaml').is_file(); assert root.joinpath('dyro-review-board','SKILL.md').is_file(); assert root.joinpath('dyro-review-board','agents','openai.yaml').is_file(); assert root.joinpath('dyro-task-merge','SKILL.md').is_file(); assert root.joinpath('dyro-task-merge','agents','openai.yaml').is_file(); assert not root.joinpath('dyro-readonly').is_dir()" test -x "$smoke_root/wheel-venv/bin/dyro" test ! -e "$smoke_root/wheel-venv/bin/dyro-bridge" test ! -e "$smoke_root/wheel-venv/bin/dyro-mcp" @@ -96,7 +96,7 @@ jobs: "$smoke_root/sdist-venv/bin/pip" install "${sdist_artifact}" "$smoke_root/sdist-venv/bin/python" -I -c "import dyro.continuation" "$smoke_root/sdist-venv/bin/python" -I -c "from dyro.console.assets import validate_assets; validate_assets()" - "$smoke_root/sdist-venv/bin/python" -I -c "from importlib.resources import files; root=files('dyro.integrations').joinpath('assets'); assert root.joinpath('dyro-control-plane','SKILL.md').is_file(); assert root.joinpath('dyro-control-plane','agents','openai.yaml').is_file(); assert root.joinpath('dyro-dispatch','SKILL.md').is_file(); assert root.joinpath('dyro-dispatch','agents','openai.yaml').is_file(); assert root.joinpath('dyro-executor','SKILL.md').is_file(); assert root.joinpath('dyro-executor','agents','openai.yaml').is_file(); assert root.joinpath('dyro-board','SKILL.md').is_file(); assert root.joinpath('dyro-board','agents','openai.yaml').is_file(); assert not root.joinpath('dyro-readonly').is_dir()" + "$smoke_root/sdist-venv/bin/python" -I -c "from importlib.resources import files; root=files('dyro.integrations').joinpath('assets'); assert root.joinpath('dyro-control-plane','SKILL.md').is_file(); assert root.joinpath('dyro-control-plane','agents','openai.yaml').is_file(); assert root.joinpath('dyro-dispatch','SKILL.md').is_file(); assert root.joinpath('dyro-dispatch','agents','openai.yaml').is_file(); assert root.joinpath('dyro-executor','SKILL.md').is_file(); assert root.joinpath('dyro-executor','agents','openai.yaml').is_file(); assert root.joinpath('dyro-board','SKILL.md').is_file(); assert root.joinpath('dyro-board','agents','openai.yaml').is_file(); assert root.joinpath('dyro-review-board','SKILL.md').is_file(); assert root.joinpath('dyro-review-board','agents','openai.yaml').is_file(); assert root.joinpath('dyro-task-merge','SKILL.md').is_file(); assert root.joinpath('dyro-task-merge','agents','openai.yaml').is_file(); assert not root.joinpath('dyro-readonly').is_dir()" test -x "$smoke_root/sdist-venv/bin/dyro" test ! -e "$smoke_root/sdist-venv/bin/dyro-bridge" test ! -e "$smoke_root/sdist-venv/bin/dyro-mcp" diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml index 27b81ec..4f862f6 100644 --- a/.github/workflows/pypi-publish.yml +++ b/.github/workflows/pypi-publish.yml @@ -149,7 +149,7 @@ jobs: "$smoke_root/wheel-venv/bin/pip" install "$GITHUB_WORKSPACE"/dist/dyro-*.whl "$smoke_root/wheel-venv/bin/python" -c "import experiments.local_agent_dispatch" "$smoke_root/wheel-venv/bin/python" -I -c "from dyro.console.assets import validate_assets; validate_assets()" - "$smoke_root/wheel-venv/bin/python" -I -c "from importlib.resources import files; root=files('dyro.integrations').joinpath('assets'); assert root.joinpath('dyro-control-plane','SKILL.md').is_file(); assert root.joinpath('dyro-control-plane','agents','openai.yaml').is_file(); assert root.joinpath('dyro-dispatch','SKILL.md').is_file(); assert root.joinpath('dyro-dispatch','agents','openai.yaml').is_file(); assert root.joinpath('dyro-executor','SKILL.md').is_file(); assert root.joinpath('dyro-executor','agents','openai.yaml').is_file(); assert root.joinpath('dyro-board','SKILL.md').is_file(); assert root.joinpath('dyro-board','agents','openai.yaml').is_file()" + "$smoke_root/wheel-venv/bin/python" -I -c "from importlib.resources import files; root=files('dyro.integrations').joinpath('assets'); assert root.joinpath('dyro-control-plane','SKILL.md').is_file(); assert root.joinpath('dyro-control-plane','agents','openai.yaml').is_file(); assert root.joinpath('dyro-dispatch','SKILL.md').is_file(); assert root.joinpath('dyro-dispatch','agents','openai.yaml').is_file(); assert root.joinpath('dyro-executor','SKILL.md').is_file(); assert root.joinpath('dyro-executor','agents','openai.yaml').is_file(); assert root.joinpath('dyro-board','SKILL.md').is_file(); assert root.joinpath('dyro-board','agents','openai.yaml').is_file(); assert root.joinpath('dyro-review-board','SKILL.md').is_file(); assert root.joinpath('dyro-review-board','agents','openai.yaml').is_file(); assert root.joinpath('dyro-task-merge','SKILL.md').is_file(); assert root.joinpath('dyro-task-merge','agents','openai.yaml').is_file()" "$smoke_root/wheel-venv/bin/python" -I -c "import importlib.util; assert importlib.util.find_spec('dyro.bridge') is None" test -x "$smoke_root/wheel-venv/bin/dyro" test ! -e "$smoke_root/wheel-venv/bin/dyro-bridge" @@ -161,7 +161,7 @@ jobs: "$smoke_root/sdist-venv/bin/pip" install "$GITHUB_WORKSPACE"/dist/dyro-*.tar.gz "$smoke_root/sdist-venv/bin/python" -c "import experiments.local_agent_dispatch" "$smoke_root/sdist-venv/bin/python" -I -c "from dyro.console.assets import validate_assets; validate_assets()" - "$smoke_root/sdist-venv/bin/python" -I -c "from importlib.resources import files; root=files('dyro.integrations').joinpath('assets'); assert root.joinpath('dyro-control-plane','SKILL.md').is_file(); assert root.joinpath('dyro-control-plane','agents','openai.yaml').is_file(); assert root.joinpath('dyro-dispatch','SKILL.md').is_file(); assert root.joinpath('dyro-dispatch','agents','openai.yaml').is_file(); assert root.joinpath('dyro-executor','SKILL.md').is_file(); assert root.joinpath('dyro-executor','agents','openai.yaml').is_file(); assert root.joinpath('dyro-board','SKILL.md').is_file(); assert root.joinpath('dyro-board','agents','openai.yaml').is_file()" + "$smoke_root/sdist-venv/bin/python" -I -c "from importlib.resources import files; root=files('dyro.integrations').joinpath('assets'); assert root.joinpath('dyro-control-plane','SKILL.md').is_file(); assert root.joinpath('dyro-control-plane','agents','openai.yaml').is_file(); assert root.joinpath('dyro-dispatch','SKILL.md').is_file(); assert root.joinpath('dyro-dispatch','agents','openai.yaml').is_file(); assert root.joinpath('dyro-executor','SKILL.md').is_file(); assert root.joinpath('dyro-executor','agents','openai.yaml').is_file(); assert root.joinpath('dyro-board','SKILL.md').is_file(); assert root.joinpath('dyro-board','agents','openai.yaml').is_file(); assert root.joinpath('dyro-review-board','SKILL.md').is_file(); assert root.joinpath('dyro-review-board','agents','openai.yaml').is_file(); assert root.joinpath('dyro-task-merge','SKILL.md').is_file(); assert root.joinpath('dyro-task-merge','agents','openai.yaml').is_file()" "$smoke_root/sdist-venv/bin/python" -I -c "import importlib.util; assert importlib.util.find_spec('dyro.bridge') is None" test -x "$smoke_root/sdist-venv/bin/dyro" test ! -e "$smoke_root/sdist-venv/bin/dyro-bridge" diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d7d932..a907814 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## Unreleased + +- First-party slash Skills `dyro-review-board` (会审 / 对抗审查) and + `dyro-task-merge` (task → line preflight). They install via + `dyro integration` and do not auto-load. Loading either is not merge, + push, or publish. `dyro-board` remains the protocol id; the public + label is 会审. + ## 0.7.5 - 2026-08-18 - Optional `local-image-gen` sidecar discovery: `dyro doctor` reports PATH diff --git a/docs/reviews/2026-08-19-slash-review-and-task-merge.md b/docs/reviews/2026-08-19-slash-review-and-task-merge.md new file mode 100644 index 0000000..22041aa --- /dev/null +++ b/docs/reviews/2026-08-19-slash-review-and-task-merge.md @@ -0,0 +1,92 @@ +# Dyro 斜杠命令 会审 / 对抗审查 + +Date: 2026-08-19 + +Scope: 本会话新增的用户斜杠 `/dyro-task-merge` 与 `/dyro-review-board`(skills-library),不是 Huiyichu 产品线,也不是 `dyro` 包装源码。 + +SSOT: 当前 skill 文件 + 已安装 `dyro-board` + `dyroengineeringflow` 中 `merge_task` / `explain_task` / `cmd_next`。本记录不是 Proof,不是 `task review` PASS。 + +## Rules + +1. 每位评审员只写自己的签字章节,不改写他人章节。 +2. 源码和现场契约高于会话里的设计口头约定。 +3. 无法证明标 `须人工核`。 +4. 仲裁只去重、裁定冲突、输出 P0/P1/P2 与 Go/No-Go。 +5. 本会审不 merge、不 push、不 signoff、不发布。 + +## Frozen Baseline (2026-08-19) + +| Object | Note | +| --- | --- | +| `dyro-task-merge` | 用户斜杠;`disable-model-invocation: true` | +| `dyro-review-board` | 用户斜杠;指向 first-party `dyro-board` | +| first-party `board` | `dyro integration status board` → `current` | +| `dyro-engineering` next | `needs_repair`;`commands=[]`;`mutation_available=false`;线 `dev_0814` expected `feat/dev_0814` found `chore/release-0.7.5` | + +## Reviewer Sections + +- A 安全与契约:[sections/01-task-merge.md](2026-08-19-slash-review-and-task-merge/sections/01-task-merge.md) +- B 命名与协议:[sections/02-review-board.md](2026-08-19-slash-review-and-task-merge/sections/02-review-board.md) + +# Final Arbitration + +Arbiter: Grok 4.6 会审 +Time: 2026-08-19 + +Final verdict: **No-Go for 提交 / 推送 / 发布。** 用户斜杠可继续用,但当前文本有成立的 P1。 + +用户本轮把「没问题就提交推送并发布」写进 `/dyro-review-board` 参数。会审 Go 也不构成该授权。`next.commands` 为空,不发明交付命令。 + +## Independent checks by arbiter + +1. `explain_task` / `_explain_with_config` 只解释调度(backlog/assigned、依赖、decision、conflict)。源码无 review / receipt / signoff / `PROOF_DECAYED`。A-02 成立。 +2. `_require_yes`:无 `--yes` 且非 `--dry-run` 才拒绝。官方只读预检是 `dyro --dry-run task merge `,不是 `task explain`。 +3. `cmd_next` 在 ready / needs_repair 都不发出 `task merge`。本 skill 打印 `--yes` 是自制 handoff,不是 `next.commands`。 +4. `merge_task` 仍是真正的门:status、复核绑定、signoff、线分支、线干净。预检漏了,CLI 仍会拒。因此 A-01/A-02 不定为「会立刻写坏 Git」的 P0,定为 **P1(预检会撒谎)**。 +5. 包装层 L15 把禁词扩到无范围的 `board`,与 `/dyro-review-board`、内部 id、恢复命令、记录文件名冲突。用户只确认禁「座位」。F3 冲突成立。 +6. 包装层 sibling 路径按 CWD 读会找不到已安装协议。F1 成立。 +7. 同一句「没问题就发布」:包装层禁令带「因为会审结果」,挡不住本轮用户授权误读。本仲裁用拒绝发布来执行闸门,并定为 P1。 + +## P0 + +无。没有已证实的「本 skill 进程会自己 merge / push / 发布」路径。 + +评审员 A 的 A-01/A-02、B 的 F5 原标 P0。仲裁降为 P1:爆炸半径是误导 handoff 和漏预检,真正写 Git 仍要人跑 `--yes`,且 `merge_task` 仍执法。 + +## P1(采纳) + +1. **`task explain` 不能证明 merge 就绪**(A-02,采纳)。预检第 5 步是死条件。应改用 `--dry-run task merge`,禁止 `--yes`。 +2. **预检 allowlist 看不到复核 / signoff / 任务 HEAD / allow_push**(A-03,采纳)。与第 1 条同一修法。 +3. **把自制 `task merge --yes` 写成控制面 `User action`**(A-01/A-05,降为 P1)。必须标明「不是 `next.commands`」,且本 skill 不得执行 `--yes`。 +4. **口头 push 就拼 `--push`**(A-04,采纳)。默认不要 `--push`。 +5. **sibling `dyro-board` 路径不稳定**(F1,采纳)。写死查找顺序:本 skill 的父目录 / 已安装 `dyro-board`。 +6. **禁词 `board` 过宽**(F3,采纳)。对人只禁「座位」。命令名、内部 id、记录文件名可说。 +7. **同一轮会审参数里的提交/推送/发布没有硬闸**(F5,降为 P1)。包装层必须写:同一句交付请求一律拒绝,拆成另一次命令。 +8. **双触发 / 包装层可当残缺协议跑**(F2/F4,采纳)。包装层加 `disable-model-invocation: true`;未读到 first-party 协议则停。 + +## P2 + +- A-06 explain 停条件歧义(第 1 条修掉后减弱) +- A-07 `task status` 双模态 +- A-08/A-09 合入主线与单参数歧义 +- A-10 doctor FAIL 范围 +- F6 Codex yaml 弱于 SKILL.md +- A-11 / A-12 须人工核 + +## Go / No-Go + +| 对象 | 结论 | +| --- | --- | +| 当作用户斜杠继续用 | Conditional Go(先收下 P1 文本) | +| 提交 / 推送 / 发布这些 skill 或任何 Dyro/Huiyichu 仓 | **No-Go** | +| 用本会审代替 `task review` / 发版 | **No-Go** | + +## 须人工核 + +1. 非 Grok 宿主是否尊重 `disable-model-invocation`。 +2. agent 会不会因「不要说 board」而拒绝念出 `/dyro-review-board`(修文案后应消失)。 +3. `dev_0814` 线分支漂移是否要另修;本会审不处理。 + +## Next human Dyro command + +无。`dyro --workspace dyro-engineering next`:`mutation_available=false`,`commands=[]`。不发明 merge / push / publish。 diff --git a/docs/reviews/2026-08-19-slash-review-and-task-merge/sections/01-task-merge.md b/docs/reviews/2026-08-19-slash-review-and-task-merge/sections/01-task-merge.md new file mode 100644 index 0000000..1eba12c --- /dev/null +++ b/docs/reviews/2026-08-19-slash-review-and-task-merge/sections/01-task-merge.md @@ -0,0 +1,17 @@ +# Reviewer A — 安全与契约 + +Reviewer: independent critic +Object: `dyro-task-merge` + +Findings A-01 … A-12 as signed in the 2026-08-19 会审 run. Not rewritten. + +成立的高优先级: + +- A-01:预检通过后打印 `task merge --yes`,而 `next` 从不发出该命令。 +- A-02:`task explain` 不报告 review / receipt / signoff / PROOF_DECAYED。 +- A-03:allowlist 观察不到 merge 谓词。 +- A-04:用户说 push 就发明 `--push`。 +- A-05:借用控制面 `User action` 外形。 +- A-06:explain 停条件歧义。 + +P2 / 须人工核:A-07 … A-12。 diff --git a/docs/reviews/2026-08-19-slash-review-and-task-merge/sections/02-review-board.md b/docs/reviews/2026-08-19-slash-review-and-task-merge/sections/02-review-board.md new file mode 100644 index 0000000..e929ce2 --- /dev/null +++ b/docs/reviews/2026-08-19-slash-review-and-task-merge/sections/02-review-board.md @@ -0,0 +1,16 @@ +# Reviewer B — 命名与协议 + +Reviewer: independent critic +Object: `dyro-review-board` + +Findings F1 … F6 as signed in the 2026-08-19 会审 run. Not rewritten. + +成立的高优先级: + +- F1:`dyro-board/SKILL.md` 按 CWD sibling 读会找不到已安装协议。 +- F2:包装层可当残缺协议独立跑。 +- F3:禁词 `board` 与命令名、内部 id、恢复命令、记录文件名冲突。 +- F4:与 first-party `dyro-board` 双触发。 +- F5:同一句「没问题就提交推送发布」挡不住。 + +P2 / 须人工核:F6;F3 是否导致拒绝说出命令名。 diff --git a/pyproject.toml b/pyproject.toml index 84c11ec..115ca7f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -71,6 +71,10 @@ packages = [ "assets/dyro-executor/agents/openai.yaml", "assets/dyro-board/SKILL.md", "assets/dyro-board/agents/openai.yaml", + "assets/dyro-review-board/SKILL.md", + "assets/dyro-review-board/agents/openai.yaml", + "assets/dyro-task-merge/SKILL.md", + "assets/dyro-task-merge/agents/openai.yaml", ] [tool.unittest] diff --git a/src/dyro/integrations/assets/dyro-review-board/SKILL.md b/src/dyro/integrations/assets/dyro-review-board/SKILL.md new file mode 100644 index 0000000..fe75bb1 --- /dev/null +++ b/src/dyro/integrations/assets/dyro-review-board/SKILL.md @@ -0,0 +1,51 @@ +--- +name: dyro-review-board +description: > + Run Dyro 会审 / 对抗审查. Use when the user runs /dyro-review-board. + Advisory record only; not Proof and not task review PASS. Same-turn + commit, push, or publish in the arguments is refused. +disable-model-invocation: true +user-invocable: true +argument-hint: "[topic]" +metadata: + short-description: "会审 / 对抗审查;不是交付 Proof" +--- + +# Dyro 会审 / 对抗审查 + +对人说 **会审** 或 **对抗审查**。不要说「座位」。 + +命令名 `/dyro-review-board`、内部 id `dyro-board`、`dyro integration status board`、记录文件名里的 `adversarial-board` 都可以说。 + +内部 id 仍是 `dyro-board`,不要改,也不要另写一套协议。 + +## Same-turn delivery + +If this turn also asks to 提交、commit、push、发布、publish、`task merge`、或「没问题就合入」: + +1. Do the 会审 only. +2. Refuse those delivery actions in this turn. +3. Say they need a later, separate command. 会审 Go is not that command. + +## Do this + +1. Load the first-party protocol before starting. Try, in order: + - `dyro-board/SKILL.md` next to this skill's parent directory + - the installed `dyro-board` skill already on this host +2. If none of those can be read, stop. Say 会审协议未安装;下一步是 `dyro integration status board`. +3. Follow that file exactly. Do not start 会审 from this wrapper alone. +4. In chat, call the activity 会审 or 对抗审查. Call the output a 记录. + +## Say this to developers + +- 这是会审 / 对抗审查:独立反证,最后由一个仲裁收口,不是投票。 +- 记录不是 Proof,也不是 `task review` PASS。 +- 会审给出 Go,也不等于可以 `task merge`、push 或发布。 +- `task review` 仍走 Core 回执绑定;不要用本次会审替代它。 + +## Do not + +- 不要 merge、push、signoff、`objective apply`、`dispatch run`、`task run`、git commit、或发布。 +- 不要改另一位评审员写过的章节。 +- 不要把模型票数升级成事实。 +- 不要扫描未安装的个人 skill 目录来代替 first-party 协议。 diff --git a/src/dyro/integrations/assets/dyro-review-board/agents/openai.yaml b/src/dyro/integrations/assets/dyro-review-board/agents/openai.yaml new file mode 100644 index 0000000..96235f7 --- /dev/null +++ b/src/dyro/integrations/assets/dyro-review-board/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Dyro 会审" + short_description: "会审 / 对抗审查记录,不是交付 Proof" + default_prompt: "Use $dyro-review-board. Load the installed dyro-board protocol first. Run 会审 / 对抗审查. Findings are a record, not Proof. Refuse same-turn commit, push, merge, or publish. Do not say 座位." diff --git a/src/dyro/integrations/assets/dyro-task-merge/SKILL.md b/src/dyro/integrations/assets/dyro-task-merge/SKILL.md new file mode 100644 index 0000000..5fa1d85 --- /dev/null +++ b/src/dyro/integrations/assets/dyro-task-merge/SKILL.md @@ -0,0 +1,89 @@ +--- +name: dyro-task-merge +description: > + Preflight whether a Dyro task can merge into its owning development line. + Use when the user runs /dyro-task-merge. Never execute the merge. +disable-model-invocation: true +user-invocable: true +argument-hint: "[workspace-alias] [task-id]" +metadata: + short-description: "Preflight task → line merge; do not merge" +--- + +# Dyro Task Merge + +Preflight only. This is not a first-party Dyro integration and not `task review` PASS. + +`task merge` means **done task branch → owning development line**. +It does not mean line → `main`, release, tag, or PyPI. + +If the user said 合入主线 and means git `main`, say Dyro has no such command here and stop **before** any preflight. + +## Hard boundary + +Do not run any of: + +- `dyro task merge` with `--yes` +- `git merge` / `git switch` / `git checkout` +- `task signoff`, `task gates`, `task review`, `task run` +- `task status ` (the write form) +- `objective apply`, `dispatch`, `console`, push, publish + +Do not invent `--yes` or `--push`. Do not restore a drifted line branch. +Do not add `--include-paths`. + +A 会审 Go, a green test log, or an empty `next.commands` is not permission to merge. + +## Resolve workspace + +1. If the user named both an alias and a task id, use them as named. +2. If only one token is given: treat it as a workspace alias when `dyro workspace list --format json` contains that name; otherwise treat it as a task id. +3. If no alias, run `dyro workspace list --format json`. If more than one workspace is `available`, ask once; do not guess. +4. Keep `--workspace ` on every later command. + +## Allowlisted reads + +```bash +dyro --workspace doctor --format json +dyro --workspace status --format json +dyro --workspace next --format json +dyro --workspace line list --format json +dyro --workspace task list +dyro --workspace task status +dyro --workspace --dry-run task merge +``` + +`task status` is read-only only when no status value is passed. +Never pass `--yes` to `task merge`. `--dry-run task merge` is the merge-gate preflight (review, receipt, task HEAD, signoff, line branch, dirty line). +Prefer JSON when the command accepts `--format json`. One JSON document only; `kind=error` is blocked evidence. + +## Preflight + +1. `doctor`. Any `FAIL` → stop. +2. `status`. The task's line row must match the registered branch and `dirty_count` must be 0. Otherwise stop. +3. Choose the task: + - Use the task id in the slash arguments if present. + - Else from `task list`, take the unique `done` task on the intended line. + - If several `done` tasks or none, ask once. +4. `task status ` must be `done`. Any other status → stop. +5. `dyro --workspace --dry-run task merge `. Non-zero exit or `kind=error` → stop and quote the CLI error. Do not proceed. + +## Report + +Use Observed / Inferred / Unknown / Plan / User action. + +If preflight failed, `User action` is only: + +```bash +dyro --workspace doctor +``` + +or the `--dry-run task merge` that failed. Do not print a live merge command. + +If every preflight step passed, say clearly that `next.commands` did not emit this, then show **one** command for the user to run personally: + +```bash +dyro --workspace task merge --yes +``` + +Do not add `--push`. Do not run that command. diff --git a/src/dyro/integrations/assets/dyro-task-merge/agents/openai.yaml b/src/dyro/integrations/assets/dyro-task-merge/agents/openai.yaml new file mode 100644 index 0000000..1441325 --- /dev/null +++ b/src/dyro/integrations/assets/dyro-task-merge/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Dyro Task Merge" + short_description: "Preflight task merge into the owning line; do not merge" + default_prompt: "Use $dyro-task-merge. Preflight with doctor/status and --dry-run task merge only. Never run task merge --yes, git merge, or push. A printed command is not next.commands." diff --git a/src/dyro/integrations/manager.py b/src/dyro/integrations/manager.py index 1a5d214..5cd98da 100644 --- a/src/dyro/integrations/manager.py +++ b/src/dyro/integrations/manager.py @@ -47,6 +47,12 @@ BOARD_INTEGRATION_ID = BOARD_ID BOARD_SKILL_NAME = BOARD_SKILL BOARD_ASSET_VERSION = 1 +REVIEW_BOARD_INTEGRATION_ID = "review-board" +REVIEW_BOARD_SKILL_NAME = "dyro-review-board" +REVIEW_BOARD_ASSET_VERSION = 1 +TASK_MERGE_INTEGRATION_ID = "task-merge" +TASK_MERGE_SKILL_NAME = "dyro-task-merge" +TASK_MERGE_ASSET_VERSION = 1 MANIFEST_SCHEMA_VERSION = 2 LEGACY_MANIFEST_SCHEMA_VERSION = 1 _SHA256_PREFIX = "sha256:" @@ -83,11 +89,23 @@ class SkillIntegrationSpec: skill_name=BOARD_SKILL_NAME, asset_version=BOARD_ASSET_VERSION, ) +REVIEW_BOARD_SPEC = SkillIntegrationSpec( + integration_id=REVIEW_BOARD_INTEGRATION_ID, + skill_name=REVIEW_BOARD_SKILL_NAME, + asset_version=REVIEW_BOARD_ASSET_VERSION, +) +TASK_MERGE_SPEC = SkillIntegrationSpec( + integration_id=TASK_MERGE_INTEGRATION_ID, + skill_name=TASK_MERGE_SKILL_NAME, + asset_version=TASK_MERGE_ASSET_VERSION, +) SKILL_INTEGRATIONS: tuple[SkillIntegrationSpec, ...] = ( CONTROL_PLANE_SPEC, DISPATCH_SPEC, EXECUTOR_SPEC, BOARD_SPEC, + REVIEW_BOARD_SPEC, + TASK_MERGE_SPEC, ) INTEGRATION_CHOICES: tuple[str, ...] = tuple( choice diff --git a/src/dyro/integrations/seats.py b/src/dyro/integrations/seats.py index 23a5709..35dbd82 100644 --- a/src/dyro/integrations/seats.py +++ b/src/dyro/integrations/seats.py @@ -72,7 +72,7 @@ class SeatSpec: SeatSpec( BOARD_ID, BOARD_SKILL, - "评审板", + "会审", "board", True, ( diff --git a/tests/test_integrations.py b/tests/test_integrations.py index c0d452e..ffa42da 100644 --- a/tests/test_integrations.py +++ b/tests/test_integrations.py @@ -218,6 +218,8 @@ def test_executor_and_board_skills_install_independently(self) -> None: for integration, skill_name in ( ("executor", "dyro-executor"), ("board", "dyro-board"), + ("review-board", "dyro-review-board"), + ("task-merge", "dyro-task-merge"), ): with self.subTest(integration=integration): installed = install_integration(integration, yes=True) diff --git a/tests/test_release_source.py b/tests/test_release_source.py index fc2e7c0..11b4132 100644 --- a/tests/test_release_source.py +++ b/tests/test_release_source.py @@ -97,6 +97,8 @@ def test_publish_workflow_requires_successful_exact_sha_ci_gate(self) -> None: self.assertIn("dyro-dispatch','agents','openai.yaml", workflow) self.assertIn("dyro-executor','SKILL.md", workflow) self.assertIn("dyro-board','SKILL.md", workflow) + self.assertIn("dyro-review-board','SKILL.md", workflow) + self.assertIn("dyro-task-merge','SKILL.md", workflow) def test_ci_no_longer_ships_agent_bridge_zero_effect_gate(self) -> None: workflow = (ROOT / ".github" / "workflows" / "ci.yml").read_text( @@ -111,6 +113,8 @@ def test_ci_no_longer_ships_agent_bridge_zero_effect_gate(self) -> None: self.assertIn("dyro-dispatch','agents','openai.yaml", workflow) self.assertIn("dyro-executor','SKILL.md", workflow) self.assertIn("dyro-board','SKILL.md", workflow) + self.assertIn("dyro-review-board','SKILL.md", workflow) + self.assertIn("dyro-task-merge','SKILL.md", workflow) self.assertGreaterEqual(workflow.count("verify_bundle_stranger.py"), 2) self.assertGreaterEqual( workflow.count("find_spec('dyro.bridge') is None"), 2 diff --git a/tests/test_seats.py b/tests/test_seats.py index 259dda3..0f23312 100644 --- a/tests/test_seats.py +++ b/tests/test_seats.py @@ -36,7 +36,7 @@ def test_first_batch_is_four_seats_and_omits_reviewer(self) -> None: ("skill", "控制面"), ("dispatch", "Dispatch"), ("executor", "执行"), - ("board", "评审板"), + ("board", "会审"), ), ) names = {seat.skill_name for seat in SEATS} @@ -110,6 +110,26 @@ def test_packaged_first_batch_skills_auto_trigger_and_stay_bounded(self) -> None msg=f"{seat.skill_name}: {pattern}", ) + def test_slash_skills_are_packaged_but_not_auto_load_seats(self) -> None: + names = {seat.skill_name for seat in SEATS} + self.assertNotIn("dyro-review-board", names) + self.assertNotIn("dyro-task-merge", names) + metadata = Path(__file__).resolve().parents[1].joinpath("pyproject.toml") + text = metadata.read_text(encoding="utf-8") + for skill_name in ("dyro-review-board", "dyro-task-merge"): + self.assertIn(f"assets/{skill_name}/SKILL.md", text) + content = ( + Path(__file__).resolve().parents[1] + / "src" + / "dyro" + / "integrations" + / "assets" + / skill_name + / "SKILL.md" + ).read_text(encoding="utf-8") + self.assertIn("disable-model-invocation: true", content) + self.assertNotIn("TODO", content) + def test_wheel_package_data_lists_every_first_batch_skill(self) -> None: metadata = Path(__file__).resolve().parents[1].joinpath("pyproject.toml") text = metadata.read_text(encoding="utf-8") From 32f34881132676f2c37da560e87ebbaaa066583b Mon Sep 17 00:00:00 2001 From: DandreYang <13072547+Dandre126@users.noreply.github.com> Date: Wed, 19 Aug 2026 13:26:14 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat(=E4=BA=A4=E4=BB=98=E7=89=A9=E7=90=86):?= =?UTF-8?q?=20=E5=87=86=E5=A4=87=200.7.6=20=E5=8F=91=E5=87=BA=E4=BC=9A?= =?UTF-8?q?=E5=AE=A1=E6=96=9C=E6=9D=A0=E5=92=8C=E4=BB=BB=E5=8A=A1=E5=90=88?= =?UTF-8?q?=E5=85=A5=E9=A2=84=E6=A3=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 把已拣选的第一方斜杠 Skill 收进可发布的补丁号,避免另开 0.8 或 1.0 功能列车。 --- CHANGELOG.md | 2 +- docs/adr/0006-delivery-physics-and-capability-plane.md | 2 +- docs/architecture.md | 2 +- pyproject.toml | 2 +- src/dyro/bridge/skill/SKILL.md | 2 +- tests/test_release_gates.py | 2 +- uv.lock | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a907814..0a11c5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -## Unreleased +## 0.7.6 - 2026-08-19 - First-party slash Skills `dyro-review-board` (会审 / 对抗审查) and `dyro-task-merge` (task → line preflight). They install via diff --git a/docs/adr/0006-delivery-physics-and-capability-plane.md b/docs/adr/0006-delivery-physics-and-capability-plane.md index 69adfaf..e7d01d0 100644 --- a/docs/adr/0006-delivery-physics-and-capability-plane.md +++ b/docs/adr/0006-delivery-physics-and-capability-plane.md @@ -64,7 +64,7 @@ - 产品叙事从「启动 agent」转为「核验完成」。 - `0.7.0` 落地 Proof、Capability Card、Host Compiler 与 `verify-bundle`。`trusted_usage` 默认 `false`;生产 `BudgetUsage` / `BudgetRequest` 从 Card 读取该字段。`objective tick` 对 automatic Objective 做预算预览(`automatic=True`),受监督 apply 仍 `automatic=False`。未信任用量只在存在 `workspace.max_provider_usage` 时硬停。Proof 仍不接入 `no_progress`。Console summary 保持 `proof_inspection=not_inspected`,不探 Git / Proof;`dyro objective attention` 走完整快照,可报 `PROOF_DECAYED`。两套入口不得写成同一套 Proof 展示。 -- Console 独立 inspect、`trigger_observation`、陌生人核验与叙事锁已在 `0.7.1` 落地。`0.7.2` 收口换工具 briefing 与 Console「现在需要你」。`0.7.3` 把空关注收回未读,并去掉首页与 `next` repair 的路径泄漏。`0.7.4` 发出第一方 Skill 首批座位:控制面、执行、评审板与 Dispatch;按情景自动戴上,戴上不是突变授权;不扫描用户 skill 目录。`0.7.5` 增加可选 `local-image-gen` sidecar 发现:`dyro doctor` 只看 PATH,`dyro image` 不进编码工具目录或座位。后续功能与产品面收口继续开发和上线,版本号保持 `0.7.x`。 +- Console 独立 inspect、`trigger_observation`、陌生人核验与叙事锁已在 `0.7.1` 落地。`0.7.2` 收口换工具 briefing 与 Console「现在需要你」。`0.7.3` 把空关注收回未读,并去掉首页与 `next` repair 的路径泄漏。`0.7.4` 发出第一方 Skill 首批座位:控制面、执行、评审板与 Dispatch;按情景自动戴上,戴上不是突变授权;不扫描用户 skill 目录。`0.7.5` 增加可选 `local-image-gen` sidecar 发现:`dyro doctor` 只看 PATH,`dyro image` 不进编码工具目录或座位。`0.7.6` 发出第一方斜杠 Skill:`dyro-review-board`(会审 / 对抗审查)与 `dyro-task-merge`(任务合入开发线预检);经 `dyro integration` 安装,不自动触发,加载不等于 merge、push 或发布。后续功能与产品面收口继续开发和上线,版本号保持 `0.7.x`。 - 可携带核验的对外承诺仍是:陌生人拿着 Proof Bundle 和自己提供的 git 对象,能得到与源机**相同的完整性结论**(字节仍在、钉死 SHA 可解析)。这不是身份证明,也不是「现在工作区还能 merge」。`schema_version = 1` 的合同在 `0.7.x` 锁住语义;冻结成 `1.0.0` 身份号须另做产品决定。 - 实施成本是新的投影层与兼容层,而不是第二套调度器。 diff --git a/docs/architecture.md b/docs/architecture.md index c10ca78..f476021 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -242,6 +242,6 @@ Dyro 的交付拓扑与之**实质相近**:TaskGraph(`depends_on` / conflict 未来的 adapter、通知、签名规则、发布平台与审批系统应使用 Python entry point 或独立 Profile 扩展包接入;不要把某个组织的策略加入 core 默认行为。 -`0.7.0` 把已有证据物理学抽成可复验的 Proof,并把 argv adapter 升级为 Capability Card,再把定律编译为只收缩权威的宿主投影。衰减与现有 merge / 下游检查同真值;`proof verify` 看当前工作区,`verify-bundle` 只核完整性,两套结论不得混称。Console summary 与 `dyro objective attention` 不是同一套 Proof 展示。Console 独立 inspect、`trigger_observation` 与可携带核验门禁已在 `0.7.1` 落地。`0.7.2` 把换工具后的开场白收成事项加一条只读下一步,不另开会话层。`0.7.3` 把 Console 空关注收回未读,并不再在首页和下一条修复命令里漏本地路径。`0.7.4` 发出第一方 Skill 首批座位:控制面、执行、评审板与 Dispatch;按情景自动戴上,戴上不等于同意突变;不扫描用户 skill 目录。`0.7.5` 增加可选 `local-image-gen` sidecar 发现:`dyro doctor` 只看 PATH,`dyro image` 不进编码工具目录或座位。后续功能继续在 `0.7.x` 开发和上线,不另开 `0.8.0` / `0.9.0` / `1.0.0` 功能号。可携带核验是 Proof Bundle 加调用方提供的 git 对象,核验完整性而不是身份,也不承诺与当前 merge 同一套 `live`。这不另造 TaskGraph 或完成状态机;见 [`交付物理学`](designs/delivery-physics.md) 与 [`ADR-0006`](adr/0006-delivery-physics-and-capability-plane.md)。 +`0.7.0` 把已有证据物理学抽成可复验的 Proof,并把 argv adapter 升级为 Capability Card,再把定律编译为只收缩权威的宿主投影。衰减与现有 merge / 下游检查同真值;`proof verify` 看当前工作区,`verify-bundle` 只核完整性,两套结论不得混称。Console summary 与 `dyro objective attention` 不是同一套 Proof 展示。Console 独立 inspect、`trigger_observation` 与可携带核验门禁已在 `0.7.1` 落地。`0.7.2` 把换工具后的开场白收成事项加一条只读下一步,不另开会话层。`0.7.3` 把 Console 空关注收回未读,并不再在首页和下一条修复命令里漏本地路径。`0.7.4` 发出第一方 Skill 首批座位:控制面、执行、评审板与 Dispatch;按情景自动戴上,戴上不等于同意突变;不扫描用户 skill 目录。`0.7.5` 增加可选 `local-image-gen` sidecar 发现:`dyro doctor` 只看 PATH,`dyro image` 不进编码工具目录或座位。`0.7.6` 发出第一方斜杠 Skill:`dyro-review-board`(会审 / 对抗审查)与 `dyro-task-merge`(任务合入开发线预检);经 `dyro integration` 安装,不自动触发,加载不等于 merge、push 或发布。后续功能继续在 `0.7.x` 开发和上线,不另开 `0.8.0` / `0.9.0` / `1.0.0` 功能号。可携带核验是 Proof Bundle 加调用方提供的 git 对象,核验完整性而不是身份,也不承诺与当前 merge 同一套 `live`。这不另造 TaskGraph 或完成状态机;见 [`交付物理学`](designs/delivery-physics.md) 与 [`ADR-0006`](adr/0006-delivery-physics-and-capability-plane.md)。 开发者侧的可选本地多 Agent 派发(五段式任务契约、注入前机密守卫、locator 核验、隔离 patch)与上述控制面分层并列,随 `dyro` 安装包分发(`dyro dispatch` / `import experiments.local_agent_dispatch`),但**不**替代 gates/合并。同时写多块走 Core Peer Wave(task worktree + `conflict_group`),见 [`peer-wave-execution.md`](designs/peer-wave-execution.md)、[`ADR-0002`](adr/0002-optional-local-agent-dispatch.md)、[`多智能体编排纪律`](agent-orchestration-discipline.md) 与 [`可选本地 Agent 派发设计`](designs/optional-local-agent-dispatch.md)。 diff --git a/pyproject.toml b/pyproject.toml index 115ca7f..7f5ae7b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "dyro" -version = "0.7.5" +version = "0.7.6" description = "DyroEngineeringFlow: local-first automation and delivery control for multi-repository teams" readme = "README.md" requires-python = ">=3.11" diff --git a/src/dyro/bridge/skill/SKILL.md b/src/dyro/bridge/skill/SKILL.md index c10d09d..07c5370 100644 --- a/src/dyro/bridge/skill/SKILL.md +++ b/src/dyro/bridge/skill/SKILL.md @@ -50,7 +50,7 @@ release, publish, console, install, or any confirmation/approval field. ```json { "protocol": {"major": 1, "minor": 0}, - "client": {"name": "dyro-agent-bridge-skill", "version": "0.7.5"}, + "client": {"name": "dyro-agent-bridge-skill", "version": "0.7.6"}, "operation": "bridge.capabilities.compact", "input": {} } diff --git a/tests/test_release_gates.py b/tests/test_release_gates.py index f53accd..43f159d 100644 --- a/tests/test_release_gates.py +++ b/tests/test_release_gates.py @@ -32,7 +32,7 @@ def test_physics_train_refuses_published_0_6_9_tag(self) -> None: def test_0_7_release_runs_gates_without_claiming_1_0(self) -> None: stdout = StringIO() with redirect_stdout(stdout): - code = main(["--root", str(ROOT), "--release-tag", "v0.7.5"]) + code = main(["--root", str(ROOT), "--release-tag", "v0.7.6"]) self.assertEqual(code, 0) self.assertIn("0.7 gates present", stdout.getvalue()) self.assertNotIn("1.0 gates present", stdout.getvalue()) diff --git a/uv.lock b/uv.lock index d6c1a96..043033a 100644 --- a/uv.lock +++ b/uv.lock @@ -286,7 +286,7 @@ wheels = [ [[package]] name = "dyro" -version = "0.7.5" +version = "0.7.6" source = { editable = "." } dependencies = [ { name = "cryptography" },