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
44 changes: 44 additions & 0 deletions docs/architecture/rfcs/monorepo-distribution-split-v0.md
Original file line number Diff line number Diff line change
Expand Up @@ -824,6 +824,50 @@ Approval needs a reviewed decision, not implementation progress or silence.
installation, R1 managed minimal or D1–D7. No preset, frontend operation,
permission or persisted state changes; protected execution keeps its owner.

### 2026-11-07 — Registry inspection selected loading

- **Baseline and boundary:** `ac6296225`. `registry` and `registry-boundary`
use the existing selected CLI route. Their shared registrar and transport
adapter stay in `cli_runtime`; the explicit full CLI reuses them through
`support_control`, retaining its inspection and renderer patch points.
Registry parsing, classification and Git decisions keep their existing owner.
Python adapts the CLI here; no new decision owner, provider or dispatcher
framework is introduced. Existing imports and I3's minimum one-minor-release
compatibility window remain intact.
- **Compatibility evidence:** 197 source CLI and support-ownership cases pass,
including 26 new inspection/result/rejection/patch-point cases and two help
cases. An independent full-CLI loading exclusion fails on the immutable base
and passes on the candidate through the same public entrypoint. Twenty
successful and rejected requests preserve output, status and exception
semantics across source and independently installed monolithic-wheel consoles.
Help and read paths also work with Node absent and exclude the unselected
Chat, Dashboard and benchmark execution modules. Both revisions pass the real
Git registry-boundary smoke in disposable temporary directories; the earlier
run under `.local` failed because that ancestor intentionally selects a private
registry classification. The failure is retained as fixture-placement evidence.
- **Cost qualification:** 30 interleaved base/head pairs for each of four
help/read workloads on both source and wheel yield 480 samples in 416.7s;
study setup/compatibility/profiling takes another 278.1s, excluding earlier
environment/wheel preparation and tests. On macOS arm64/Python 3.13.5 with
identical dependencies, precompiled bytecode and warm filesystem caches,
fresh-console help medians fall from 1.536–1.609s to 0.100–0.103s; registry
reads from 1.584–1.631s to 0.301–0.310s, and boundary reads from 1.542–1.612s
to 0.222–0.225s. Median RSS falls from about 91–92 MiB to 16–30 MiB.
Both help closures fall from 1,158 to 9 LoopX modules; registry/boundary reads
fall to 148/44. The PR records per-workload median/p95, CPU, separate audit
open counts and unchanged output bytes. No Node starts/IPC or Python-audit
socket events are observed; boundary reads keep their three Git queries.
Warm-cache block I/O is zero, while exact bytes read and native activity
outside the audit remain unknown. These results qualify local CLI cost
reductions, not portable D6 limits, full preparation/model costs or adoption.
- **Validation and remaining work:** changed-file lint passes. The CI lint scope
still fails on the baseline-existing F841, whose repair is already proposed
separately; this result is not waived. Source and wheel evidence does not
upgrade an installed host. M1/#8011, full R0/V10, R1's real managed Turn,
context/model composition, split installation, adoption and complete costs
remain open. No D2/D5/D6/D3, default or authority decision changes. The related
bounded refactor shares the existing grammar and inspection adapter; larger
support/control extraction remains outside this slice.
### 2026-11-06 — Selected Turn host loading seam

Following the generic-cli/SQLite candidate in
Expand Down
32 changes: 32 additions & 0 deletions docs/architecture/rfcs/monorepo-distribution-split-v0.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,38 @@ R0 可以先于目录移动交付,但不代表内核合格。R3/R4 可选,
不关闭完整 R0/V10、M1、拆包安装、R1 managed minimal 或 D1–D7。不改变 preset、
前端操作、权限或持久化状态;受保护执行仍由既有 owner 所有。

### 2026-11-07 — Registry 检查的选择性加载

- **基线与边界:** `ac6296225`。`registry` 和 `registry-boundary` 使用现有
selected CLI 路由。共享注册器与传输适配器留在 `cli_runtime`;显式完整 CLI
通过 `support_control` 复用它们,保留原有检查函数与 renderer patch point。
Registry 解析、分类及 Git 判定仍归现有 owner。Python 在此只适配 CLI,不新增
决策 owner、provider 或 dispatcher 框架。既有导入与 I3 至少一个 minor release
的兼容窗口保持不变。
- **兼容证据:** 源码 CLI 与 support ownership 的 197 个用例通过,包含新增的
26 个检查结果、拒绝与 patch-point 用例及两个 help 用例。独立的完整 CLI 加载
排除断言经同一公开入口,在不可变基线上失败、候选版本上通过。20 个成功与
拒绝请求在源码及独立安装的单体 wheel 控制台上保持输出、状态与异常语义一致。
Help 与读取路径在缺少 Node 时仍可用,并排除未选择的 Chat、Dashboard 和
benchmark 执行模块。两版本在一次性临时目录中均通过真实 Git registry-boundary
smoke;先前放在 `.local` 下的运行失败,因为该祖先路径会按既有规则选择私有
registry 分类。这次失败作为 fixture 放置证据保留。
- **成本资格:** 源码与 wheel 各四条 help/读取路径,每条运行 30 对交替 base/head
样本,共 480 个样本,采样耗时 416.7 秒;研究准备、兼容验证及 profiling 另耗时
278.1 秒,不含此前环境、wheel 准备及测试。macOS arm64/Python 3.13.5、相同依赖、
预编译字节码和暖文件缓存下,新进程控制台 help 中位数从 1.536–1.609 秒降至
0.100–0.103 秒;registry 读取从 1.584–1.631 秒降至 0.301–0.310 秒,边界读取从
1.542–1.612 秒降至 0.222–0.225 秒。RSS 中位数从约 91–92 MiB 降至 16–30 MiB。
两条 help 的 LoopX 模块闭包均从 1,158 降至 9;registry/边界读取降至 148/44。
PR 记录逐路径 median/p95、CPU、独立 audit 的文件打开次数及未改变的输出字节。
未观察到 Node 启动/IPC 或 Python audit socket 事件;边界读取保留三次 Git 查询。
暖缓存块 I/O 为零,准确读取字节数及 audit 之外的原生活动仍未知。这些结果只限定
本地 CLI 成本改善,不建立可移植 D6 限值、完整准备/model 成本或采纳结论。
- **验证与剩余工作:** 改动文件 lint 通过。CI lint 范围仍失败于基线已有的 F841,
其修复已另行提案;这里不豁免该结果。源码与 wheel 证据不升级已安装 host。
M1/#8011、完整 R0/V10、R1 的真实受管 Turn、context/model 组合、拆包安装、采纳
与完整成本仍开放。D2/D5/D6/D3、默认行为及权限判定均不改变。相关有界重构
共享既有 grammar 与检查适配器;更大的 support/control 提取不纳入本切片。
### 2026-11-06 — 所选 Turn 宿主的加载接缝

承接 [#8180](https://github.com/loopx-project/loopx/pull/8180) 的
Expand Down
69 changes: 12 additions & 57 deletions loopx/cli_commands/support_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
from collections.abc import Callable
from pathlib import Path

from ..cli_runtime import (
_handle_registry_inspection_command,
_register_registry_inspection_commands,
)
from ..chat_server import (
DEFAULT_CHAT_HOST,
DEFAULT_CHAT_PORT,
Expand Down Expand Up @@ -106,30 +110,7 @@ def register_support_control_commands(
register_update_command(subparsers, add_subcommand_format)
register_dsh_launch_command(subparsers, add_subcommand_format)

subparsers.add_parser(
"registry", help="Inspect registry goals and adapter declarations."
)
registry_boundary_parser = subparsers.add_parser(
"registry-boundary",
help="Classify a registry file as local-only, global-local, public projection, or public fixture.",
)
registry_boundary_parser.add_argument(
"--path",
help="Registry path to classify. Defaults to the active --registry path.",
)
registry_boundary_parser.add_argument(
"--require-not-tracked",
action="store_true",
help=(
"For registries that cannot be pushed, return non-zero if tracked "
"or if Git cannot verify the tracking state."
),
)
registry_boundary_parser.add_argument(
"--require-gitignored",
action="store_true",
help="Return non-zero if the registry should be ignored but is neither ignored nor tracked.",
)
_register_registry_inspection_commands(subparsers)

serve_status_parser = subparsers.add_parser(
"serve-status", help="Serve live status JSON for the local dashboard."
Expand Down Expand Up @@ -300,39 +281,13 @@ def handle_support_control_command(
output_format=output_format,
)

if args.command == "registry":
payload = inspect_registry(registry_path)
print_payload(payload, args.format, render_registry_markdown)
return 0 if payload.get("ok") else 1

if args.command == "registry-boundary":
boundary_path = Path(args.path).expanduser() if args.path else registry_path
payload = inspect_registry_boundary(boundary_path)
git = payload.get("git") if isinstance(payload.get("git"), dict) else {}
if (
args.require_not_tracked
and payload.get("ok")
and not payload.get("github_push_allowed")
and git.get("probe_status") == "git_unavailable"
and (git.get("inside_worktree") or git.get("worktree_discovery_unknown"))
):
payload["ok"] = False
payload["error"] = "Could not determine whether the registry is tracked by Git."
if (
args.require_gitignored
and payload.get("ok")
and payload.get("should_be_gitignored")
):
if (
git.get("inside_worktree")
and not git.get("ignored")
and not git.get("tracked")
):
payload = dict(payload)
payload["ok"] = False
payload.setdefault("risks", []).append("registry_should_be_gitignored")
print_payload(payload, args.format, render_registry_boundary_markdown)
return 0 if payload.get("ok") else 1
if args.command in {"registry", "registry-boundary"}:
return _handle_registry_inspection_command(
args, registry_path=registry_path, print_payload=print_payload,
inspect_registry=inspect_registry, render_registry_markdown=render_registry_markdown,
inspect_registry_boundary=inspect_registry_boundary,
render_registry_boundary_markdown=render_registry_boundary_markdown,
)

if args.command == "serve-status":
try:
Expand Down
96 changes: 93 additions & 3 deletions loopx/cli_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from collections.abc import Callable
from contextlib import contextmanager
from pathlib import Path
from typing import Any

from . import __version__
from .cli_commands.summary_all_registration import GLOBAL_READ_COMMANDS
Expand Down Expand Up @@ -70,7 +71,9 @@
PROJECT_LIFECYCLE_COMMANDS = {
"refresh-state", "checkpoint-context", "read-only-map", "reward", "operator-gate",
}
_SELECTED_COMMANDS = _STATUS_COMMANDS | GLOBAL_READ_COMMANDS | PROJECT_LIFECYCLE_COMMANDS | {
# Existing CLI vocabulary; this group selects loading, not registry authority.
_REGISTRY_INSPECTION_COMMANDS = frozenset({"registry", "registry-boundary"})
_SELECTED_COMMANDS = _STATUS_COMMANDS | GLOBAL_READ_COMMANDS | PROJECT_LIFECYCLE_COMMANDS | _REGISTRY_INSPECTION_COMMANDS | {
"todo", "quota", "task-lease", "change-window", "delegation", "turn", "doctor", "commands",
"authority-archive", "coordination-shadow", "extension", "slash-commands",
"manager-inbox",
Expand Down Expand Up @@ -310,9 +313,84 @@ def cli_invocation_route(argv: list[str]):
yield


def _register_registry_inspection_commands(subparsers: argparse._SubParsersAction) -> None:
"""Share the existing registry grammar with the complete support parser."""
subparsers.add_parser(
"registry", help="Inspect registry goals and adapter declarations."
)
registry_boundary_parser = subparsers.add_parser(
"registry-boundary",
help="Classify a registry file as local-only, global-local, public projection, or public fixture.",
)
registry_boundary_parser.add_argument(
"--path",
help="Registry path to classify. Defaults to the active --registry path.",
)
registry_boundary_parser.add_argument(
"--require-not-tracked",
action="store_true",
help=(
"For registries that cannot be pushed, return non-zero if tracked "
"or if Git cannot verify the tracking state."
),
)
registry_boundary_parser.add_argument(
"--require-gitignored",
action="store_true",
help="Return non-zero if the registry should be ignored but is neither ignored nor tracked.",
)


def _handle_registry_inspection_command(
args: argparse.Namespace,
*,
registry_path: Path,
print_payload: Callable[[dict[str, Any], str, Callable[[dict[str, Any]], str]], None],
inspect_registry: Callable[[Path], dict[str, Any]],
render_registry_markdown: Callable[[dict[str, Any]], str],
inspect_registry_boundary: Callable[[Path], dict[str, Any]],
render_registry_boundary_markdown: Callable[[dict[str, Any]], str],
) -> int:
"""Keep one inspection adapter and the full CLI's existing patch points."""
if args.command == "registry":
payload = inspect_registry(registry_path)
print_payload(payload, args.format, render_registry_markdown)
return 0 if payload.get("ok") else 1

boundary_path = Path(args.path).expanduser() if args.path else registry_path
payload = inspect_registry_boundary(boundary_path)
git = payload.get("git") if isinstance(payload.get("git"), dict) else {}
if (
args.require_not_tracked
and payload.get("ok")
and not payload.get("github_push_allowed")
and git.get("probe_status") == "git_unavailable"
and (git.get("inside_worktree") or git.get("worktree_discovery_unknown"))
):
payload["ok"] = False
payload["error"] = "Could not determine whether the registry is tracked by Git."
if (
args.require_gitignored
and payload.get("ok")
and payload.get("should_be_gitignored")
):
if (
git.get("inside_worktree")
and not git.get("ignored")
and not git.get("tracked")
):
payload = dict(payload)
payload["ok"] = False
payload.setdefault("risks", []).append("registry_should_be_gitignored")
print_payload(payload, args.format, render_registry_boundary_markdown)
return 0 if payload.get("ok") else 1


def _build_selected_parser(command: str) -> LoopXArgumentParser:
parser, subparsers = build_cli_parser(parser_class=_SelectedArgumentParser)
if command in _STATUS_COMMANDS:
if command in _REGISTRY_INSPECTION_COMMANDS:
_register_registry_inspection_commands(subparsers)
elif command in _STATUS_COMMANDS:
from .cli_commands.status_registration import register_status_commands

register_status_commands(subparsers, add_subcommand_format)
Expand Down Expand Up @@ -710,7 +788,19 @@ 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)
if args.command == "capability":
if args.command in _REGISTRY_INSPECTION_COMMANDS:
from .registry import (
inspect_registry, inspect_registry_boundary,
render_registry_markdown, render_registry_boundary_markdown,
)

result = _handle_registry_inspection_command(
args, registry_path=registry_path, print_payload=print_payload,
inspect_registry=inspect_registry, render_registry_markdown=render_registry_markdown,
inspect_registry_boundary=inspect_registry_boundary,
render_registry_boundary_markdown=render_registry_boundary_markdown,
)
elif args.command == "capability":
from .cli_commands.capability import handle_capability_command

# Keep the explicit full CLI's handler and protected operations unchanged.
Expand Down
Loading
Loading