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
2 changes: 1 addition & 1 deletion docs/book/chapters/00-reading-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ lifecycle 选择,不是所有贡献的默认终点。

## 版本基线

当前内容以 LoopX GitHub release `v1.0.2` 为发布锚点;本地命令示例已按 `loopx 1.0.2` 的
当前内容以 LoopX GitHub release `v1.0.3` 为发布锚点;本地命令示例已按 `loopx 1.0.3` 的
公开 CLI 与协议表面复核。该版本要求 Python 3.11+ 与 Node.js 22.6+;后者运行由 LoopX 自动管理、
空闲后退出的 TypeScript Effect runtime,用户不需要手工维护 daemon。

Expand Down
4 changes: 2 additions & 2 deletions docs/book/en/chapters/00-reading-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,8 @@ Do not bypass a newer permission or lifecycle check just to make an older exampl

## Version baseline

The current release anchor is LoopX GitHub release `v1.0.2`. Local command examples were checked against
the public `loopx 1.0.2` CLI and protocol surface. This release requires Python 3.11+ and Node.js 22.6+.
The current release anchor is LoopX GitHub release `v1.0.3`. Local command examples were checked against
the public `loopx 1.0.3` CLI and protocol surface. This release requires Python 3.11+ and Node.js 22.6+.
LoopX starts and reuses its managed, idle-exiting TypeScript Effect runtime automatically; users do not
operate that runtime as a manual daemon.

Expand Down
2 changes: 1 addition & 1 deletion docs/book/en/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ versioned or optional functionality, not the default shape of every contribution
- Source format: Markdown
- Site generator: MkDocs Material
- Hosting: GitHub Pages
- LoopX release anchor: `v1.0.2`
- LoopX release anchor: `v1.0.3`
- Runtime prerequisites: Python 3.11+ and Node.js 22.6+

The official public protocols remain authoritative for protocol facts. Commands that change across
Expand Down
2 changes: 1 addition & 1 deletion docs/book/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ Extension 只是可独立版本化和交付的一种路径。
- 正文格式:Markdown;
- 站点生成器:MkDocs Material;
- 在线发布:GitHub Pages;
- LoopX 发布锚点:`v1.0.2`;
- LoopX 发布锚点:`v1.0.3`;
- 运行时前提:Python 3.11+ 与 Node.js 22.6+。

协议解释以 LoopX 官方公开合同为事实源。易变化的命令仍以对应发布物、官方文档和当前
Expand Down
8 changes: 8 additions & 0 deletions docs/product/release-readiness.md
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,14 @@ path, and canary route rather than as a user-facing release baseline.
and frozen bundles install the same version-bound workflow skills as package
distributions. The exact-tag Python, PyPI, macOS, Windows, signed-update,
public-smoke, and live-model gates passed before `stable` fast-forwarded.
- `v1.0.2` on 2026-09-09 11:22 +08:00: single-owner Todo authority and
recovery release at the matching `v1.0.2` tag (`a5374d5b`). Promoted Todo
terminal transitions commit through one TypeScript-owned provider
transaction; missing generated Todo projections recover without making
Markdown authoritative; and long-history, Desktop, DSH, and managed-skill
paths gain bounded reads and clearer recovery diagnostics. The published
wheel, source distribution, macOS, Windows, checksum, update, and PyPI
artifacts were verified against the exact release source before promotion.

When a new public release is promoted, add it here only after the matching tag,
release note, stable ref, update path, and focused release canary agree.
Expand Down
4 changes: 2 additions & 2 deletions examples/blocker-push-runtime-smoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,8 @@ def main() -> int:
assert "Normal turns use CLI `interaction_contract`" in compact_prompt, prompt
assert "`user_channel.notify` controls OUTPUT only" in compact_prompt, prompt
assert "NOTIFY=向用户输出动作; DONT_NOTIFY=安静输出" in compact_prompt, prompt
assert "Due/peer gate != prompt" in compact_prompt, prompt
assert "missing NOTIFY action->具体user todo未投影" in compact_prompt, prompt
assert "Due/peer非用户动作" in compact_prompt, prompt
assert "NOTIFY缺动作→具体user todo未投影" in compact_prompt, prompt
assert "`LOOPX_TURN=<current_time_iso>`; reuse." in compact_prompt, prompt
assert "guard receipt; 2 stalls->replan" in compact_prompt, prompt
assert "no-change=`surface_only`/no spend" in compact_prompt, prompt
Expand Down
4 changes: 3 additions & 1 deletion examples/bootstrap-command-pack-smoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -717,7 +717,9 @@ def test_skill_slash_fallback_contract() -> None:
assert "pull_requests[review_action_kind!=null].review_template" in pr_review_skill_text
assert "pull_requests[review_action_kind!=null].evidence_commands" in pr_review_skill_text
assert "Do not pipe the only copy through `jq`" in pr_review_skill_text
assert "review_groups" in pr_review_skill_text
assert "`review_groups`" in pr_review_skill_text
assert "ranked actionable `review_sequence`" in pr_review_skill_text
assert "must not appear in `review_sequence`" in pr_review_skill_text
assert "The five sections are output structure, while the execution contract is the evidence authority" in pr_review_normalized


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ def main() -> int:
"LOOPX_RELEASES_DIR": str(root / "releases"),
"LOOPX_SHELL_PROFILE": str(home / ".zshrc"),
"LOOPX_REGISTRY": str(registry_path),
"LOOPX_PYTHON": sys.executable,
"PYTHONPATH": str(REPO_ROOT),
"SHELL": "/bin/zsh",
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ def main() -> int:
choice_run = subprocess.run(
shlex.split(selected_choice["activation_input_command"]),
cwd=REPO_ROOT,
env={**os.environ, "HOME": str(home)},
env={**os.environ, "HOME": str(home), "LOOPX_PYTHON": sys.executable},
check=True,
text=True,
capture_output=True,
Expand Down Expand Up @@ -415,7 +415,7 @@ def main() -> int:
app_ssh_prompt_run = subprocess.run(
shlex.split(app_ssh_activation["activation_input_command"]),
cwd=REPO_ROOT,
env={**os.environ, "HOME": str(home)},
env={**os.environ, "HOME": str(home), "LOOPX_PYTHON": sys.executable},
check=True,
text=True,
capture_output=True,
Expand Down Expand Up @@ -466,7 +466,7 @@ def main() -> int:
app_ssh_quota_run = subprocess.run(
app_ssh_quota_argv,
cwd=project,
env={**os.environ, "HOME": str(home)},
env={**os.environ, "HOME": str(home), "LOOPX_PYTHON": sys.executable},
check=True,
text=True,
capture_output=True,
Expand All @@ -493,7 +493,7 @@ def main() -> int:
cli_onboarding["host_loop_activation"]["activation_input_command"]
),
cwd=REPO_ROOT,
env={**os.environ, "HOME": str(home)},
env={**os.environ, "HOME": str(home), "LOOPX_PYTHON": sys.executable},
check=True,
text=True,
capture_output=True,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
ALLOWED_LEGACY_PATHS = {
REPO_ROOT / "loopx" / "control_plane" / "agents" / "legacy_migration.py",
REPO_ROOT / "loopx" / "control_plane" / "todos" / "contract.py",
REPO_ROOT / "loopx" / "control_plane" / "todos" / "legacy_continuation_policy_migration.ts",
REPO_ROOT / "docs" / "reference" / "protocols" / "peer-agent-runtime-v1.md",
REPO_ROOT / "docs" / "project-agent-todo-contract.md",
REPO_ROOT / "docs" / "product" / "foundations" / "agent-profile-contract.md",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@
)
from loopx.control_plane.todos.decision_scope import ( # noqa: E402
build_required_decision_scope_consistency,
build_standing_decision_authority,
standing_decision_authority_for_agent,
)
from loopx.control_plane.todos.standing_decision import ( # noqa: E402
build_standing_decision_authority,
)
from loopx.quota import build_quota_should_run # noqa: E402

GOAL_ID = "standing-decision-authority-fixture"
Expand Down
21 changes: 14 additions & 7 deletions examples/delivery-batch-scale-enum-smoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@
normalize_delivery_batch_scale,
require_delivery_batch_scale,
)
from loopx.control_plane.work_items.delivery_history import ( # noqa: E402
project_delivery_history,
)
from loopx.state_refresh import refresh_state_run # noqa: E402
from loopx.status import delivery_batch_scale_for_run # noqa: E402


GOAL_ID = "delivery-batch-scale-enum-fixture"
Expand Down Expand Up @@ -205,9 +207,14 @@ def assert_refresh_state_enforces_enum(registry_path: Path) -> None:
assert alias_payload["delivery_batch_scale"] == DeliveryBatchScale.SINGLE_SURFACE.value, alias_payload


def assert_status_uses_enum_not_raw_value() -> None:
def projected_delivery_batch_scale(run: dict[str, object]) -> str:
projection = project_delivery_history([run])
return str(projection["runs"][0]["delivery_batch_scale"])


def assert_delivery_history_uses_enum_not_raw_value() -> None:
assert (
delivery_batch_scale_for_run(
projected_delivery_batch_scale(
{
"classification": "runner_batch_fixture",
"delivery_batch_scale": DeliveryBatchScale.MULTI_SURFACE.value,
Expand All @@ -216,7 +223,7 @@ def assert_status_uses_enum_not_raw_value() -> None:
== DeliveryBatchScale.MULTI_SURFACE.value
)
assert (
delivery_batch_scale_for_run(
projected_delivery_batch_scale(
{
"classification": "runner_batch_fixture",
"delivery_batch_scale": "batch_plus_raw_logs",
Expand All @@ -225,11 +232,11 @@ def assert_status_uses_enum_not_raw_value() -> None:
== UNKNOWN_DELIVERY_BATCH_SCALE
)
assert (
delivery_batch_scale_for_run({"classification": "owner_handoff_consumer_test"})
projected_delivery_batch_scale({"classification": "owner_handoff_consumer_test"})
== UNKNOWN_DELIVERY_BATCH_SCALE
)
assert (
delivery_batch_scale_for_run(
projected_delivery_batch_scale(
{
"classification": "owner_handoff_consumer_test",
"delivery_batch_scale": DeliveryBatchScale.TEST_ONLY.value,
Expand All @@ -244,7 +251,7 @@ def main() -> int:
with tempfile.TemporaryDirectory(prefix="delivery-batch-scale-enum-") as tmp:
registry_path, _runtime = write_fixture(Path(tmp))
assert_refresh_state_enforces_enum(registry_path)
assert_status_uses_enum_not_raw_value()
assert_delivery_history_uses_enum_not_raw_value()
print("delivery batch scale enum smoke ok")
return 0

Expand Down
28 changes: 22 additions & 6 deletions examples/issue-fix-acceptance-loop-smoke.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
from __future__ import annotations

import json
import os
import shlex
import shutil
import subprocess
import sys
import tempfile
Expand Down Expand Up @@ -45,6 +47,7 @@ def _run_caller_repo_json_command(
*,
issue_branch: str = "codex/issue-123-public-metadata-fixture",
expect_success: bool = True,
env: dict[str, str] | None = None,
) -> dict[str, Any]:
result = subprocess.run(
[
Expand Down Expand Up @@ -74,6 +77,7 @@ def _run_caller_repo_json_command(
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=expect_success,
env=env,
)
if not expect_success and result.returncode == 0:
raise AssertionError("caller repo branch mode unexpectedly succeeded")
Expand Down Expand Up @@ -346,18 +350,30 @@ def main() -> int:
_run_git(repo_path, ["commit", "-m", "Prepare a later base revision"])
later_revision = _git_output(repo_path, ["rev-parse", "HEAD"])
_run_git(repo_path, ["checkout", "-b", "caller-start", "main"])
hook = repo_path / ".git" / "hooks" / "post-checkout"
hook.write_text(
real_git = shutil.which("git")
assert real_git is not None
shim_dir = fixture_root / "git-shim"
shim_dir.mkdir()
git_shim = shim_dir / "git"
git_shim.write_text(
"#!/bin/sh\n"
f"git update-ref refs/heads/main {later_revision}\n",
f"{shlex.quote(real_git)} \"$@\"\n"
"status=$?\n"
"if [ \"$status\" -eq 0 ] && [ \"$1\" = checkout ] && "
"[ \"$2\" = -b ] && [ \"$3\" = codex/issue-123-pinned-base ]; then\n"
f" {shlex.quote(real_git)} update-ref refs/heads/main {later_revision}\n"
"fi\n"
"exit \"$status\"\n",
encoding="utf-8",
)
hook.chmod(0o755)
_run_git(repo_path, ["config", "core.hooksPath", ".git/hooks"])

git_shim.chmod(0o755)
pinned_payload = _run_caller_repo_json_command(
repo_path,
issue_branch="codex/issue-123-pinned-base",
env={
**os.environ,
"PATH": str(shim_dir) + os.pathsep + os.environ.get("PATH", ""),
},
)
pinned_branch = pinned_payload["caller_repo_branch"]
assert pinned_branch["base_snapshot"]["base_revision"] == approved_revision
Expand Down
3 changes: 3 additions & 0 deletions examples/reward-memory-dogfood-smoke.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env python3
from __future__ import annotations

import os
import sys
import json
import subprocess
Expand Down Expand Up @@ -582,6 +583,7 @@ def main() -> None:
check=True,
capture_output=True,
text=True,
env={**os.environ, "LOOPX_PYTHON": sys.executable},
)
cli_packet = json.loads(completed.stdout)
assert cli_packet["status"] == "ready_for_bounded_issue_fix_pilot"
Expand Down Expand Up @@ -631,6 +633,7 @@ def main() -> None:
check=True,
capture_output=True,
text=True,
env={**os.environ, "LOOPX_PYTHON": sys.executable},
)
control_packet = json.loads(completed.stdout)
assert control_packet["status"] == "control_ready"
Expand Down
2 changes: 1 addition & 1 deletion loopx/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

__all__ = ["__version__"]

__version__ = "1.0.2"
__version__ = "1.0.3"
53 changes: 3 additions & 50 deletions loopx/cli_commands/support_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
handle_backup_state_command,
register_backup_state_command,
)
from .support_control_agent_runtime import register_agent_runtime_arguments
from .support_control_chat_endpoint import (
handle_chat_endpoint_command,
register_chat_endpoint_command,
Expand Down Expand Up @@ -325,31 +326,7 @@ def register_support_control_commands(
"--host", default=DEFAULT_CHAT_HOST, help="Loopback bind host."
)
chat_parser.add_argument("--port", type=int, default=DEFAULT_CHAT_PORT)
chat_parser.add_argument(
"--codex-bin",
default="codex",
help="Codex CLI executable used for the read-only app-server session.",
)
chat_parser.add_argument(
"--claude-bin",
default="claude",
help="Claude Code CLI executable used for read-only Agent sessions.",
)
chat_parser.add_argument(
"--kiro-cli-bin",
default=KIRO_CLI_BIN,
help=(
"Kiro CLI executable used for read-only ACP Agent sessions "
"(`<bin> acp`)."
),
)
chat_parser.add_argument(
"--lark-cli-bin",
help=(
"Optional explicit lark-cli executable. When omitted, LoopX uses its bounded "
"runtime discovery order."
),
)
register_agent_runtime_arguments(chat_parser, kiro_cli_bin=KIRO_CLI_BIN)
chat_parser.add_argument(
"--startup-timeout-seconds",
type=float,
Expand Down Expand Up @@ -424,31 +401,7 @@ def register_support_control_commands(
"--host", default=DEFAULT_CHAT_HOST, help="Loopback bind host."
)
dashboard_parser.add_argument("--port", type=int, default=DEFAULT_CHAT_PORT)
dashboard_parser.add_argument(
"--codex-bin",
default="codex",
help="Codex CLI executable used for the read-only app-server session.",
)
dashboard_parser.add_argument(
"--claude-bin",
default="claude",
help="Claude Code CLI executable used for read-only Agent sessions.",
)
dashboard_parser.add_argument(
"--kiro-cli-bin",
default=KIRO_CLI_BIN,
help=(
"Kiro CLI executable used for read-only ACP Agent sessions "
"(`<bin> acp`)."
),
)
dashboard_parser.add_argument(
"--lark-cli-bin",
help=(
"Optional explicit lark-cli executable. When omitted, LoopX uses its bounded "
"runtime discovery order."
),
)
register_agent_runtime_arguments(dashboard_parser, kiro_cli_bin=KIRO_CLI_BIN)
dashboard_parser.add_argument(
"--assets-dir",
help="Optional LoopX Chat web bundle directory. Defaults to packaged assets.",
Expand Down
Loading
Loading