Merge pull request 'feat(onboarding/parity): lian-jing skill 补写操作确认与 unavailable/unknown 两条通用边界,接入包升至 1.8.0' (#31) from feat/agent-client-parity-p7-skill-rules into master
All checks were successful
Push & PR Check / check (push) Successful in 58s
All checks were successful
Push & PR Check / check (push) Successful in 58s
This commit is contained in:
commit
19b9388ccf
@ -145,7 +145,8 @@ skill 只是五个接入面之一,兼容层覆盖全部五面。
|
|||||||
② Codex CLI 0.155.0-alpha.9.2(codex exec --json -s read-only,-c mcp_servers.ljprobe.url 临时覆盖,用户默认模型,cwd 为空目录):不支持。实验组、对照组都答 NONE;桩服务日志证明 initialize(codex-mcp-client,协议 2025-06-18)与 tools/list 均成功,即说明已送达客户端、但没进入模型;两组输入 token 完全相同(16320 = 16320;换宽问法「全部上下文里是否有 LJ-P7- 开头的字符串」为 16335 = 16335);该会话 rollout 文件中标记出现 0 次。正向对照:让模型调用 probe_ping,它确实发起 mcp_tool_call(server=ljprobe, tool=probe_ping)(被 approval policy never 拦下),说明桩服务的工具在模型视野里,NONE 不是没连上。加开发中特性 --enable mcp_2026_07_28 后仍答 NONE,输入 token 仍为 16320。
|
② Codex CLI 0.155.0-alpha.9.2(codex exec --json -s read-only,-c mcp_servers.ljprobe.url 临时覆盖,用户默认模型,cwd 为空目录):不支持。实验组、对照组都答 NONE;桩服务日志证明 initialize(codex-mcp-client,协议 2025-06-18)与 tools/list 均成功,即说明已送达客户端、但没进入模型;两组输入 token 完全相同(16320 = 16320;换宽问法「全部上下文里是否有 LJ-P7- 开头的字符串」为 16335 = 16335);该会话 rollout 文件中标记出现 0 次。正向对照:让模型调用 probe_ping,它确实发起 mcp_tool_call(server=ljprobe, tool=probe_ping)(被 approval policy never 拦下),说明桩服务的工具在模型视野里,NONE 不是没连上。加开发中特性 --enable mcp_2026_07_28 后仍答 NONE,输入 token 仍为 16320。
|
||||||
③ DSH(桌面版内置运行时 headless;--patch 叠加层停用用户已有的三个 MCP 条目、插入指向桩服务的 dsh-mcp-client 条目,用户 ~/.dsh 与 DSH_HOME 均未改):支持。实验组答出 LJ-P7-B0628021BF,思考过程原样复述了说明全文;对照组答 NONE;实验组输入 token 多 53(6487 对 6434)。DSH 在 initialize 前先发 server/discover,桩服务与炼境一样回 -32601,DSH 随即回退到 initialize → tools/list;其 mcp-client 配置有 maxInstructionBytes(默认 32768)。
|
③ DSH(桌面版内置运行时 headless;--patch 叠加层停用用户已有的三个 MCP 条目、插入指向桩服务的 dsh-mcp-client 条目,用户 ~/.dsh 与 DSH_HOME 均未改):支持。实验组答出 LJ-P7-B0628021BF,思考过程原样复述了说明全文;对照组答 NONE;实验组输入 token 多 53(6487 对 6434)。DSH 在 initialize 前先发 server/discover,桩服务与炼境一样回 -32601,DSH 随即回退到 initialize → tools/list;其 mcp-client 配置有 maxInstructionBytes(默认 32768)。
|
||||||
探针脚本、桩服务与原始日志留在本机 %TEMP%\lj-p7-probe(未入库,P4 固化时可参考)。"
|
探针脚本、桩服务与原始日志留在本机 %TEMP%\lj-p7-probe(未入库,P4 固化时可参考)。"
|
||||||
- known_limits: "Claude 与 DSH 只验了无头模式,Claude 只用了 sonnet-4-6;Codex 未验 TUI 与桌面版(桌面版可能走 app-server 路径,结论可能不同),也未验 SSE 传输。探针用的是桩服务的中英混合短说明,未用炼境真实说明(中文 238 字、UTF-8 500 字节,远低于 DSH 默认上限 32768 字节)实测。Codex 后续版本若开始带入服务器说明,按「检测行为而非嗅探版本」重跑本探针(P4 固化后纳入矩阵),三端都支持时再迁移。现有服务器说明五条要点中,「先读状态」「canonical project_id」「开发变量先查后登记」在 skill 有对应条目;「其他写操作须用户明确确认」与「unavailable/unknown 不得当作成功」在 skill 与 AGENTS.md 中只有分场景表述(合并 PR、CI workflow、工具链安装;MCP 不可用时不伪造诊断),Codex 会话只能从这些条目与工具描述获知——是否补成通用条目留待下次改 skill 时一并处理(需升接入包版本),本卡不改。"
|
- known_limits: "Claude 与 DSH 只验了无头模式,Claude 只用了 sonnet-4-6;Codex 未验 TUI 与桌面版(桌面版可能走 app-server 路径,结论可能不同),也未验 SSE 传输。探针用的是桩服务的中英混合短说明,未用炼境真实说明(中文 238 字、UTF-8 500 字节,远低于 DSH 默认上限 32768 字节)实测。Codex 后续版本若开始带入服务器说明,按「检测行为而非嗅探版本」重跑本探针(P4 固化后纳入矩阵),三端都支持时再迁移。现有服务器说明五条要点中,「先读状态」「canonical project_id」「开发变量先查后登记」在 skill 有对应条目;「其他写操作须用户明确确认」与「unavailable/unknown 不得当作成功」在 skill 与 AGENTS.md 中只有分场景表述(合并 PR、CI workflow、工具链安装;MCP 不可用时不伪造诊断),Codex 会话只能从这些条目与工具描述获知——是否补成通用条目留待下次改 skill 时一并处理(需升接入包版本),本卡不改。
|
||||||
|
已补齐(2026-10-03,分支 feat/agent-client-parity-p7-skill-rules):两条已作为通用边界写进 lian-jing skill 模板开头(双投影到 .agents 与 .claude),两条规则的核心句与服务器说明逐字一致;只读查询、登记开发需求元数据与回传炼境明确列为无需确认的例外,与服务器说明的「其他写操作」及 skill「回传不必逐条征得同意」相容。服务器说明的例外是泛称「不含秘密的开发需求元数据」,skill 例外括注同时列出两个登记工具 register_project_test_account 与 register_project_dev_variable(审查修复:初版只列前者,会把开发变量登记误归入须确认的写操作);同批修正 skill 第 8 条——开发变量改用 register_project_dev_variable 登记名称与用途,测试账号仍用 register_project_test_account(初版两者都指向后者)。接入包 base-onboarding 1.7.0→1.8.0(修复在 1.8.0 发布前,未再升版本,只更新指纹);mcp_server 新增 server_instruction_universal_rules_are_in_skill,服务器说明与 skill 任一侧缺这两条即失败,skill 例外行缺任一登记工具也失败。AGENTS.md 托管块未改,仍只有分场景表述;存量项目需重建安装炼境后重跑 apply_onboarding_pack 才拿到新 skill。"
|
||||||
|
|
||||||
### 📋 P8 — 一键接入:创建/添加子项目即完成三端可用
|
### 📋 P8 — 一键接入:创建/添加子项目即完成三端可用
|
||||||
- status: todo
|
- status: todo
|
||||||
|
|||||||
@ -7,7 +7,12 @@ description: 读取炼境项目健康状态,并使用炼境 MCP 的项目管
|
|||||||
|
|
||||||
开始项目工作时,先读取根 `AGENTS.md` 与 `.lian-jing/context.json` 取得 `project_id`。`.claude/liangjing.json` 仅为旧项目的 Claude 兼容投影,不能作为长期身份来源。
|
开始项目工作时,先读取根 `AGENTS.md` 与 `.lian-jing/context.json` 取得 `project_id`。`.claude/liangjing.json` 仅为旧项目的 Claude 兼容投影,不能作为长期身份来源。
|
||||||
|
|
||||||
如果当前客户端已发现 `lian-jing` MCP:
|
如果当前客户端已发现 `lian-jing` MCP,所有调用都遵守两条通用边界(与炼境 MCP 服务器说明一致):
|
||||||
|
|
||||||
|
- 只读查询、登记不含秘密的开发需求元数据(`register_project_test_account`、`register_project_dev_variable`)与回传炼境(`report_envoy_return`)可直接调用;其他写操作仅在用户明确确认目标与影响后调用,下文安装工具链、合并 PR、生成 CI workflow 是它的具体场景。
|
||||||
|
- 状态 `unavailable` / `unknown` 不得当作成功:如实报告实际状态,不据此宣称完成。
|
||||||
|
|
||||||
|
具体用法:
|
||||||
|
|
||||||
1. scoped MCP 连接已由 URL 绑定当前 `project_id`,项目级调用可省略 `project_id`;若 server initialize 明确报告 legacy,迁移前才显式传入 canonical `project_id`,绝不传其他项目 ID。
|
1. scoped MCP 连接已由 URL 绑定当前 `project_id`,项目级调用可省略 `project_id`;若 server initialize 明确报告 legacy,迁移前才显式传入 canonical `project_id`,绝不传其他项目 ID。
|
||||||
2. 优先调用 `get_diagnostics`,将其作为当前项目健康状态的事实来源。
|
2. 优先调用 `get_diagnostics`,将其作为当前项目健康状态的事实来源。
|
||||||
@ -16,7 +21,7 @@ description: 读取炼境项目健康状态,并使用炼境 MCP 的项目管
|
|||||||
5. 交付、Gitea、CI、蓝图等平台状态优先通过 MCP 查询,不凭静态文档猜测。
|
5. 交付、Gitea、CI、蓝图等平台状态优先通过 MCP 查询,不凭静态文档猜测。
|
||||||
6. 合并 PR 前调用 `get_delivery_status(project_id)`,向用户展示 PR 编号、完整 head SHA、CI 与 review。只有当前用户明确确认该 PR 与 SHA 后,才调用 `merge_pull_request(project_id, pr_number, expected_head_sha, confirmed=true)`;合并后再次读取交付状态。不得自行推断确认。
|
6. 合并 PR 前调用 `get_delivery_status(project_id)`,向用户展示 PR 编号、完整 head SHA、CI 与 review。只有当前用户明确确认该 PR 与 SHA 后,才调用 `merge_pull_request(project_id, pr_number, expected_head_sha, confirmed=true)`;合并后再次读取交付状态。不得自行推断确认。
|
||||||
7. 生成 CI workflow 时先调用 `plan_cicd_workflow(project_id, workflow_type)`,向用户展示目标文件、变更类型、`content_digest`、工具链来源与所需 secret 名称;只有用户明确确认这份摘要后,才以同一 digest 调用 `apply_cicd_workflow(..., confirmed=true)`。该流程只写项目 workflow,不配置 secret、不触发 CI 或部署;不得通过 MCP 传服务器地址、命令、路径或 secret 值,部署类配置先由用户在炼境 UI 保存。
|
7. 生成 CI workflow 时先调用 `plan_cicd_workflow(project_id, workflow_type)`,向用户展示目标文件、变更类型、`content_digest`、工具链来源与所需 secret 名称;只有用户明确确认这份摘要后,才以同一 digest 调用 `apply_cicd_workflow(..., confirmed=true)`。该流程只写项目 workflow,不配置 secret、不触发 CI 或部署;不得通过 MCP 传服务器地址、命令、路径或 secret 值,部署类配置先由用户在炼境 UI 保存。
|
||||||
8. 需要测试账号或开发变量(登录自验、沙箱 key 等)时,先调用 `list_project_dev_variables` 查找:已配置则用 `read_project_test_account` / `read_project_dev_variable` 单项读取;没有则用 `register_project_test_account` 登记名称、目标系统、非生产环境与用途,并请用户在炼境项目 UI「开发变量」中填写。不得在对话中向用户索要明文密码,不得登记生产账号,不得把读到的值写入受 Git 跟踪的文件。读取因缺少项目认证被拒时,如实报告并改用项目原有配置方式,不得绕过认证。若 lian-jing 工具中根本没有 `list_project_dev_variables`,说明当前客户端连的是全局地址 `/mcp` 而非本项目地址,按下方 `configured` 一步处理。
|
8. 需要测试账号或开发变量(登录自验、沙箱 key 等)时,先调用 `list_project_dev_variables` 查找:已配置则用 `read_project_test_account` / `read_project_dev_variable` 单项读取;没有则先登记需求——测试账号用 `register_project_test_account` 登记名称、目标系统、非生产环境与用途,开发变量用 `register_project_dev_variable` 登记名称与用途——再请用户在炼境项目 UI「开发变量」中填写。不得在对话中向用户索要明文密码,不得登记生产账号,不得把读到的值写入受 Git 跟踪的文件。读取因缺少项目认证被拒时,如实报告并改用项目原有配置方式,不得绕过认证。若 lian-jing 工具中根本没有 `list_project_dev_variables`,说明当前客户端连的是全局地址 `/mcp` 而非本项目地址,按下方 `configured` 一步处理。
|
||||||
9. 遇到体系级摩擦时,按下方「遇到摩擦时回传炼境」调用 `report_envoy_return`。
|
9. 遇到体系级摩擦时,按下方「遇到摩擦时回传炼境」调用 `report_envoy_return`。
|
||||||
|
|
||||||
## 遇到摩擦时回传炼境
|
## 遇到摩擦时回传炼境
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
"packs": [
|
"packs": [
|
||||||
{
|
{
|
||||||
"pack_id": "base-onboarding",
|
"pack_id": "base-onboarding",
|
||||||
"version": "1.7.0",
|
"version": "1.8.0",
|
||||||
"supported_clients": ["codex", "claude", "dsh"],
|
"supported_clients": ["codex", "claude", "dsh"],
|
||||||
"project_artifacts": [
|
"project_artifacts": [
|
||||||
"AGENTS.md",
|
"AGENTS.md",
|
||||||
|
|||||||
@ -19,14 +19,14 @@ const TMPL_GIT_WORKFLOW: &str = include_str!("../../resources/onboarding/git-wor
|
|||||||
const TMPL_ENVOY: &str = include_str!("../../resources/onboarding/.claude/commands/envoy.md");
|
const TMPL_ENVOY: &str = include_str!("../../resources/onboarding/.claude/commands/envoy.md");
|
||||||
const TMPL_BLUEPRINT_GATE: &str =
|
const TMPL_BLUEPRINT_GATE: &str =
|
||||||
include_str!("../../resources/onboarding/.claude/hooks/blueprint-gate.cjs");
|
include_str!("../../resources/onboarding/.claude/hooks/blueprint-gate.cjs");
|
||||||
const TMPL_LIANJING_SKILL: &str =
|
pub(crate) const TMPL_LIANJING_SKILL: &str =
|
||||||
include_str!("../../resources/onboarding/.agents/skills/lian-jing/SKILL.md");
|
include_str!("../../resources/onboarding/.agents/skills/lian-jing/SKILL.md");
|
||||||
const TMPL_CAPABILITY_PACKS: &str =
|
const TMPL_CAPABILITY_PACKS: &str =
|
||||||
include_str!("../../resources/onboarding/.lian-jing/capability-packs.json");
|
include_str!("../../resources/onboarding/.lian-jing/capability-packs.json");
|
||||||
const TMPL_MISE: &str = include_str!("../../resources/onboarding/mise.toml.tmpl");
|
const TMPL_MISE: &str = include_str!("../../resources/onboarding/mise.toml.tmpl");
|
||||||
const PROJECT_CONTEXT_SCHEMA_VERSION: u64 = 1;
|
const PROJECT_CONTEXT_SCHEMA_VERSION: u64 = 1;
|
||||||
const CAPABILITY_PACK_SCHEMA_VERSION: u64 = 1;
|
const CAPABILITY_PACK_SCHEMA_VERSION: u64 = 1;
|
||||||
const BASE_ONBOARDING_PACK_VERSION: &str = "1.7.0";
|
const BASE_ONBOARDING_PACK_VERSION: &str = "1.8.0";
|
||||||
const TOOLCHAIN_PACK_VERSION: &str = "1.0.0";
|
const TOOLCHAIN_PACK_VERSION: &str = "1.0.0";
|
||||||
|
|
||||||
/// 单个模板的分发结果。
|
/// 单个模板的分发结果。
|
||||||
@ -1947,8 +1947,8 @@ mod tests {
|
|||||||
const TEMPLATE_FINGERPRINTS: [(&str, &str, &str); 3] = [
|
const TEMPLATE_FINGERPRINTS: [(&str, &str, &str); 3] = [
|
||||||
(
|
(
|
||||||
"resources/onboarding/**",
|
"resources/onboarding/**",
|
||||||
"1.7.0",
|
"1.8.0",
|
||||||
"0c39acd7c0513a1063ea0c2de5da06ba2baa69b7eb85b167499859ab6ad0eb45",
|
"fac04a0bd9f3a443349fdd50d6eef22147c56d0843e1769a60b9bbddc372d091",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
"AGENTS.md.tmpl#ONBOARDING_MANAGED",
|
"AGENTS.md.tmpl#ONBOARDING_MANAGED",
|
||||||
|
|||||||
@ -533,6 +533,42 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 服务器说明只有部分客户端带入模型上下文(Codex 不带,见蓝图 agent-client-parity P7),
|
||||||
|
/// 其中三端都须遵守的通用规则必须同时写在接入包 lian-jing skill 里。
|
||||||
|
#[test]
|
||||||
|
fn server_instruction_universal_rules_are_in_skill() {
|
||||||
|
let skill = crate::commands::onboarding::TMPL_LIANJING_SKILL;
|
||||||
|
for phrase in [
|
||||||
|
"其他写操作仅在用户明确确认目标与影响后调用",
|
||||||
|
"unavailable",
|
||||||
|
"unknown",
|
||||||
|
"不得当作成功",
|
||||||
|
] {
|
||||||
|
assert!(
|
||||||
|
MCP_SERVER_INSTRUCTIONS.contains(phrase),
|
||||||
|
"服务器说明缺少「{phrase}」"
|
||||||
|
);
|
||||||
|
assert!(skill.contains(phrase), "lian-jing skill 缺少「{phrase}」");
|
||||||
|
}
|
||||||
|
// 服务器说明的免确认例外是泛称「开发需求元数据」,两个登记工具都属于;
|
||||||
|
// skill 的例外括注须同时列出两者,否则开发变量登记会被误归入「其他写操作」。
|
||||||
|
assert!(MCP_SERVER_INSTRUCTIONS.contains("开发需求元数据"));
|
||||||
|
let exception = skill
|
||||||
|
.lines()
|
||||||
|
.find(|line| line.contains("其他写操作仅在用户明确确认目标与影响后调用"))
|
||||||
|
.expect("lian-jing skill 缺少写操作确认规则");
|
||||||
|
for term in [
|
||||||
|
"开发需求元数据",
|
||||||
|
"register_project_test_account",
|
||||||
|
"register_project_dev_variable",
|
||||||
|
] {
|
||||||
|
assert!(
|
||||||
|
exception.contains(term),
|
||||||
|
"lian-jing skill 免确认例外缺少「{term}」"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn scoped_handshake_binds_project_and_filters_global_tools() {
|
async fn scoped_handshake_binds_project_and_filters_global_tools() {
|
||||||
let init = process_rpc(&rpc("initialize"), Some("p1")).await.unwrap();
|
let init = process_rpc(&rpc("initialize"), Some("p1")).await.unwrap();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user