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
25 changes: 25 additions & 0 deletions .github/workflows/python-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ on:
- "scripts/**"
- "tests/**"
- "examples/**"
- "package.json"
- "package-lock.json"
- "pyproject.toml"
- "tsconfig.control-plane.json"
push:
branches:
- main
Expand All @@ -18,7 +21,10 @@ on:
- "scripts/**"
- "tests/**"
- "examples/**"
- "package.json"
- "package-lock.json"
- "pyproject.toml"
- "tsconfig.control-plane.json"

permissions:
contents: read
Expand All @@ -43,9 +49,22 @@ jobs:
python-version: "3.11"
cache: pip

- name: Set up the TypeScript Effect runtime
uses: actions/setup-node@v6
with:
node-version: "22.6"
cache: npm
cache-dependency-path: package-lock.json

- name: Install test dependencies
run: python -m pip install --disable-pip-version-check -e ".[test]"

- name: Qualify the TypeScript Effect core
run: |
npm ci --ignore-scripts
npm run typecheck:control-plane
npm run test:control-plane

- name: Lint test suite
run: >-
python -m ruff check
Expand Down Expand Up @@ -85,6 +104,11 @@ jobs:
python-version: "3.11"
cache: pip

- name: Set up the TypeScript Effect runtime
uses: actions/setup-node@v6
with:
node-version: "22.6"

- name: Install test dependencies
run: python -m pip install --disable-pip-version-check -e ".[test]"

Expand All @@ -95,5 +119,6 @@ jobs:
tests/test_doctor_install_freshness.py
tests/test_file_lock.py
tests/test_file_lock_cross_process.py
tests/control_plane/test_effect_runtime_integration.py
tests/test_self_update_runtime_activation.py
tests/test_windows_install.py
95 changes: 95 additions & 0 deletions .github/workflows/release-artifacts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@ on:
- ".github/workflows/release-artifacts.yml"
- "examples/release-artifacts-smoke.py"
- "loopx/**"
- "package.json"
- "package-lock.json"
- "pyproject.toml"
- "README.md"
- "scripts/release_artifacts.py"
- "tsconfig.control-plane.json"
release:
types: [published]
workflow_dispatch:
Expand Down Expand Up @@ -49,6 +52,11 @@ jobs:
python-version: "3.11"
cache: pip

- name: Set up the packaged TypeScript control-plane runtime
uses: actions/setup-node@v6
with:
node-version: "22.6"

