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
56 changes: 55 additions & 1 deletion docs/architecture/rfcs/monorepo-distribution-split-v0.md
Original file line number Diff line number Diff line change
Expand Up @@ -824,6 +824,61 @@ 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 — Heartbeat pre-quota selected loading

- **Baseline and owner:** `79665b97a`. The heartbeat builder already emits
`heartbeat-prequota` when external evidence polling is available. Its selected
parser reuses the existing heartbeat registrar; dispatch uses the existing
heartbeat transport module. The complete facade passes its original runner,
renderer and global-registry resolver patch points. Explicit/implicit routing,
aliases, format, timeout, exception, degraded-return and controller semantics
remain characterized; reconciliation/hook ordering and typed owners are unchanged.
- **Source and package evidence:** 50 new transport/loading/real-backend cases;
22 cases on each of four independently verified source/wheel consoles; 16
configured/degraded File/SQLite console scenarios with replay, retained opaque
work and zero quota debit. Dropping `continue_to_quota` is rejected by the same
public-console oracle on both real stores. The existing acknowledgement smoke
now exercises both CLI facades. The full source selection initially has 378
passes and two route-test failures reproduced on the immutable baseline. The
fixture now reads the original digest-bound progressive capture without
re-admission; its complete affected file then passes all 17 cases. No failure
was waived. Focused lint, configured mypy and the code canary pass.
- **Matched local cost:** macOS 27.0 arm64, Python 3.13.5, Node 26.0.0; identical
dependencies and synthetic stores, precompiled bytecode, fresh Python processes,
warm filesystem caches and reused isolated Node services. Thirty alternating
base/head pairs for each workload/distribution yield 360 samples in 339 seconds;
study setup/validation takes 260 seconds separately. This shared development
host was not an idle-machine experiment. Values below are wall median/p95 in
seconds and median process RSS in MiB, including interpreter startup.

| Workload / distribution | Base median / p95 | Candidate median / p95 | Base → candidate RSS |
| --- | --- | --- | --- |
| `source/prequota-help` | 1.433 / 1.889 | 0.172 / 0.226 | 91.5 → 24.7 |
| `source/prequota-file-off` | 1.494 / 1.927 | 0.545 / 0.667 | 92.0 → 47.0 |
| `source/prequota-sqlite-off` | 1.547 / 1.977 | 0.507 / 0.640 | 91.9 → 46.3 |
| `wheel/prequota-help` | 1.555 / 1.967 | 0.170 / 0.210 | 91.7 → 24.5 |
| `wheel/prequota-file-off` | 1.545 / 2.155 | 0.536 / 0.650 | 91.7 → 46.7 |
| `wheel/prequota-sqlite-off` | 1.397 / 1.866 | 0.531 / 0.667 | 92.0 → 46.9 |

- **Closure and complete-cost limits:** help loads 29 LoopX modules instead of
1,158; default-off execution loads 277 instead of 1,160. Python open observations
fall from 1,292–1,312 to 81–395. Opened-file size sums are footprints, not actual
read bytes. Median user/system process CPU is 0.04/0.02 seconds for candidate
help and 0.13–0.14/0.065–0.08 for execution, versus 0.40–0.42/0.16–0.185 at base.
Output remains 737 bytes for help and 827 for JSON execution. A separate SQLite
loopback profile observes 1,362 sent and 668 received bytes on each revision,
two Node starts for version/server discovery with a cold service and none with
a reused service. Its single instrumented cold/warm observations do not isolate
Node readiness or service CPU; those costs remain unqualified. The initial IPC
probe assumed Unix sockets and failed; loopback instrumentation corrected the
probe without changing product behavior. Help p95 of 0.226/0.210 seconds exceeds
the proposed 0.200-second target; that D6 proposal is not promoted or relabeled.
- **Remaining:** this is one R0/V1/V3/V10 caller proposal, preserving I3 for at
least one minor release. Full R0/V10, M1/#8011, R1, context/model and split-install
qualification, installed-host recovery, adoption and complete task costs remain
open. The bounded Python change adapts transport; it adds no decision owner,
preset, provider or authority rule. Required final-head review/CI remains a
delivery gate distinct from these local observations.
### 2026-11-07 — Registry inspection selected loading

- **Baseline and boundary:** `ac6296225`. `registry` and `registry-boundary`
Expand Down Expand Up @@ -1128,7 +1183,6 @@ comparison before adopting a performance change. This checkpoint creates no
new owner, budget, preset or default. R0's complete census, R1/minimal,
D2/D5/D6, I3/D3, hard-lease/context/model and split-install acceptance remain
open; independent exact-head review and required validation govern adoption.

## Appendix B: Decision log

| Date | Decision | Owner / approval | Alternatives | Normative sections changed |
Expand Down
44 changes: 43 additions & 1 deletion 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,49 @@ R0 可以先于目录移动交付,但不代表内核合格。R3/R4 可选,
不关闭完整 R0/V10、M1、拆包安装、R1 managed minimal 或 D1–D7。不改变 preset、
前端操作、权限或持久化状态;受保护执行仍由既有 owner 所有。

### 2026-11-07 — Heartbeat pre-quota 按需加载

- **基线与 owner:**`79665b97a`。heartbeat builder 已在 external evidence polling
可用时生成 `heartbeat-prequota`。选择性 parser 复用原 heartbeat registrar,dispatch
放在现有 heartbeat transport 模块;完整 facade 显式传入原 runner、renderer 和全局
registry resolver patch points。显式/隐式路由、别名、格式、timeout、异常、降级返回和
controller 语义均已刻画;reconciliation/hook 顺序及 typed owners 保持原路径。
- **源码与包证据:**新增 50 个 transport/加载/真实后端用例;四个独立核对的源码/
wheel console 各运行 22 个场景;16 个配置/降级 File/SQLite console 场景覆盖重放、
opaque 原工作保留和零 quota 扣费。删除 `continue_to_quota` 的 mutation 经同一公开
console oracle 在两种真实 store 上被拒绝。原 acknowledgement smoke 同时覆盖两个
CLI facade。完整源码选择初轮得到 378 pass、两个路由测试失败,精确基线同样失败。
fixture 改为读取原 digest-bound progressive capture,不重新准入;受影响文件随后
17 个用例全部通过。失败未豁免。聚焦 lint、配置内 mypy 和代码 canary 通过。
- **配对本地成本:**macOS 27.0 arm64、Python 3.13.5、Node 26.0.0;相同依赖和合成
store,预编译 bytecode、全新 Python 进程、热文件缓存及复用的隔离 Node 服务。
每个 workload/distribution 30 个交错 base/head 配对,共 360 个样本,采样 339 秒;
study 准备/验证另计 260 秒。测量使用共享开发机,并非空闲机器实验。下表是含解释器
启动的墙钟 median/p95(秒)和进程 RSS 中位数(MiB)。

| Workload / distribution | 基线 median / p95 | 候选 median / p95 | 基线 → 候选 RSS |
| --- | --- | --- | --- |
| `source/prequota-help` | 1.433 / 1.889 | 0.172 / 0.226 | 91.5 → 24.7 |
| `source/prequota-file-off` | 1.494 / 1.927 | 0.545 / 0.667 | 92.0 → 47.0 |
| `source/prequota-sqlite-off` | 1.547 / 1.977 | 0.507 / 0.640 | 91.9 → 46.3 |
| `wheel/prequota-help` | 1.555 / 1.967 | 0.170 / 0.210 | 91.7 → 24.5 |
| `wheel/prequota-file-off` | 1.545 / 2.155 | 0.536 / 0.650 | 91.7 → 46.7 |
| `wheel/prequota-sqlite-off` | 1.397 / 1.866 | 0.531 / 0.667 | 92.0 → 46.9 |

- **加载边界与完整成本限制:**help 的 LoopX 模块为 29,基线 1,158;默认关闭的执行
为 277,基线 1,160。Python open 观察由 1,292–1,312 降至 81–395;打开文件尺寸之和
是 footprint,不是实际读取字节。候选 help 的 user/system 进程 CPU 中位数为
0.04/0.02 秒,执行为 0.13–0.14/0.065–0.08 秒;基线为
0.40–0.42/0.16–0.185 秒。help 输出仍为 737 字节,JSON 执行仍为 827 字节。
独立 SQLite loopback profile 在两版均观察到发送 1,362、接收 668 字节;冷服务启动
Node version/server 两个进程,复用服务启动数为零。单次带 instrumentation 的冷/
热观察不能拆出 Node readiness 或 service CPU,相关成本未通过资格。初轮 IPC probe
误以为使用 Unix socket 而失败,改为 loopback instrumentation 后通过,未修改产品。
help p95 为 0.226/0.210 秒,超过提议的 0.200 秒目标;未晋升或重标该 D6 提议。
- **剩余:**这是一条 R0/V1/V3/V10 caller 的作者提案,I3 至少保留一个 minor release。
完整 R0/V10、M1/#8011、R1、context/model、split-install、installed-host 恢复、
采纳和完整任务成本仍开放。Python 仅适配 transport,没有新增 decision owner、preset、
provider 或权限规则。最终 exact-head review/CI 仍是独立交付门,与这些本地观察分开。
### 2026-11-07 — Registry 检查的选择性加载

- **基线与边界:** `ac6296225`。`registry` 和 `registry-boundary` 使用现有
Expand Down Expand Up @@ -839,7 +882,6 @@ owner;采纳性能变更前必须另做匹配的真实路径对照。本 check
预算、preset 或默认行为。R0 完整清点、R1/minimal、D2/D5/D6、I3/D3、
hard-lease/context/model 和拆包安装验收仍开放;采纳仍受独立 exact-head
评审及必需验证约束。

## 附录 B:决策日志

| 日期 | 决策 | 负责人 / 批准 | 备选 | 变更的规范章节 |
Expand Down
49 changes: 25 additions & 24 deletions examples/issue-fix-pr-review-reconcile-smoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,30 +307,31 @@ def check_reconciliation(provider: str) -> None:
cli_env = dict(os.environ)
cli_env["HOME"] = str(home)
cli_env["PYTHONPATH"] = str(ROOT)
cli = subprocess.run(
[
sys.executable,
"-m",
"loopx.cli",
"--format",
"json",
"heartbeat-prequota",
"-g",
"example-goal",
"-a",
"codex-test",
],
cwd=project,
env=cli_env,
check=False,
capture_output=True,
text=True,
)
assert cli.returncode == 0, cli.stderr
cli_payload = json.loads(cli.stdout)
assert cli_payload["schema_version"] == "heartbeat_pre_quota_v0", cli_payload
assert cli_payload["continue_to_quota"] is True, cli_payload
assert cli_payload["quota_spend_required"] is False, cli_payload
for module in ("loopx.entrypoint", "loopx.cli"):
cli = subprocess.run(
[
sys.executable,
"-m",
module,
"--format",
"json",
"heartbeat-prequota",
"-g",
"example-goal",
"-a",
"codex-test",
],
cwd=project,
env=cli_env,
check=False,
capture_output=True,
text=True,
)
assert cli.returncode == 0, cli.stderr
cli_payload = json.loads(cli.stdout)
assert cli_payload["schema_version"] == "heartbeat_pre_quota_v0", cli_payload
assert cli_payload["continue_to_quota"] is True, cli_payload
assert cli_payload["quota_spend_required"] is False, cli_payload

assert todo(state_file, "todo_other_provider")["status"] == "open"
native = list_goal_todos(registry_path=registry_path, goal_id="example-goal")
Expand Down
26 changes: 9 additions & 17 deletions loopx/cli_commands/support_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,24 +234,16 @@ def handle_support_control_command(
)

if args.command == "heartbeat-prequota":
prequota_registry = (
registry_path
if registry_was_supplied
else explicit_global_registry(args.runtime_root)
)
payload = run_heartbeat_pre_quota(
registry_path=prequota_registry,
runtime_root_arg=args.runtime_root,
goal_id=args.goal_id,
agent_id=args.agent_id,
fetch_timeout_seconds=args.fetch_timeout_seconds,
)
print_payload(
payload,
output_format(args),
render_heartbeat_pre_quota_markdown,
from .support_control_heartbeat import handle_heartbeat_prequota_command

return handle_heartbeat_prequota_command(
args, registry_path=registry_path,
registry_was_supplied=registry_was_supplied,
print_payload=print_payload, output_format=output_format,
runner=run_heartbeat_pre_quota,
markdown_renderer=render_heartbeat_pre_quota_markdown,
global_registry_resolver=explicit_global_registry,
)
return 0

supervisor_result = handle_supervisor_control_command(
args,
Expand Down
43 changes: 43 additions & 0 deletions loopx/cli_commands/support_control_heartbeat.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,49 @@
FormatSelector = Callable[..., str]


def handle_heartbeat_prequota_command(
args: argparse.Namespace,
*,
registry_path: Path,
registry_was_supplied: bool,
print_payload: PrintPayload,
output_format: FormatSelector,
runner: Callable[..., dict[str, object]] | None = None,
markdown_renderer: Callable[[dict[str, object]], str] | None = None,
global_registry_resolver: Callable[[str | None], Path] | None = None,
) -> int:
# The complete facade passes its released patch points. The selected
# entry loads these same owners only when this command executes.
if runner is None:
from ..heartbeat_prequota import run_heartbeat_pre_quota

runner = run_heartbeat_pre_quota
if markdown_renderer is None:
from ..heartbeat_prequota import render_heartbeat_pre_quota_markdown

markdown_renderer = render_heartbeat_pre_quota_markdown
if global_registry_resolver is None:
from .support_control_registry import explicit_global_registry

global_registry_resolver = explicit_global_registry
prequota_registry = (
registry_path
if registry_was_supplied
else global_registry_resolver(args.runtime_root)
)
payload = runner(
registry_path=prequota_registry,
runtime_root_arg=args.runtime_root,
goal_id=args.goal_id,
agent_id=args.agent_id,
fetch_timeout_seconds=args.fetch_timeout_seconds,
)
print_payload(payload, output_format(args), markdown_renderer)
# Reconciliation is best effort: a returned degraded payload still
# continues to quota. Exceptions retain the complete facade's path.
return 0


def handle_heartbeat_prompt_command(
args: argparse.Namespace,
*,
Expand Down
11 changes: 10 additions & 1 deletion loopx/cli_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
"handoff", "handoff-mode",
"performance-diagnosis",
"heartbeat-prompt",
"heartbeat-prequota",
"goal-acceptance",
"host-mode-plan",
"capability",
Expand Down Expand Up @@ -478,7 +479,7 @@ def _build_selected_parser(command: str) -> LoopXArgumentParser:
from .capabilities.performance_diagnosis.cli import register_performance_diagnosis_commands

register_performance_diagnosis_commands(subparsers, add_subcommand_format)
elif command == "heartbeat-prompt":
elif command in {"heartbeat-prompt", "heartbeat-prequota"}:
from .cli_commands.support_control_heartbeat_registration import register_heartbeat_control_commands

register_heartbeat_control_commands(subparsers, add_subcommand_format)
Expand Down Expand Up @@ -808,6 +809,14 @@ def _dispatch_selected(args: argparse.Namespace, raw_argv: list[str]) -> int:
args, registry_path=registry_path, runtime_root_arg=args.runtime_root,
output_format=output_format, print_payload=print_payload,
)
elif args.command == "heartbeat-prequota":
from .cli_commands.support_control_heartbeat import handle_heartbeat_prequota_command

result = handle_heartbeat_prequota_command(
args, registry_path=registry_path,
registry_was_supplied=user_supplied_registry(raw_argv),
output_format=output_format, print_payload=print_payload,
)
else:
result = dispatch_common_command(
args,
Expand Down
16 changes: 15 additions & 1 deletion tests/control_plane/test_heartbeat_registry_route.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,21 @@ def _execute(argv: list[str], cwd: Path):
result = subprocess.run([sys.executable, "-m", "loopx.entrypoint", "--format", "json", *argv],
cwd=cwd, env=env, text=True, capture_output=True, check=False)
assert result.stdout, result.stderr
return result.returncode, json.loads(result.stdout)
payload = json.loads(result.stdout)
# Progressive presentation retains the original decision behind a private,
# digest-bound capture. Read it without repeating admission or changing the
# generated command whose authority route this journey verifies.
from loopx.control_plane.turn_driver.decision_capture import (
read_context_capture_file, read_context_read_reference,
)
if payload.get("schema_version") == "context_capture_navigation_v1":
_, reference = read_context_read_reference(payload["recovery"]["reference_file"])
assert "selection" not in reference
payload = read_context_capture_file(reference["path"], reference["sha256"], private=True)[2]
elif isinstance(payload.get("captured_decision"), dict):
reference = payload["captured_decision"]
payload = read_context_capture_file(reference["path"], reference["sha256"], private=True)[2]
return result.returncode, payload


@pytest.mark.parametrize("provider", ["file", "sqlite"])
Expand Down
Loading
Loading