From 58442be4dc9c6c5217c80c2a4b34b63eaee069a1 Mon Sep 17 00:00:00 2001 From: "lusendong.6789" Date: Wed, 4 Nov 2026 13:23:51 +0800 Subject: [PATCH] perf(cli): load capability discovery through its existing owner Signed-off-by: lusendong.6789 Co-authored-by: TRAE CLI --- .../rfcs/monorepo-distribution-split-v0.md | 34 +++++ .../monorepo-distribution-split-v0.zh-CN.md | 26 ++++ loopx/cli_runtime.py | 34 ++++- tests/test_cli_entrypoint.py | 136 ++++++++++++++++++ 4 files changed, 225 insertions(+), 5 deletions(-) diff --git a/docs/architecture/rfcs/monorepo-distribution-split-v0.md b/docs/architecture/rfcs/monorepo-distribution-split-v0.md index 2aad7cff1b..df8c5cf6bf 100644 --- a/docs/architecture/rfcs/monorepo-distribution-split-v0.md +++ b/docs/architecture/rfcs/monorepo-distribution-split-v0.md @@ -786,6 +786,40 @@ Approval needs a reviewed decision, not implementation progress or silence. exact expiry remains a release-owner decision. M1 is open. No module-count, loading-performance, split-package or R1 managed-Turn benefit is claimed. +### 2026-11-04 — Capability discovery selected loading + +- **Baseline:** `79b781865`. `capability list` / `show` and command help now + reuse the existing capability registrar and catalog handler without loading + the full CLI or Chat. `inspect`, `bind` and `invoke` execution, the explicit + full-CLI handler patch point, registry routing and canonical rejected-request + diagnostics retain their existing paths. The local discovery set reuses CLI + vocabulary; it creates no capability, provider, protocol or authority owner. +- **Evidence:** `tests/test_cli_entrypoint.py` covers output/help parity, + declared-but-absent and real installed-disabled providers, hidden helpers, + missing/invalid manifests and no process/network/write activation. The five + loading oracles fail on the baseline and pass on the candidate. All 23 focused + cases also pass against an independently installed wheel outside the checkout + with Node absent; this does not upgrade an existing host. Eight additional + immutable base/candidate rejection pairs preserve output and exit status. +- **Measured closure:** three fresh-process help/list/show workloads use 20 + samples per revision, one discarded warm-up and alternating order on macOS + arm64/Python 3.13.5. Both source snapshots have warm filesystem caches and no + bytecode; Node and telemetry are absent. The real `main()` path loads 362 + LoopX modules instead of 1,149, with 832 read-open observations instead of + 2,494. Output bytes are identical; observed writes, process starts, network + connections and thread starts are zero. Timing and RSS evidence includes + equal instrumentation and interpreter startup; it is scoped PR evidence, + not a portable D6 threshold or installed-host/task-benefit claim. +- **Validation holds:** the broader source selection has 320 passing cases and + one failing `test_settlement_plan_import_does_not_load_native_capture_io`. + The immutable baseline fails identically. Configured mypy likewise reports + the same 789 errors in 126 files on both revisions. Neither failure is waived + or relabeled as a pass; focused parity does not certify all required checks. +- **Remaining:** this extends R0-style discovery qualification, independently + of the public-status proposal #8011. It does not close full R0/V10, M1, split + installation, R1 managed minimal or D1–D7. No preset, frontend operation, + permission or persisted state changes; protected execution keeps its owner. + ## Appendix B: Decision log | Date | Decision | Owner / approval | Alternatives | Normative sections changed | diff --git a/docs/architecture/rfcs/monorepo-distribution-split-v0.zh-CN.md b/docs/architecture/rfcs/monorepo-distribution-split-v0.zh-CN.md index a4793dc90f..a4bfc31e48 100644 --- a/docs/architecture/rfcs/monorepo-distribution-split-v0.zh-CN.md +++ b/docs/architecture/rfcs/monorepo-distribution-split-v0.zh-CN.md @@ -570,6 +570,32 @@ R0 可以先于目录移动交付,但不代表内核合格。R3/R4 可选, 版本,D3 精确退役时间仍由发布负责人决定。M1 未关闭,不声明模块数量、加载性能、 拆包或 R1 managed Turn 收益。 +### 2026-11-04 — Capability 发现路径的所选加载 + +- **基线:** `79b781865`。`capability list` / `show` 及命令帮助复用既有 + capability registrar 与 catalog handler,不再加载完整 CLI 或 Chat。 + `inspect`、`bind`、`invoke` 的执行、显式完整 CLI 的 handler patch 点、registry + 路由及非法请求的规范诊断仍保留原路径。本地发现集合复用 CLI 词汇,不新增 + capability、provider、协议或权限 owner。 +- **证据:** `tests/test_cli_entrypoint.py` 覆盖输出/帮助一致性、已声明但未安装 + 及真实已安装且禁用的 provider、隐藏 helper、缺失/非法 manifest,以及不启动 + 进程、不联网、不写文件。五个加载 oracle 在基线失败、候选通过;全部 23 个聚焦 + 用例也在 checkout 外独立安装的 wheel 上、无 Node 条件下通过,不升级既有宿主。 + 另有八组不可变基线/候选拒绝对照保留输出和退出码。 +- **加载闭包测量:** macOS arm64/Python 3.13.5 上,三个新进程 help/list/show + 负载各自每版本采样 20 次,丢弃一次 warm-up 并交替顺序。两份源码快照均为暖文件 + 缓存、无 bytecode,Node 与遥测关闭。真实 `main()` 路径加载的 LoopX 模块由 + 1,149 降到 362,read-open 观察由 2,494 降到 832。输出字节完全一致;观察到的 + 文件写入、进程启动、网络连接和线程启动均为零。耗时与 RSS 包含相同插桩及解释器 + 启动,是 PR 中的限定证据,不是跨机器 D6 阈值,也不声明已安装宿主或任务收益。 +- **验证保留项:** 更广的源码选择为 320 个通过, + `test_settlement_plan_import_does_not_load_native_capture_io` 一个失败;不可变 + 基线有完全相同的失败。配置的 mypy 在两个版本上同样报告 126 个文件中的 789 个 + 错误。两项失败均不豁免、不改写为通过;聚焦一致性不证明所有必需检查通过。 +- **剩余:** 本片独立于公开 status 提案 #8011,扩展 R0 类发现路径的资格证据, + 不关闭完整 R0/V10、M1、拆包安装、R1 managed minimal 或 D1–D7。不改变 preset、 + 前端操作、权限或持久化状态;受保护执行仍由既有 owner 所有。 + ## 附录 B:决策日志 | 日期 | 决策 | 负责人 / 批准 | 备选 | 变更的规范章节 | diff --git a/loopx/cli_runtime.py b/loopx/cli_runtime.py index d2ed3aabab..e5a5514495 100644 --- a/loopx/cli_runtime.py +++ b/loopx/cli_runtime.py @@ -80,7 +80,10 @@ "heartbeat-prompt", "goal-acceptance", "host-mode-plan", + "capability", } +# Existing CLI vocabulary; this set chooses loading, not operation authority. +_CAPABILITY_DISCOVERY_COMMANDS = frozenset({"list", "show"}) class LoopXArgumentParser(argparse.ArgumentParser): @@ -409,6 +412,10 @@ def _build_selected_parser(command: str) -> LoopXArgumentParser: from .cli_commands.host_mode_plan import register_host_mode_plan_command register_host_mode_plan_command(subparsers, add_subcommand_format) + elif command == "capability": + from .cli_commands.capability import register_capability_commands + + register_capability_commands(subparsers, add_subcommand_format) else: # pragma: no cover - caller guards the private interface raise ValueError(f"unsupported selected command: {command}") return parser @@ -703,11 +710,20 @@ def _dispatch_selected(args: argparse.Namespace, raw_argv: list[str]) -> int: if guard_result is not None: return guard_result registry_path, _registry_was_configured = resolve_cli_registry(args, raw_argv) - result = dispatch_common_command( - args, - registry_path=registry_path, - allow_missing_registry=not user_supplied_registry(raw_argv), - ) + if args.command == "capability": + from .cli_commands.capability import handle_capability_command + + # Keep the explicit full CLI's handler and protected operations unchanged. + result = handle_capability_command( + args, registry_path=registry_path, runtime_root_arg=args.runtime_root, + output_format=output_format, print_payload=print_payload, + ) + else: + result = dispatch_common_command( + args, + registry_path=registry_path, + allow_missing_registry=not user_supplied_registry(raw_argv), + ) if result is None: # pragma: no cover - caller guards the private interface raise AssertionError(f"selected command was not dispatched: {args.command}") return result @@ -755,6 +771,14 @@ def _run_command(command: str | None, raw_argv: list[str]) -> int: args = _build_selected_parser(command).parse_args(raw_argv) except _SelectedParseRejected: return _run_full_cli(raw_argv) + if command == "capability": + if args.capability_command not in _CAPABILITY_DISCOVERY_COMMANDS: + return _run_full_cli(raw_argv) + try: + return _dispatch_selected(args, raw_argv) + except _SelectedParseRejected: + # Catalog/manifest refusals use their canonical argparse diagnostic. + return _run_full_cli(raw_argv) return _dispatch_selected(args, raw_argv) return _run_full_cli(raw_argv) diff --git a/tests/test_cli_entrypoint.py b/tests/test_cli_entrypoint.py index 821368a360..01bb65aeca 100644 --- a/tests/test_cli_entrypoint.py +++ b/tests/test_cli_entrypoint.py @@ -918,6 +918,142 @@ def reject_activation(event, args): assert not list(tmp_path.iterdir()) +@pytest.mark.parametrize("arguments", [ + ["list"], ["show", "performance-diagnosis"], + ["--help"], ["list", "--help"], ["show", "--help"], +]) +def test_capability_discovery_skips_unselected_layers(tmp_path, arguments): + result = run_cli_main("loopx.entrypoint", + ["--registry", str(tmp_path / "absent.json"), "--runtime-root", str(tmp_path / "runtime"), + "capability", *arguments], + forbidden_modules=("loopx.cli", "loopx.chat_server", "loopx.chat_runtime", + "loopx.cli_commands.benchmark_dispatch", "loopx.capabilities.content_ops.cli"), + environment={"LOOPX_USAGE_PING": "0"}) + assert result.returncode == 0, result.stderr + assert not list(tmp_path.iterdir()) + + +@pytest.mark.parametrize("format_name", ["json", "markdown"]) +@pytest.mark.parametrize("extension_mode", ["absent", "declared", "disabled"]) +@pytest.mark.parametrize("command", ["list", "show"]) +def test_capability_discovery_preserves_catalog_and_default_off( + tmp_path, format_name, extension_mode, command, +): + from tests.capabilities.test_capability_extension_registry import _write_manifest + from loopx.extensions.runtime import default_extension_state_file, disable_extension, install_extension + + registry, runtime = write_command_fixture(tmp_path) + declared_extension = extension_mode != "absent" + arguments = [command] + if command == "show": + arguments.append("sample-report" if declared_extension else "performance-diagnosis") + if declared_extension: + provider = None + if extension_mode == "disabled": + provider = tmp_path / "provider" + provider.write_text(f"#!{sys.executable}\nraise SystemExit(0)\n", encoding="utf-8") + provider.chmod(0o755) + manifest = _write_manifest(tmp_path / "extension.toml", entrypoint=provider) + arguments += ["--extension-manifest", str(manifest)] + if extension_mode == "disabled": + state_file = default_extension_state_file(runtime) + install_extension(manifest, state_file=state_file, execute=True) + disable_extension("sample-extension", state_file=state_file, execute=True) + argv = ["--registry", str(registry), "--runtime-root", str(runtime), + "--format", "markdown", "capability", *arguments, "--format", format_name] + before = {str(path): path.read_bytes() for path in tmp_path.rglob("*") if path.is_file()} + selected = run_cli_main("loopx.entrypoint", argv, environment={"LOOPX_USAGE_PING": "0"}) + full = run_cli_main("loopx.cli", argv, environment={"LOOPX_USAGE_PING": "0"}) + assert selected.returncode == full.returncode == 0, (selected.stderr, full.stderr) + assert selected.stdout == full.stdout + assert selected.stderr == full.stderr == "" + assert {str(path): path.read_bytes() for path in tmp_path.rglob("*") if path.is_file()} == before + assert runtime.exists() == (extension_mode == "disabled") + if format_name == "json": + payload = json.loads(selected.stdout) + assert payload["schema_version"] == ( + "loopx_capability_catalog_v0" if command == "list" else "loopx_capability_detail_v0") + entries = payload["capabilities"] if command == "list" else [payload["capability"]] + assert any(item["id"] == ("sample-report" if declared_extension else "performance-diagnosis") + for item in entries) + assert all(item["id"] != "sample-internal-helper" for item in entries) + if declared_extension: + entry = next(item for item in entries if item["id"] == "sample-report") + assert entry["origin"] == "extension" + assert entry["provider_state"] == { + "declared": True, "installed": extension_mode == "disabled", "enabled": False, "ready": False} + + +def test_capability_discovery_rejections_keep_canonical_diagnostics(tmp_path): + manifest = tmp_path / "invalid.toml" + manifest.write_text("not valid TOML [", encoding="utf-8") + prefix = ["--registry", str(tmp_path / "absent.json"), + "--runtime-root", str(tmp_path / "runtime"), "capability"] + cases = [["list", "--unknown"], ["show"], ["show", "absent-capability"], + ["list", "--format", "yaml"], ["list", "--extension-manifest"], + ["list", "--extension-manifest", str(tmp_path / "absent.toml")], + ["list", "--extension-manifest", str(manifest)], ["list", "--ext"]] + assert run_cli_batch("loopx.entrypoint", [prefix + case for case in cases]) == ( + run_cli_batch("loopx.cli", [prefix + case for case in cases])) + assert not (tmp_path / "runtime").exists() + + +@pytest.mark.parametrize("command", ["inspect", "bind", "invoke"]) +def test_capability_non_discovery_retains_full_cli_patch_point(tmp_path, command): + arguments = ["inspect", "--goal-id", "fixture-goal"] if command == "inspect" else ( + [command, "sample-report", "--goal-id", "fixture-goal", "--operation", "read", "--execute"]) + if command == "invoke": + arguments += ["--input-json", str(tmp_path / "input.json")] + script = f""" +import os +os.environ["LOOPX_USAGE_PING"] = "0" +import loopx.cli as full +from loopx.entrypoint import main +seen = [] +def original_path(args, **kwargs): + seen.append((args.capability_command, args.goal_id, getattr(args, "execute", False))) + return 73 +full.handle_capability_command = original_path +assert main(["--registry", {str(tmp_path / "absent.json")!r}, "--runtime-root", {str(tmp_path / "runtime")!r}, + "capability", *{arguments!r}]) == 73 +assert seen == [({command!r}, "fixture-goal", {command != 'inspect'!r})] +""" + result = run_isolated_script(script) + assert result.returncode == 0, result.stderr + assert not list(tmp_path.iterdir()) + + +@pytest.mark.parametrize("arguments", [["list"], ["show", "performance-diagnosis"]]) +def test_capability_discovery_does_not_activate_providers(tmp_path, arguments): + from tests.capabilities.test_capability_extension_registry import _write_manifest + + provider = tmp_path / "provider.py" + provider.write_text("raise AssertionError('discovery cannot run a provider')\n", encoding="utf-8") + manifest = _write_manifest(tmp_path / "extension.toml", entrypoint=provider) + script = f""" +import os +import sys +os.environ["LOOPX_USAGE_PING"] = "0" +sys.dont_write_bytecode = True +os.chdir({str(tmp_path)!r}) +def reject_activation(event, args): + assert event not in ("subprocess.Popen", "os.system", "os.posix_spawn", "socket.connect"), event + if event == "open": + mode, flags = args[1:3] + assert not (isinstance(mode, str) and any(c in mode for c in "wax+")), args + assert not (isinstance(flags, int) and flags & (os.O_WRONLY | os.O_RDWR | os.O_CREAT)), args +sys.addaudithook(reject_activation) +from loopx.entrypoint import main +assert main(["--registry", {str(tmp_path / "absent.json")!r}, "--runtime-root", {str(tmp_path / "runtime")!r}, + "--format", "json", "capability", *{arguments!r}, "--extension-manifest", {str(manifest)!r}]) == 0 +""" + before = {str(path): path.read_bytes() for path in tmp_path.rglob("*") if path.is_file()} + result = run_isolated_script(script, environment={"PATH": str(tmp_path / "no-node")}) + assert result.returncode == 0, result.stderr + assert {str(path): path.read_bytes() for path in tmp_path.rglob("*") if path.is_file()} == before + assert not (tmp_path / "runtime").exists() + + @pytest.mark.parametrize("provider", ["file", "sqlite"]) def test_selected_refresh_preview_keeps_canonical_state_without_full_cli(tmp_path, monkeypatch, provider): from tests.control_plane.canonical_authority_fixture import initialize_canonical_source_fixture, isolate_sqlite_runtime