- name: Resolve and validate release identity
id: identity
env:
Expand Down Expand Up @@ -102,6 +110,93 @@ jobs:
dist/packages/*.whl
test "$("${RUNNER_TEMP}/loopx-wheel/bin/loopx" --version)" = \
"loopx ${RELEASE_TAG#v}"
"${RUNNER_TEMP}/loopx-wheel/bin/python" - <<'PY'
from loopx.control_plane.effect_program import SettlementIdentity
from loopx.control_plane.effect_runtime import (
collect_effect_runtime_readiness,
)
from loopx.control_plane.turn_driver.turn_journal_runtime import (
interpret_turn_journal_projection,
write_turn_journal,
)
from pathlib import Path
from tempfile import TemporaryDirectory

turn_key = "sha256:" + "a" * 64
journal = {
"schema_version": "loopx_turn_journal_v0",
"goal_id": "fixture-goal",
"turn_key": turn_key,
"status": "committed",
"completed_phases": [
"host_execute",
"typed_result",
"validation",
"durable_writeback",
"quota_spend",
"scheduler_apply",
"scheduler_ack",
],
"plan": {
"turn_envelope": {
"goal_id": "fixture-goal",
"agent_id": "fixture-agent",
},
"transaction": {
"turn_key": turn_key,
"settlement_plan": {
"identity": {
"goal_id": "fixture-goal",
"agent_id": "fixture-agent",
}
},
},
},
}
result = interpret_turn_journal_projection(
journal,
goal_id="fixture-goal",
agent_id="fixture-agent",
turn_key=turn_key,
)
assert result["decision"] == "replay_legal", result
assert result["effects"] == [], result
identity = SettlementIdentity(
goal_id="fixture-goal",
agent_id="fixture-agent",
todo_id="fixture-todo",
turn_instance_id="fixture-turn",
)
assert identity.effect_id == (
"fixture-goal:fixture-agent:fixture-todo:fixture-turn"
), identity
readiness = collect_effect_runtime_readiness(deep=True)
assert readiness["status"] == "ready", readiness
assert readiness["semantic_probe"] == "passed", readiness
with TemporaryDirectory() as temporary_directory:
effect_id = identity.effect_id
writable_journal = {
"schema_version": "loopx_turn_journal_v0",
"goal_id": "fixture-goal",
"turn_key": turn_key,
"status": "in_progress",
"completed_phases": ["host_execute"],
"plan": {
"transaction": {
"settlement_plan": {
"identity": {"effect_id": effect_id}
}
}
},
}
committed = write_turn_journal(
str(Path(temporary_directory) / "turn.json"),
writable_journal,
expected_effect_id=effect_id,
)
assert committed["appended"] is True, committed
assert committed["replayed"] is False, committed
PY
skills_dir="${RUNNER_TEMP}/loopx-wheel-skills"
"${RUNNER_TEMP}/loopx-wheel/bin/loopx" --format json \
workflow-skills --install --skills-dir "${skills_dir}" \
Expand Down
28 changes: 17 additions & 11 deletions docs/architecture/rfcs/agent-loop-effect-interpreter-v0.md
Original file line number Diff line number Diff line change
Expand Up @@ -568,19 +568,25 @@ and budget rejection distinct, and leave scheduler apply or ACK outside the
agent-owned settlement boundary.

M7.3: after both M7.2 adapters consume the proven plan and receipt semantics,
compare their execution ownership. Extract the smallest shared executor or
Kleisli-like bind protocol only if it deletes duplicate orchestration without
crossing the Codex App agent/host boundary. Do not add a registry or generic
composition framework. `quota should-run` may derive both its packet and
effect projection from one canonical decision plan, but constructing
`EffectTurn` earlier is not itself an acceptance condition. If the two callers
share only the algebra and not an executor boundary, close M7.3 with a
structured no-follow-up decision and keep their executors local.
compare their execution ownership. The 2026-08-21 cutover qualification found
that settlement identity, bind/short-circuit, replay seeding, next-action
selection, and commit reduction were still duplicated across the adapters.
This reopens M7.3 for one bounded TypeScript Effect runtime. The runtime owns
that shared algebra and the first internal effect, atomic Turn-journal
checkpointing. Its server is only a temporary Python-to-TypeScript transport;
one static typed handler registry routes coarse transactions to domain owners.
It is not a generic composition framework and does not move model, user, host
scheduler, credential, or third-party authority behind a universal executor.
Every replaced Python semantic path is deleted in the same cutover PR.

M7.4: expand one bounded family at a time only when it removes duplicate
knowledge. Todo, monitor, capability, scheduler, and gate state machines keep
their domain transition invariants. They do not move behind a shared protocol
merely because their packets have similar fields.
knowledge and switches a real production caller. Todo, monitor, capability,
scheduler, and gate state machines keep their domain transition invariants.
They may execute through the same managed runtime as they migrate, but they do
not move behind one generic state protocol merely because their packets have
similar fields. After the CLI is native TypeScript, CLI-only execution imports
the kernel in-process; the daemon remains optional for App/multi-client shared
authority rather than a mandatory server per family.

The replan semantic-exit repair in #3208 is an explicit non-candidate:
`refresh-state` already re-derives the current obligation and records a typed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -373,9 +373,9 @@ M7.1:在添加 protocol 前刻画选中的 vertical slice。为合法与非法

M7.2:用一个 typed plan/receipt algebra 替换核心 settlement truth。plan step 必须携带稳定 kind、owner、precondition、idempotency identity 和 expected receipt。默认 Codex App path 与隔离 turn driver 把 validation、durable writeback、quota spend 和 conditional terminal closeout 绑定到原始 quota-turn effect identity。普通 successor completion 可以在 settlement 前推进 Todo frontier;final `no_followup` 只有在 matching writeback/spend receipt 后才提交,不能增加 terminal guard 例外。每个 replacement PR 都必须删除对应的 manual command 或 settlement truth。Raw mappings 和 free-form CLI commands 可以保留为 compatibility payloads,但不是语义执行合同。组合必须满足上文定义的 identity、associativity、short-circuit、replay 和 ordering 性质,保持 cancellation、permission denial 和 budget rejection 可区分,并让 scheduler apply 或 ACK 留在 agent-owned settlement boundary 之外。

M7.3:在两个 M7.2 adapter 都消费经过验证的 plan/receipt 语义后,比较它们的执行所有权。只有在删除重复编排且不跨越 Codex App agent/host boundary 时,才抽取最小的共享 executor 或 Kleisli-like bind protocol。不要增加 registry 或通用组合框架。`quota should-run` 可以从同一个 canonical decision plan 派生 packet 和 effect projection,但更早构造 `EffectTurn` 本身不是验收条件。如果两个 caller 只共享 algebra 而不共享 executor boundary,用结构化 no-follow-up decision 关闭 M7.3,并保留各自的 local executor。
M7.3:在两个 M7.2 adapter 都消费经过验证的 plan/receipt 语义后,比较它们的执行所有权。2026-08-21 的 cutover qualification 发现,settlement identity、bind/short-circuit、replay seeding、next-action selection 与 commit reduction 仍在 adapter 间重复。因此重新打开 M7.3,引入一个 bounded TypeScript Effect runtime。Runtime 拥有共享 algebra 和第一个内部 effect——atomic Turn-journal checkpoint。它的 server 只是临时 Python-to-TypeScript transport;一个静态 typed handler registry 把粗粒度 transaction 路由给 domain owner。它不是通用组合框架,也不会把 model、user、host scheduler、credential 或第三方 authority 藏到万能 executor 后面。每条被替代的 Python 语义路径都必须在同一 cutover PR 删除

M7.4:只有在移除重复知识时,才一次扩展一个有界状态族。Todo、monitor、capability、scheduler 和 gate 状态机保留自己的 domain transition invariants。它们不能仅仅因为 packet 字段相似就移到共享 protocol 后面
M7.4:只有在移除重复知识并切换真实生产 caller 时,才一次扩展一个 bounded 状态族。Todo、monitor、capability、scheduler 和 gate 状态机保留自己的 domain transition invariants。它们迁移后可以通过同一个 managed runtime 执行,但不能仅仅因为 packet 字段相似就移到一个通用状态协议后面。CLI 原生迁到 TypeScript 后,CLI-only 在进程内 import kernel;daemon 只在 App/多 client 共享 authority 时可选保留,而不是每个状态族一个必选 server

#3208 的 replan semantic-exit 修复明确不是候选:`refresh-state` 已经会重新推导当前 obligation 并记录 typed semantic ACK,实际缺陷是 goal-frontier 中一个额外的 settlement 条件在 acceptance gaps 仍存在时忽略了合法的 non-successor ACK。这是 domain-local reducer/ACK invariant,不是第二个 multi-step executor,应继续由 replan/goal-frontier owner 持有。只有第二个真实 runtime 场景(例如具有相同 plan/receipt lifecycle 的 quota/status read ACK)出现,并且能在两个 adapter 间删除重复编排时,才重新评估 Effect Program 迁移。

Expand Down
Loading
Loading