Add workspace review summaries
Add workspace summary across the CLI, SDK, and MCP, and include it in the workspace-core profile so chat hosts can review one concise view of the current session. Persist lightweight review events for syncs, file edits, patch applies, exports, service lifecycle, and snapshot activity, then synthesize them with command history, current services, snapshot state, and current diff data since the last reset. Update the walkthroughs, use-case docs, public contract, changelog, and roadmap for 4.3.0, and make dist-check invoke the CLI module directly so local package reinstall quirks do not break the packaging gate. Validation: uv lock; ./.venv/bin/pytest --no-cov tests/test_vm_manager.py tests/test_cli.py tests/test_api.py tests/test_server.py tests/test_public_contract.py tests/test_workspace_use_case_smokes.py; UV_OFFLINE=1 UV_CACHE_DIR=.uv-cache make check; UV_OFFLINE=1 UV_CACHE_DIR=.uv-cache make dist-check; real guest-backed workspace create -> patch apply -> workspace summary --json -> delete smoke.
This commit is contained in:
parent
899a6760c4
commit
dc86d84e96
24 changed files with 994 additions and 31 deletions
|
|
@ -699,6 +699,124 @@ def test_workspace_diff_and_export_round_trip(tmp_path: Path) -> None:
|
|||
assert logs["count"] == 0
|
||||
|
||||
|
||||
def test_workspace_summary_synthesizes_current_session(tmp_path: Path) -> None:
|
||||
seed_dir = tmp_path / "seed"
|
||||
seed_dir.mkdir()
|
||||
(seed_dir / "note.txt").write_text("hello\n", encoding="utf-8")
|
||||
update_dir = tmp_path / "update"
|
||||
update_dir.mkdir()
|
||||
(update_dir / "more.txt").write_text("more\n", encoding="utf-8")
|
||||
|
||||
manager = VmManager(
|
||||
backend_name="mock",
|
||||
base_dir=tmp_path / "vms",
|
||||
network_manager=TapNetworkManager(enabled=False),
|
||||
)
|
||||
|
||||
workspace_id = str(
|
||||
manager.create_workspace(
|
||||
environment="debian:12-base",
|
||||
allow_host_compat=True,
|
||||
seed_path=seed_dir,
|
||||
name="review-eval",
|
||||
labels={"suite": "smoke"},
|
||||
)["workspace_id"]
|
||||
)
|
||||
manager.push_workspace_sync(workspace_id, source_path=update_dir)
|
||||
manager.write_workspace_file(workspace_id, "src/app.py", text="print('hello')\n")
|
||||
manager.apply_workspace_patch(
|
||||
workspace_id,
|
||||
patch=(
|
||||
"--- a/note.txt\n"
|
||||
"+++ b/note.txt\n"
|
||||
"@@ -1 +1 @@\n"
|
||||
"-hello\n"
|
||||
"+patched\n"
|
||||
),
|
||||
)
|
||||
manager.exec_workspace(workspace_id, command="cat note.txt", timeout_seconds=30)
|
||||
manager.create_snapshot(workspace_id, "checkpoint")
|
||||
export_path = tmp_path / "exported-note.txt"
|
||||
manager.export_workspace(workspace_id, "note.txt", output_path=export_path)
|
||||
manager.start_service(
|
||||
workspace_id,
|
||||
"app",
|
||||
command='sh -lc \'trap "exit 0" TERM; touch .ready; while true; do sleep 60; done\'',
|
||||
readiness={"type": "file", "path": ".ready"},
|
||||
)
|
||||
manager.stop_service(workspace_id, "app")
|
||||
|
||||
summary = manager.summarize_workspace(workspace_id)
|
||||
|
||||
assert summary["workspace_id"] == workspace_id
|
||||
assert summary["name"] == "review-eval"
|
||||
assert summary["labels"] == {"suite": "smoke"}
|
||||
assert summary["outcome"]["command_count"] == 1
|
||||
assert summary["outcome"]["export_count"] == 1
|
||||
assert summary["outcome"]["snapshot_count"] == 1
|
||||
assert summary["commands"]["total"] == 1
|
||||
assert summary["commands"]["recent"][0]["command"] == "cat note.txt"
|
||||
assert [event["event_kind"] for event in summary["edits"]["recent"]] == [
|
||||
"patch_apply",
|
||||
"file_write",
|
||||
"sync_push",
|
||||
]
|
||||
assert summary["changes"]["available"] is True
|
||||
assert summary["changes"]["changed"] is True
|
||||
assert summary["changes"]["summary"]["total"] == 4
|
||||
assert summary["services"]["current"][0]["service_name"] == "app"
|
||||
assert [event["event_kind"] for event in summary["services"]["recent"]] == [
|
||||
"service_stop",
|
||||
"service_start",
|
||||
]
|
||||
assert summary["artifacts"]["exports"][0]["workspace_path"] == "/workspace/note.txt"
|
||||
assert summary["snapshots"]["named_count"] == 1
|
||||
assert summary["snapshots"]["recent"][0]["snapshot_name"] == "checkpoint"
|
||||
|
||||
|
||||
def test_workspace_summary_degrades_gracefully_for_stopped_and_legacy_workspaces(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
seed_dir = tmp_path / "seed"
|
||||
seed_dir.mkdir()
|
||||
(seed_dir / "note.txt").write_text("hello\n", encoding="utf-8")
|
||||
|
||||
manager = VmManager(
|
||||
backend_name="mock",
|
||||
base_dir=tmp_path / "vms",
|
||||
network_manager=TapNetworkManager(enabled=False),
|
||||
)
|
||||
|
||||
stopped_workspace_id = str(
|
||||
manager.create_workspace(
|
||||
environment="debian:12-base",
|
||||
allow_host_compat=True,
|
||||
seed_path=seed_dir,
|
||||
)["workspace_id"]
|
||||
)
|
||||
manager.exec_workspace(stopped_workspace_id, command="cat note.txt", timeout_seconds=30)
|
||||
manager.stop_workspace(stopped_workspace_id)
|
||||
stopped_summary = manager.summarize_workspace(stopped_workspace_id)
|
||||
assert stopped_summary["commands"]["total"] == 1
|
||||
assert stopped_summary["changes"]["available"] is False
|
||||
assert "must be in 'started' state" in str(stopped_summary["changes"]["reason"])
|
||||
|
||||
legacy_workspace_id = str(
|
||||
manager.create_workspace(
|
||||
environment="debian:12-base",
|
||||
allow_host_compat=True,
|
||||
seed_path=seed_dir,
|
||||
)["workspace_id"]
|
||||
)
|
||||
baseline_path = (
|
||||
tmp_path / "vms" / "workspaces" / legacy_workspace_id / "baseline" / "workspace.tar"
|
||||
)
|
||||
baseline_path.unlink()
|
||||
legacy_summary = manager.summarize_workspace(legacy_workspace_id)
|
||||
assert legacy_summary["changes"]["available"] is False
|
||||
assert "baseline snapshot" in str(legacy_summary["changes"]["reason"])
|
||||
|
||||
|
||||
def test_workspace_file_ops_and_patch_round_trip(tmp_path: Path) -> None:
|
||||
seed_dir = tmp_path / "seed"
|
||||
seed_dir.mkdir()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue