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
130 changes: 130 additions & 0 deletions docs/reference/goal-acceptance-observations.md
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,133 @@ Readback validation: Dashboard `npm run smoke:delivery-review`,
After the integrated Dashboard build, `npm run smoke:goal-acceptance-contract-packaged`
runs the same contract browser check against the shipped assets.

<a id="owner-check-scope-reconciliation"></a>

### Reconcile obsolete work before supersession / 替代旧任务前协调检查范围

Use this owner path when an authorized correction replaces a Todo's behavior,
while the original objective and unaffected acceptance remain valid. For example,
`todo_parse` is already accepted, `todo_transform_old` is obsolete,
`todo_transform_current` is its existing replacement, and `todo_final` still checks
the delivery. Inspect acceptance and Todos on the same registry/runtime route.
Keep the original owner document and correction source with the operation receipt:
public `inspect` deliberately omits executable argv, so it cannot reconstruct the
complete configuration.

With `all_advancement`, superseding bound work can return
`goal_acceptance_validation_required`. Removing only its binding still leaves it
in scope and can return `goal_acceptance_unbound`. Neither means that an Agent may
disable checks. The authorized local owner must review the **whole replacement
document**, including every retained criterion, command/pin/oracle and binding.
Explicitly narrow coverage to the current accepted work; future Todos will no
longer be included automatically and need owner review if they belong in scope.

For this standalone contract, save the following as `acceptance-current.json`.
Replace the illustrative IDs and bounded artifact checks with the real reviewed
ones, including the available Python executable. Preserve any existing validator
pins/oracles. The checks below assert actual output, not unconditional success.
Do not add `outcome_id` to this expanded document: the compact outcome authoring
form resolves its work links and supports only omitted scope or explicit `goal`
scope; it cannot express this explicit work subset.

```json
{
"scope": {"kind": "selected_work", "todo_ids": ["todo_parse", "todo_transform_current", "todo_final"]},
"objective": "Deliver validated records",
"non_goals": ["Publish the records"],
"criteria": [
{
"id": "parse",
"description": "The accepted parsed records remain intact.",
"validation_argv": ["python3", "-c", "from pathlib import Path; assert Path('parsed.txt').read_text() == 'alpha\\nbeta\\n'"],
"validation_timeout_seconds": 5
},
{
"id": "transform-current",
"description": "The corrected transform preserves the requested order.",
"validation_argv": ["python3", "-c", "from pathlib import Path; assert Path('transformed.txt').read_text() == 'ALPHA\\nBETA\\n'"],
"validation_timeout_seconds": 5
},
{
"id": "final",
"description": "The final delivery contains the corrected records.",
"validation_argv": ["python3", "-c", "from pathlib import Path; assert Path('delivery.txt').read_text() == 'ALPHA\\nBETA\\n'"],
"validation_timeout_seconds": 5
}
],
"bindings": [
{"todo_id": "todo_parse", "criterion_ids": ["parse"]},
{"todo_id": "todo_transform_current", "criterion_ids": ["transform-current"]},
{"todo_id": "todo_final", "criterion_ids": ["final"]}
]
}
```

Keep explicit `--registry` / `--runtime-root` arguments on **every** command when
using a nondefault route. Configure without `--agent-id` only under existing local
owner authorization; omission selects a trusted local role, not authentication.
Use the exact current `provider_revision` from `inspect`, never the acceptance
contract's numeric `revision`. Preview and apply the identical reviewed document
with the same operation ID and provider revision:

```bash
loopx --format json goal-acceptance inspect --goal-id example-goal
loopx --format json goal-acceptance configure --goal-id example-goal \
--document acceptance-current.json --expected-provider-revision '<provider_revision>' \
--operation-id reconcile-transform-v2
loopx --format json goal-acceptance configure --goal-id example-goal \
--document acceptance-current.json --expected-provider-revision '<provider_revision>' \
--operation-id reconcile-transform-v2 --execute
```

Read the returned `operation_id`, `provider_revision`, scope, criteria and `tasks`
associations (`todo_id`, `criterion_ids`, `state`);
the accepted parser and final check must remain, and the obsolete Todo must be
absent from both scope and bindings. Retain the reviewed document, correction
source and receipt together. After a lost response retry that exact operation
with its original revision. After a confirmed revision conflict, inspect and
review current state before creating a new operation; do not silently rebase.

Only then supersede the obsolete Todo. For canonical `hard_lease` work already
claimed by `example-agent`, acquire its ordinary lease (or inspect its existing
lease) and use the exact returned `lease.idempotency_key` and `lease.version`:

```bash
loopx --format json task-lease acquire --goal-id example-goal \
--todo-id todo_transform_old --owner example-agent --idempotency-key retire-transform-v1
loopx --format json todo supersede --goal-id example-goal \
--todo-id todo_transform_old --agent-id example-agent \
--reason 'message:owner-correction: use the corrected transform' \
--successor-todo-id todo_transform_current \
--task-lease-idempotency-key '<lease.idempotency_key>' \
--task-lease-expected-version '<lease.version>'
loopx --format json todo list --goal-id example-goal
loopx --format json goal-acceptance inspect --goal-id example-goal
```

This uses the raw valid lease pair; it does not require managed supersede claims.
Do not reuse a released/expired lease or substitute a provider revision for its
version. Read back the original Todo's successor link and the retained work/checks.
Supersession records obsolete history; it does not verify the successor, rerun the
accepted parser, or complete the Goal. Current/final work still needs its normal
fresh checks, claims, leases and continuation handling.

**保守边界:** 仅在已有明确授权且原目标与未受影响的验收仍有效时,由本地 owner 审阅
完整替换文档。仅删 binding 不能让旧任务退出 `all_advancement`;改为上述
`selected_work` 保留已验收 parser、当前替代任务和最终检查,同时明确以后新增任务
不再自动纳入。每条命令沿用同一 registry/runtime,配置用最新 `provider_revision`,
预览与执行复用同一 operation ID;丢响应原样重试,确认冲突后重新审阅并新建操作。
保留原配置、用户更正来源、拟用文档和回执,再用实际 lease key/version 替代旧任务。
配置与 supersede 都不证明新产物通过;后续仍须执行真实检查。

This changes executable check coverage only. It does **not** retire or replace
persisted additive `outcome_requirements`, erase their sources/work history, or
resolve a contradictory immutable requirement. Report that conflict as unresolved
unless a supported owner amendment path actually reconciles it; do not claim this
recipe aligns it. 中文:本流程只协调命令检查覆盖,不撤销、覆盖或伪装满足累计的不可变
成果要求;若已持久化要求互相冲突,在受支持的 owner 修订实际解决前,必须明确保留
未解决状态。

## 中文

打开本机 Goal,选择 **概览 → 交付与依据 → 验收观察**。
Expand Down Expand Up @@ -612,6 +739,9 @@ status 同时在独立的 `run_history.goals[].artifact_lifecycle` 和 Markdown

### 默认验收中的命令检查

旧任务被新任务替代时,使用上方[检查范围协调示例](#owner-check-scope-reconciliation):
完整保留仍有效的检查与工作绑定,由 owner 明确排除旧任务后,再按真实租约执行 supersede。

已注册且**已提升到 canonical authority** 的 Goal 可为已有成果要求配置命令检查。
命令不会自动提升 provider,canonical 读取失败也不回退到旧 Markdown。
优先引用 `outcome_id` 和要求 ID,由类型化 owner 解析目标、条件描述和关联任务;
Expand Down
13 changes: 12 additions & 1 deletion loopx/cli_commands/goal_acceptance.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,18 @@ def register_goal_acceptance_command(subparsers: Any, add_format: Any) -> None:
Own work review/adoption needs no human reply or unanimous peer vote.
All steps require --goal-id GOAL. Proposal preparation alone requires neither
owner input nor adoption; only an applied objective revision needs adoption.
Authority, budgets, stops and existing gated actions remain binding.''',
Authority, budgets, stops and existing gated actions remain binding.

Owner check-scope reconciliation (configure without --agent-id):
--document is the full replacement: {"scope":{"kind":"selected_work","todo_ids":["todo_current"]},
"objective":"Retained objective","non_goals":[],"criteria":[{"id":"result","description":"Real check", "validation_argv":["python3","check.py"]}],
"bindings":[{"todo_id":"todo_current","criterion_ids":["result"]}]}
Retain accepted/current/final work and checks; exclude obsolete work from scope
AND bindings. Removing a binding alone leaves all_advancement work held.
Use inspect's provider_revision and one stable --operation-id for preview/apply
and unchanged retries. Configure changes checks, not additive outcome requirements.
Full example, owner authority and lease-fenced supersession:
docs/reference/goal-acceptance-observations.md#owner-check-scope-reconciliation''',
)
add_format(parser)
parser.add_argument("action", type=lambda value: "clear-checks" if value == "disable" else value, choices=("inspect", "prepare", "configure", "verify", "clear-checks", "refine", "prepare-objective", "inspect-proposal", "apply-proposal", "revise-objective", "adopt-objective"))
Expand Down
8 changes: 8 additions & 0 deletions skills/loopx-project/references/outcome-refinement.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ and mark both met, or silently edit an authoritative file. Ask only when a
consequential meaning or authority is genuinely missing, not merely because the
user gave a correction.

For obsolete work covered by executable checks, follow the
[owner check-scope reconciliation example](../../../docs/reference/goal-acceptance-observations.md#owner-check-scope-reconciliation).
It preserves accepted work and current/final checks in an explicit
`selected_work` replacement before lease-fenced supersession. Removing a binding
alone does not remove work from `all_advancement`. This owner configuration path
does not supersede persisted additive outcome requirements; keep any conflicting
requirement explicit rather than claiming the check update retired it.

## Current operating objective

When recorded explicit user direction makes the Goal objective obsolete, prepare
Expand Down
126 changes: 126 additions & 0 deletions tests/control_plane/test_owner_check_scope_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
"""Execute the documented owner reconciliation against disposable canonical stores."""
from __future__ import annotations

from copy import deepcopy
import json
from pathlib import Path
import sys

import pytest
from canonical_authority_fixture import initialize_canonical_authority, isolate_sqlite_runtime

from loopx.control_plane.coordination.runtime_shadow import build_todo_runtime_shadow_projection
from loopx.control_plane.testing.canary_harness import run_json_cli_result, write_fixture_registry


@pytest.mark.parametrize("provider", ["file", "sqlite"])
def test_documented_owner_scope_reconciliation(tmp_path, monkeypatch, provider):
if provider == "sqlite":
isolate_sqlite_runtime(tmp_path, monkeypatch)
project = tmp_path / "project"
project.mkdir()
(project / "parsed.txt").write_text("alpha\nbeta\n")
state = project / "state.md"
state.write_text("---\nstatus: active\n---\n# Fixture\n## Agent Todo\n")
runtime, registry = tmp_path / "runtime", tmp_path / "registry.json"
write_fixture_registry(project=project, runtime_root=runtime, registry_path=registry,
goal_id="example-goal", domain="acceptance", adapter_kind="generic_project_goal_v0",
state_file=str(state), registered_agents=["example-agent"])
todos = [{"schema_version": "todo_item_v0", "todo_id": todo_id, "role": "agent",
"text": text, "status": "done" if index == 1 else "open", "done": index == 1,
"task_class": "advancement_task", "action_kind": "implement", "index": index,
"source_section": "Agent Todo", "archive_state": "active", "claimed_by": "example-agent"}
for index, (todo_id, text) in enumerate([
("todo_parse", "Parse the records"),
("todo_transform_old", "Transform using the obsolete ordering"),
("todo_transform_current", "Transform preserving the requested order"),
("todo_final", "Check the final delivery"),
], 1)]
initialize_canonical_authority(runtime, "example-goal",
build_todo_runtime_shadow_projection(goal_id="example-goal", todos=todos, handoff_mode="hard_lease"),
state_path=state, provider=provider)

def run(*args, expected=0):
code, result = run_json_cli_result(*args, registry_path=registry, runtime_root=runtime)
assert code == expected, result
return result

def acceptance(action, *args, expected=0):
return run("goal-acceptance", action, "--goal-id", "example-goal", *args, expected=expected)

refinement = project / "outcome.json"
refinement.write_text(json.dumps({"outcome_id": "records", "owner_agent_id": "example-agent",
"objective": "Deliver validated records", "todo_ids": ["todo_parse"],
"requirements": [{"id": "parse", "description": "The accepted parsed records remain intact."}],
"source": {"ref": "message:initial", "summary": "The owner requested validated records"}}))
acceptance("refine", "--agent-id", "example-agent", "--document", str(refinement),
"--expected-provider-revision", acceptance("inspect")["provider_revision"],
"--operation-id", "declare-records", "--execute")
retained = acceptance("inspect")["goal_acceptance_contract"]["outcome_requirements"]["outcomes"]

docs = Path(__file__).resolve().parents[2] / "docs/reference/goal-acceptance-observations.md"
section = docs.read_text(encoding="utf-8").split('<a id="owner-check-scope-reconciliation"></a>', 1)[1]
document = json.loads(section.split("```json\n", 1)[1].split("```", 1)[0])
# Use this test process's interpreter; all validator code is the documented code.
for criterion in document["criteria"]:
criterion["validation_argv"][0] = sys.executable
path = project / "acceptance-current.json"

def configure(value, operation):
path.write_text(json.dumps(value))
return ("--document", str(path), "--expected-provider-revision",
acceptance("inspect")["provider_revision"], "--operation-id", operation)

original = deepcopy(document)
original["scope"] = {"kind": "all_advancement"}
original["bindings"][1]["todo_id"] = "todo_transform_old"
original["criteria"][1].update(id="transform-old", description="The obsolete transform reverses the records.",
validation_argv=[sys.executable, "-c",
"from pathlib import Path; assert Path('transformed.txt').read_text() == 'BETA\\nALPHA\\n'"])
original["bindings"][1]["criterion_ids"] = ["transform-old"]
acceptance("configure", *configure(original, "configure-original"), "--execute")
lease = run("task-lease", "acquire", "--goal-id", "example-goal", "--todo-id", "todo_transform_old",
"--owner", "example-agent", "--idempotency-key", "retire-transform-v1")["lease"]
supersede = ("todo", "supersede", "--goal-id", "example-goal", "--todo-id", "todo_transform_old",
"--agent-id", "example-agent", "--reason", "message:owner-correction: use the corrected transform",
"--successor-todo-id", "todo_transform_current", "--task-lease-idempotency-key", lease["idempotency_key"],
"--task-lease-expected-version", str(lease["version"]))
assert run(*supersede, expected=1)["reason_code"] == "goal_acceptance_validation_required"

rebind_only = {**document, "scope": {"kind": "all_advancement"}}
acceptance("configure", *configure(rebind_only, "rebind-only"), "--execute")
assert run(*supersede, expected=1)["reason_code"] == "goal_acceptance_unbound"

args = configure(document, "reconcile-transform-v2")
assert acceptance("configure", *args)["status"] == "planned"
applied = acceptance("configure", *args, "--execute")
assert applied["status"] == "applied" and applied["operation_id"] == "reconcile-transform-v2"
assert applied["source_authority"] == f"{provider}_v0"
replay = acceptance("configure", *args, "--execute")
assert replay["status"] == "replayed" and replay["provider_revision"] == applied["provider_revision"]
assert replay["operation_id"] == applied["operation_id"]
contract = applied["goal_acceptance_contract"]
assert contract["scope"]["kind"] == "selected_work"
assert set(contract["scope"]["todo_ids"]) == set(document["scope"]["todo_ids"])
assert {item["todo_id"]: item["criterion_ids"] for item in contract["tasks"]} == {
binding["todo_id"]: binding["criterion_ids"] for binding in document["bindings"]}
assert all(item["state"] == "ready" for item in contract["tasks"])
assert {item["id"] for item in contract["criteria"]} == {"parse", "transform-current", "final"}
assert contract["outcome_requirements"]["outcomes"] == retained

retired = run(*supersede)
assert retired["superseded"] is True and retired["source_authority"] == f"{provider}_v0"
readback = run("todo", "list", "--goal-id", "example-goal")
records = {todo["todo_id"]: todo for todo in readback["todos"]}
assert records["todo_transform_old"]["superseded_by"] == "todo_transform_current"
assert records["todo_parse"]["done"] is True
assert records["todo_transform_current"]["done"] is False
assert records["todo_final"]["done"] is False
assert acceptance("inspect")["goal_acceptance_contract"]["outcome_requirements"]["outcomes"] == retained
assert acceptance("verify", "--agent-id", "example-agent", "--execute", expected=1)["checks_passed"] is False
for filename, content in [("parsed.txt", "alpha\nbeta\n"),
("transformed.txt", "ALPHA\nBETA\n"), ("delivery.txt", "ALPHA\nBETA\n")]:
(project / filename).write_text(content)
verified = acceptance("verify", "--agent-id", "example-agent", "--execute")
assert verified["checks_passed"] is True
assert acceptance("inspect")["goal_acceptance_contract"]["status"] == "accepted"
Loading