glove/tests/control/linux_session_executor_test.cpp
0xkite 5c48e5fd8a
fix(control): report start replay disposition, hard-cap jsonl audit journal, deterministic control tests
MEDIUM 1: session_runtime::start() now returns an explicit
fresh-launch vs replay disposition (session_start_result), and
handle_start_session sets outcome.applied only for a fresh committed
launch. An authenticated idempotent start replay therefore never gains
connection-scoped teardown authority: a broken pipe during a replay can
no longer stop an already-running guest. The control audit event for a
replayed delivery carries the replay disposition
(start_session:replay:<session>).

MEDIUM 2: jsonl_sink truncation now accounts for record delimiters
exactly, so current_bytes_ always tracks the physical file; only
newline-terminated complete records are retained (no blank or partial
lines, bounded read window); the cap is a hard cap (an oversized event
is dropped with a structured operator note; a pre-existing oversized
journal is bounded with a bounded read at construction).

MEDIUM 3: the broken-pipe degrade test now synchronizes through a
mutex-protected condition-variable gate handshake that proves the
dispatch is parked inside the blocked gate before the client resets,
records the start only after the dispatch completes, and the
mark_stopping test destroys every registry lock holder (runtime,
protocol, registry) before the independent registry reopen so the
nonblocking exclusive flock can actually be acquired.
2026-08-31 09:34:45 -06:00

1304 lines
56 KiB
C++

#include "glove/container/digest.hpp"
#include "glove/container/receipt_producer.hpp"
#include "glove/control/receipt_audit_protocol.hpp"
#include "glove/control/session_registry.hpp"
#include "glove/supervisor/library_bundle.hpp"
#include "glove/supervisor/path_alias.hpp"
#include "glove/supervisor/session_plan.hpp"
#include "linux_session_executor.hpp"
#include "linux_session_preparation.hpp"
#include "receipt_audit_wire.hpp"
#include <glaze/glaze.hpp>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <array>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <filesystem>
#include <fstream>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <system_error>
#include <utility>
#include <vector>
namespace glove_test {
#define REQUIRE(cond) \
do { \
if (!(cond)) { \
std::fprintf(stderr, "REQUIRE failed: %s @ %s:%d\n", #cond, __FILE__, __LINE__); \
return 1; \
} \
} while (0)
constexpr std::string_view controller_digest =
"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc";
constexpr std::string_view audit_key =
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f";
constexpr std::string_view bootstrap_secret =
"dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd";
struct rpc_error {
std::string code;
std::string message;
};
struct rpc_response {
std::string jsonrpc;
std::string id;
std::optional<glz::raw_json> result;
std::optional<rpc_error> error;
};
struct session_control_capabilities {
bool validate_plan = false;
bool create_session = false;
bool start_session = false;
bool session_status = false;
bool attach = false;
bool resize = false;
bool write_stdin = false;
bool signal = false;
bool detach = false;
bool stop_session = false;
bool cleanup_session = false;
};
struct resource_enforcement_capabilities {
std::string cpu_time;
std::string memory;
std::string pids;
std::string wall_time;
std::string disk;
std::string terminal_output;
std::uint8_t receipt_schema_version = 0;
};
struct backend_capabilities {
std::string backend;
resource_enforcement_capabilities resource_enforcement;
};
struct supervisor_capabilities {
std::uint8_t schema_version = 0;
glz::raw_json receipt_audit;
session_control_capabilities session_control;
std::uint8_t agent_runtime_adapter_schema_version = 0;
std::vector<std::string> managed_runtime_ids;
std::uint8_t path_exposure_admin_schema_version = 0;
std::uint8_t path_exposure_catalog_schema_version = 0;
std::uint8_t retained_write_schema_version = 0;
std::uint8_t change_manifest_schema_version = 0;
std::uint8_t change_apply_authorization_schema_version = 0;
std::uint8_t refinement_evaluation_protocol_schema_version = 0;
std::uint8_t observation_intent_channel_schema_version = 0;
std::vector<backend_capabilities> backends;
};
struct session_record_result {
std::uint8_t schema_version = 0;
std::string session_id;
std::string controller_plan_digest;
std::string plan_content_digest;
std::string state;
std::uint64_t policy_revision = 0;
std::uint64_t expires_at_ms = 0;
std::uint64_t created_at_ms = 0;
std::optional<std::string> profile_digest;
};
struct transcript_result {
std::uint8_t schema_version = 0;
std::string session_id;
std::uint64_t oldest_cursor = 0;
std::uint64_t next_cursor = 0;
bool truncated = false;
bool eof = false;
std::vector<std::uint8_t> bytes;
};
struct session_mutation_result {
std::uint8_t schema_version = 0;
std::string session_id;
};
struct session_cursor_result {
std::uint8_t schema_version = 0;
std::string session_id;
std::uint64_t transcript_cursor = 0;
};
auto decode_response(std::string_view frame) -> std::optional<rpc_response> {
rpc_response response;
constexpr glz::opts strict{.error_on_unknown_keys = true};
if (glz::read<strict>(response, frame)) {
return std::nullopt;
}
return response;
}
auto make_request(
std::string_view id,
std::string_view method,
std::string_view payload,
std::optional<std::string_view> idempotency_key,
std::uint64_t deadline_ms
) -> std::string {
std::string request =
"{\"jsonrpc\":\"2.0\",\"id\":\"" + std::string{id} + "\",\"method\":\"" +
std::string{method} + "\",\"params\":{\"schema_version\":1,\"bootstrap_secret\":\"" +
std::string{bootstrap_secret} + "\",\"deadline_ms\":" + std::to_string(deadline_ms);
if (idempotency_key) {
request += ",\"idempotency_key\":\"" + std::string{*idempotency_key} + "\"";
}
request += ",\"payload\":" + std::string{payload} + "}}";
return request;
}
auto interactive_runtime_digest() -> std::string {
auto digest = glove::supervisor::runtime_launch_template_digest({
.runtime_discovery = {},
.executable_path = "/usr/bin/cat",
.executable_search_paths = {},
.arguments = {},
.environment = {"PATH=/usr/bin:/bin", "TERM=xterm-256color"},
.read_only_paths = {},
});
return digest.value_or(std::string{});
}
auto safe_runtime_digest() -> std::string {
auto digest = glove::supervisor::runtime_launch_template_digest({
.runtime_discovery = {},
.executable_path = "/usr/bin/true",
.executable_search_paths = {},
.arguments = {"--version"},
.environment = {"PATH=/usr/bin:/bin", "TERM=xterm-256color"},
.read_only_paths = {},
});
return digest.value_or(std::string{});
}
auto refinement_runtime_digest() -> std::string {
auto digest = glove::supervisor::runtime_launch_template_digest({
.runtime_discovery = {},
.executable_path = "/usr/bin/sh",
.executable_search_paths = {},
.arguments = {"-c", "printf 'alpha beta done'"},
.environment = {"PATH=/usr/bin:/bin", "TERM=xterm-256color"},
.read_only_paths = {},
});
return digest.value_or(std::string{});
}
class temporary_tree {
public:
temporary_tree() {
std::string pattern = "/tmp/glove-linux-executor-test-XXXXXX";
if (char* created = ::mkdtemp(pattern.data()); created != nullptr) {
root_ = created;
}
}
temporary_tree(const temporary_tree&) = delete;
auto operator=(const temporary_tree&) -> temporary_tree& = delete;
~temporary_tree() {
std::error_code error;
std::filesystem::remove_all(root_, error);
}
[[nodiscard]] auto root() const -> const std::filesystem::path& { return root_; }
private:
std::filesystem::path root_;
};
auto epoch_ms() -> std::uint64_t {
const auto now = std::chrono::system_clock::now().time_since_epoch();
return static_cast<std::uint64_t>(
std::chrono::duration_cast<std::chrono::milliseconds>(now).count()
);
}
auto validator_for(const std::filesystem::path& source, std::uint64_t page)
-> glove::supervisor::result<glove::supervisor::session_plan_validator> {
using namespace glove::supervisor;
auto paths = path_alias_registry::build({
path_alias_policy{
.alias = "workspace",
.host_path = std::filesystem::canonical(source).string(),
.target_path = "/workspace",
.max_ttl_secs = 120,
.access = {
path_access_policy{
.access = path_access::ephemeral_write,
.materialization = path_materialization::copy,
.create_policy = path_create_policy::empty_directory,
.cleanup_policy = path_cleanup_policy::remove,
.max_bytes = page * 4U,
},
},
},
});
if (!paths) {
return std::unexpected(paths.error());
}
const auto safe_digest = safe_runtime_digest();
const auto interactive_digest = interactive_runtime_digest();
const auto refinement_digest = refinement_runtime_digest();
if (safe_digest.empty() || interactive_digest.empty() || refinement_digest.empty()) {
return std::unexpected(std::string{"derive runtime digest"});
}
return session_plan_validator::build(
session_plan_policy{
.revision = 7,
.max_plan_ttl_ms = 120'000,
.runtime_templates =
{
runtime_template_policy{
.runtime_template_id = "codex-safe",
.runtime_id = "codex",
.adapter_command_digest = safe_digest,
.backend = sandbox_backend::linux_production,
.allowed_path_aliases = {"workspace"},
.allowed_projection_destinations = {"libraries"},
.launch =
runtime_launch_template{
.runtime_discovery = {},
.executable_path = "/usr/bin/true",
.executable_search_paths = {},
.arguments = {"--version"},
.environment = {"PATH=/usr/bin:/bin", "TERM=xterm-256color"},
.read_only_paths = {},
},
.adoption = std::nullopt,
},
runtime_template_policy{
.runtime_template_id = "codex-interactive",
.runtime_id = "codex",
.adapter_command_digest = interactive_digest,
.backend = sandbox_backend::linux_production,
.allowed_path_aliases = {"workspace"},
.allowed_projection_destinations = {"libraries"},
.launch =
runtime_launch_template{
.runtime_discovery = {},
.executable_path = "/usr/bin/cat",
.executable_search_paths = {},
.arguments = {},
.environment = {"PATH=/usr/bin:/bin", "TERM=xterm-256color"},
.read_only_paths = {},
},
.adoption = std::nullopt,
},
runtime_template_policy{
.runtime_template_id = "refinement-eval-v1",
.runtime_id = "refinement-harness",
.adapter_command_digest = refinement_digest,
.backend = sandbox_backend::linux_production,
.allowed_path_aliases = {"workspace"},
.allowed_projection_destinations = {"fixtures", "skills"},
.launch =
runtime_launch_template{
.runtime_discovery = {},
.executable_path = "/usr/bin/sh",
.executable_search_paths = {},
.arguments = {"-c", "printf 'alpha beta done'"},
.environment = {"PATH=/usr/bin:/bin", "TERM=xterm-256color"},
.read_only_paths = {},
},
.adoption = std::nullopt,
},
},
.library_projection_destinations =
{
library_projection_destination_policy{
.alias = "libraries",
.target_path = "/opt/sage/library-bundles",
},
library_projection_destination_policy{
.alias = "fixtures",
.target_path = "/opt/glove/fixtures",
},
library_projection_destination_policy{
.alias = "skills",
.target_path = "/opt/glove/refinement-skills",
},
},
.resource_profiles =
{
resource_limits{
.cpu_time_ms = 10'000,
.memory_bytes = std::uint64_t{128} * 1024U * 1024U,
.pids = 16,
.wall_time_ms = 5'000,
.disk_bytes = page * 16U,
.terminal_output_bytes = std::uint64_t{1024} * 1024U,
},
},
.egress_policy_ids = {"no-network"},
.tool_policy_ids = {"sage-readonly"},
.secret_handles = {},
.egress_policies = {},
.secret_mounts = {},
},
std::move(*paths)
);
}
auto plan(
std::uint64_t now_ms, std::uint64_t page, std::string_view runtime_template_id = "codex-safe"
) -> std::string {
const auto adapter_digest = runtime_template_id == "codex-interactive"
? interactive_runtime_digest()
: safe_runtime_digest();
return std::string{"{\"schema_version\":1,\"runtime_id\":\"codex\","} +
"\"runtime_template_id\":\"" + std::string{runtime_template_id} +
"\",\"adapter_command_digest\":\"" + adapter_digest +
"\",\"sandbox_backend\":\"linux_production\",\"egress_policy_id\":\"no-network\"," +
"\"tool_policy_id\":\"sage-readonly\",\"path_grants\":[{\"alias\":\"workspace\"," +
"\"access\":\"ephemeral_write\",\"materialization\":\"copy\",\"max_bytes\":" +
std::to_string(page * 4U) +
",\"ttl_secs\":60,\"cleanup_policy\":\"remove\"}],\"library_projections\":[]," +
"\"secret_handles\":[],\"limits\":{\"cpu_time_ms\":10000,\"memory_bytes\":134217728," +
"\"pids\":16,\"wall_time_ms\":5000,\"disk_bytes\":" + std::to_string(page * 16U) +
",\"terminal_output_bytes\":1048576},\"policy_revision\":7,\"expires_at_ms\":" +
std::to_string(now_ms + 120'000) + "}";
}
auto refinement_plan(
std::uint64_t now_ms,
std::uint64_t page,
std::string_view fixture_digest,
std::string_view candidate_digest,
std::string_view matched_context_digest
) -> std::string {
return std::string{
R"({"schema_version":1,"runtime_id":"refinement-harness","runtime_template_id":"refinement-eval-v1","adapter_command_digest":")"
} +
refinement_runtime_digest() +
R"(","sandbox_backend":"linux_production","egress_policy_id":"no-network","tool_policy_id":"sage-readonly","path_grants":[],"library_projections":[{"projection_id":"candidate","content_digest":")" +
std::string{candidate_digest} +
R"(","destination_alias":"skills"},{"projection_id":"fixture","content_digest":")" +
std::string{fixture_digest} +
R"(","destination_alias":"fixtures"}],"secret_handles":[],"limits":{"cpu_time_ms":10000,"memory_bytes":134217728,"pids":16,"wall_time_ms":5000,"disk_bytes":)" +
std::to_string(page * 16U) +
R"(,"terminal_output_bytes":1048576},"policy_revision":7,"expires_at_ms":)" +
std::to_string(now_ms + 120'000) +
R"(,"refinement":{"schema_version":1,"variant":"candidate","fixture":{"projection_id":"fixture","content_digest":")" +
std::string{fixture_digest} +
R"(","destination_alias":"fixtures"},"base":{"projection_id":"base","content_digest":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","destination_alias":"skills"},"candidate":{"projection_id":"candidate","content_digest":")" +
std::string{candidate_digest} +
R"(","destination_alias":"skills"},"matched_context_digest":")" +
std::string{matched_context_digest} + R"("}})";
}
auto run() -> int {
temporary_tree tree;
REQUIRE(!tree.root().empty());
const auto source = tree.root() / "source";
const auto materializations = tree.root() / "materializations";
REQUIRE(std::filesystem::create_directory(source));
REQUIRE(std::filesystem::create_directory(materializations));
REQUIRE(::chmod(materializations.c_str(), 0700) == 0);
std::ofstream{source / "tracked.txt"} << "host-owned\n";
const long page_size = ::sysconf(_SC_PAGESIZE);
REQUIRE(page_size > 0);
const auto page = static_cast<std::uint64_t>(page_size);
const auto now_ms = epoch_ms();
auto validator = validator_for(source, page);
if (!validator) {
std::fprintf(stderr, "validator unavailable: %s\n", validator.error().c_str());
}
REQUIRE(validator.has_value());
auto shared_validator =
std::make_shared<const glove::supervisor::session_plan_validator>(std::move(*validator));
auto registry = glove::control::session_registry::open_or_create(
tree.root() / "sessions.journal", shared_validator
);
REQUIRE(registry.has_value());
auto created = (*registry)->create(
"session-executor", controller_digest, plan(now_ms, page), "create-session-executor", now_ms
);
REQUIRE(created.has_value());
const glove::control::session_start_authorization authorization{
.schema_version = 1,
.authorization_id = "approval-session-executor",
.session_id = created->session_id,
.controller_plan_digest = created->controller_plan_digest,
.plan_content_digest = created->plan_content_digest,
.approved_at_ms = now_ms,
.expires_at_ms = now_ms + 30'000U,
};
REQUIRE((*registry)
->reserve_start(authorization, "reserve-session-executor", now_ms + 1U)
.has_value());
const auto key_path = tree.root() / "receipt.key";
{
std::ofstream output{key_path, std::ios::binary | std::ios::trunc};
output << audit_key << '\n';
output.flush();
REQUIRE(output.good());
}
REQUIRE(::chmod(key_path.c_str(), 0600) == 0);
auto producer = glove::container::receipt_audit_producer::initialize({
.key_path = key_path,
.journal_path = tree.root() / "receipts.journal",
});
REQUIRE(producer.has_value());
REQUIRE((*producer)->acknowledge_bootstrap((*producer)->anchor()).has_value());
auto preparer_result =
glove::control::linux_detail::linux_session_preparer::create(materializations.string());
if (!preparer_result) {
std::fprintf(
stderr, "Linux executor topology unavailable: %s\n", preparer_result.error().c_str()
);
return 77;
}
auto preparer = std::make_shared<glove::control::linux_detail::linux_session_preparer>(
std::move(*preparer_result)
);
auto exited = glove::control::linux_detail::execute_linux_session(
**registry,
*preparer,
**producer,
{
.session_id = created->session_id,
.authorization_id = authorization.authorization_id,
.idempotency_namespace = "execute-session-executor",
}
);
if (!exited) {
std::fprintf(stderr, "execute Linux session: %s\n", exited.error().c_str());
}
REQUIRE(exited.has_value());
REQUIRE(exited->session.state == glove::control::session_state::exited);
REQUIRE(exited->session.session_id == created->session_id);
REQUIRE(exited->receipt_sequence == 1);
REQUIRE(exited->termination_cause == glove::container::resource_termination_cause::exited);
REQUIRE(exited->exit_code == 0);
REQUIRE(exited->filesystem_identity.schema_version == 1);
REQUIRE(exited->filesystem_identity.disk_limit_bytes == page * 16U);
REQUIRE(exited->filesystem_identity.partitions.size() == 1);
REQUIRE(exited->filesystem_identity.partitions.front().alias == "workspace");
REQUIRE(exited->filesystem_identity.partitions.front().quota_bytes == page * 4U);
auto durable_exited = (*registry)->exited_status(created->session_id);
REQUIRE(durable_exited.has_value());
REQUIRE(*durable_exited == *exited);
REQUIRE((*producer)->anchor().sequence == 1);
REQUIRE(std::filesystem::is_empty(materializations));
REQUIRE(std::filesystem::exists(source / "tracked.txt"));
const auto crash_pid = ::fork();
REQUIRE(crash_pid >= 0);
if (crash_pid == 0) {
const auto crash_now_ms = epoch_ms();
auto crash_created = (*registry)->create(
"session-starting-crash",
controller_digest,
plan(crash_now_ms, page),
"create-session-starting-crash",
crash_now_ms
);
if (!crash_created) {
::_exit(11);
}
const glove::control::session_start_authorization crash_authorization{
.schema_version = 1,
.authorization_id = "approval-session-starting-crash",
.session_id = crash_created->session_id,
.controller_plan_digest = crash_created->controller_plan_digest,
.plan_content_digest = crash_created->plan_content_digest,
.approved_at_ms = crash_now_ms,
.expires_at_ms = crash_now_ms + 30'000U,
};
if (!(*registry)
->reserve_start(
crash_authorization, "reserve-session-starting-crash", crash_now_ms + 1U
)
.has_value()) {
::_exit(12);
}
auto crash_inputs = (*registry)->resolve_start_inputs(
crash_created->session_id, crash_authorization.authorization_id, crash_now_ms + 2U
);
if (!crash_inputs) {
::_exit(13);
}
auto crash_prepared = preparer->prepare(std::move(*crash_inputs), crash_now_ms + 2U);
if (!crash_prepared) {
::_exit(14);
}
auto crash_receipt_reservation = (*producer)->reserve_terminal(
crash_prepared->session_id,
crash_prepared->controller_plan_digest,
crash_prepared->binding.profile_digest
);
if (!crash_receipt_reservation) {
::_exit(15);
}
if (!(*registry)
->mark_starting(
crash_prepared->execution_binding(),
*crash_receipt_reservation,
"starting-session-starting-crash",
crash_now_ms + 3U
)
.has_value()) {
::_exit(16);
}
::_exit(0);
}
int crash_status = 0;
REQUIRE(::waitpid(crash_pid, &crash_status, 0) == crash_pid);
REQUIRE(WIFEXITED(crash_status));
REQUIRE(WEXITSTATUS(crash_status) == 0);
REQUIRE(!std::filesystem::is_empty(materializations));
registry->reset();
auto recovered_registry_result = glove::control::session_registry::open_or_create(
tree.root() / "sessions.journal", shared_validator
);
REQUIRE(recovered_registry_result.has_value());
auto shared_registry =
std::shared_ptr<glove::control::session_registry>{std::move(*recovered_registry_result)};
REQUIRE(!glove::control::linux_detail::linux_session_runtime::create({}, preparer, {}, 1)
.has_value());
REQUIRE(!glove::control::linux_detail::linux_session_runtime::create(shared_registry, {}, {}, 1)
.has_value());
REQUIRE(!glove::control::linux_detail::linux_session_runtime::create(
shared_registry, preparer, {}, 0
)
.has_value());
auto session_runtime_result = glove::control::linux_detail::linux_session_runtime::create(
shared_registry,
preparer,
{
.transcript_bytes = page,
.max_read_bytes = page,
.max_input_frame_bytes = page,
.input_timeout_ms = 1'000,
.initial_rows = 24,
.initial_columns = 80,
.refinement_evaluator = nullptr,
},
1
);
REQUIRE(session_runtime_result.has_value());
REQUIRE(!(*session_runtime_result)
->start(**producer, authorization, "unreconciled-start", epoch_ms())
.has_value());
auto reconciliation = (*session_runtime_result)->reconcile(**producer, epoch_ms() + 1U);
REQUIRE(reconciliation.has_value());
REQUIRE(reconciliation->inspected == 1);
REQUIRE(reconciliation->recovered_failed == 1);
REQUIRE(reconciliation->recovered_exited == 0);
REQUIRE(reconciliation->recovered_terminated == 0);
REQUIRE((*session_runtime_result)->reconcile(**producer, epoch_ms() + 2U) == reconciliation);
// Release this runtime (and its registry reference) before the
// interactive section; every registry lock holder must be gone before
// any later independent registry reopen below.
(*session_runtime_result).reset();
auto recovered_failure = shared_registry->failed_status("session-starting-crash");
REQUIRE(recovered_failure.has_value());
REQUIRE(
recovered_failure->code == glove::control::session_failure_code::recovered_without_process
);
REQUIRE(!recovered_failure->process_identity);
REQUIRE(recovered_failure->cgroup_identity.has_value());
REQUIRE(recovered_failure->filesystem_identity.has_value());
REQUIRE(std::filesystem::is_empty(materializations));
REQUIRE(std::filesystem::exists(source / "tracked.txt"));
auto shared_producer =
std::shared_ptr<glove::container::receipt_audit_producer>{std::move(*producer)};
const auto interactive_now_ms = epoch_ms();
auto interactive_created = shared_registry->create(
"session-interactive",
controller_digest,
plan(interactive_now_ms, page, "codex-interactive"),
"create-session-interactive",
interactive_now_ms
);
REQUIRE(interactive_created.has_value());
const glove::control::session_start_authorization interactive_authorization{
.schema_version = 1,
.authorization_id = "approval-session-interactive",
.session_id = interactive_created->session_id,
.controller_plan_digest = interactive_created->controller_plan_digest,
.plan_content_digest = interactive_created->plan_content_digest,
.approved_at_ms = interactive_now_ms,
.expires_at_ms = interactive_now_ms + 30'000U,
};
auto wire_runtime_result = glove::control::linux_detail::linux_session_runtime::create(
shared_registry,
preparer,
{
.transcript_bytes = page,
.max_read_bytes = page,
.max_input_frame_bytes = page,
.input_timeout_ms = 1'000,
.initial_rows = 24,
.initial_columns = 80,
.refinement_evaluator = nullptr,
},
1
);
REQUIRE(wire_runtime_result.has_value());
auto session_runtime = std::shared_ptr<glove::control::linux_detail::linux_session_runtime>{
std::move(*wire_runtime_result)
};
auto protocol = glove::control::receipt_audit_protocol::create(
bootstrap_secret,
shared_producer,
shared_validator,
shared_registry,
session_runtime,
{},
materializations.string()
);
REQUIRE(protocol.has_value());
auto capabilities_frame = (*protocol)->handle_frame(
make_request(
"runtime-capabilities",
"capabilities",
"null",
std::nullopt,
interactive_now_ms + 10'000U
),
interactive_now_ms
);
REQUIRE(capabilities_frame.has_value());
auto capabilities_response = decode_response(*capabilities_frame);
REQUIRE(capabilities_response.has_value());
REQUIRE(capabilities_response->result.has_value());
supervisor_capabilities capabilities;
REQUIRE(!glz::read<glz::opts{.error_on_unknown_keys = true}>(
capabilities, capabilities_response->result->str
));
REQUIRE(capabilities.schema_version == 1);
REQUIRE(capabilities.session_control.start_session);
REQUIRE(capabilities.session_control.stop_session);
REQUIRE(capabilities.session_control.attach);
REQUIRE(capabilities.session_control.resize);
REQUIRE(capabilities.session_control.write_stdin);
REQUIRE(capabilities.session_control.signal);
REQUIRE(capabilities.session_control.detach);
REQUIRE(capabilities.session_control.cleanup_session);
REQUIRE(capabilities.agent_runtime_adapter_schema_version == 1);
REQUIRE(
capabilities.managed_runtime_ids ==
std::vector<std::string>({"codex", "claude-code", "pi", "copilot", "opencode"})
);
REQUIRE(capabilities.path_exposure_admin_schema_version == 0);
REQUIRE(capabilities.path_exposure_catalog_schema_version == 0);
REQUIRE(capabilities.retained_write_schema_version == 0);
REQUIRE(capabilities.change_manifest_schema_version == 0);
REQUIRE(capabilities.change_apply_authorization_schema_version == 0);
REQUIRE(capabilities.refinement_evaluation_protocol_schema_version == 0);
REQUIRE(capabilities.backends.size() == 2);
REQUIRE(capabilities.backends[0].backend == "linux_production");
REQUIRE(capabilities.backends[0].resource_enforcement.cpu_time == "cgroup_v2");
REQUIRE(capabilities.backends[0].resource_enforcement.memory == "cgroup_v2");
REQUIRE(capabilities.backends[0].resource_enforcement.pids == "cgroup_v2");
REQUIRE(capabilities.backends[0].resource_enforcement.wall_time == "watchdog");
REQUIRE(capabilities.backends[0].resource_enforcement.disk == "filesystem_quota");
REQUIRE(capabilities.backends[0].resource_enforcement.terminal_output == "byte_counter");
REQUIRE(capabilities.backends[0].resource_enforcement.receipt_schema_version == 1);
REQUIRE(capabilities.backends[1].backend == "apple_container");
REQUIRE(capabilities.backends[1].resource_enforcement.cpu_time == "unavailable");
auto authorization_json = glz::write_json(interactive_authorization);
REQUIRE(authorization_json.has_value());
const auto start_payload = "{\"authorization\":" + *authorization_json + "}";
auto missing_start_idempotency = (*protocol)->handle_frame(
make_request(
"start-interactive-without-key",
"start_session",
start_payload,
std::nullopt,
interactive_now_ms + 10'000U
),
interactive_now_ms + 1U
);
REQUIRE(missing_start_idempotency.has_value());
auto missing_start_response = decode_response(*missing_start_idempotency);
REQUIRE(missing_start_response.has_value());
REQUIRE(missing_start_response->error.has_value());
REQUIRE(missing_start_response->error->code == "invalid_request");
REQUIRE(session_runtime->list()->empty());
auto start_frame = (*protocol)->handle_frame(
make_request(
"start-interactive",
"start_session",
start_payload,
"execute-session-interactive",
interactive_now_ms + 10'000U
),
interactive_now_ms + 1U
);
REQUIRE(start_frame.has_value());
auto start_response = decode_response(*start_frame);
REQUIRE(start_response.has_value());
REQUIRE(start_response->result.has_value());
session_record_result interactive_running;
REQUIRE(!glz::read<glz::opts{.error_on_unknown_keys = true}>(
interactive_running, start_response->result->str
));
REQUIRE(interactive_running.session_id == interactive_created->session_id);
REQUIRE(interactive_running.state == "running");
auto interactive_running_status =
shared_registry->running_status(interactive_created->session_id);
REQUIRE(interactive_running_status.has_value());
REQUIRE(
interactive_running.profile_digest ==
std::optional<std::string>{interactive_running_status->profile_digest}
);
auto start_replay_frame = (*protocol)->handle_frame(
make_request(
"start-interactive-replay",
"start_session",
start_payload,
"execute-session-interactive",
interactive_now_ms + 10'000U
),
interactive_now_ms + 2U
);
REQUIRE(start_replay_frame.has_value());
auto start_replay_response = decode_response(*start_replay_frame);
REQUIRE(start_replay_response.has_value());
REQUIRE(start_replay_response->result.has_value());
REQUIRE(start_replay_response->result->str == start_response->result->str);
REQUIRE(session_runtime->list() == std::vector<std::string>{"session-interactive"});
REQUIRE(!session_runtime->cleanup(interactive_created->session_id).has_value());
const std::string input_text = "interactive control\n";
const std::vector<std::uint8_t> input_bytes{input_text.begin(), input_text.end()};
auto input_json = glz::write_json(input_bytes);
REQUIRE(input_json.has_value());
const auto input_payload = "{\"session_id\":\"" + interactive_created->session_id +
"\",\"bytes\":" + *input_json + "}";
auto input_frame = (*protocol)->handle_frame(
make_request(
"input-interactive",
"write_stdin",
input_payload,
"input-session-interactive",
interactive_now_ms + 10'000U
),
interactive_now_ms + 2U
);
REQUIRE(input_frame.has_value());
auto input_response = decode_response(*input_frame);
REQUIRE(input_response.has_value());
REQUIRE(input_response->result.has_value());
session_mutation_result input_result;
REQUIRE(!glz::read<glz::opts{.error_on_unknown_keys = true}>(
input_result, input_response->result->str
));
REQUIRE(input_result.schema_version == 1);
REQUIRE(input_result.session_id == interactive_created->session_id);
auto input_replay_frame = (*protocol)->handle_frame(
make_request(
"input-interactive-replay",
"write_stdin",
input_payload,
"input-session-interactive",
interactive_now_ms + 10'000U
),
interactive_now_ms + 3U
);
REQUIRE(input_replay_frame.has_value());
auto input_replay_response = decode_response(*input_replay_frame);
REQUIRE(input_replay_response.has_value());
REQUIRE(input_replay_response->result.has_value());
REQUIRE(input_replay_response->result->str == input_response->result->str);
const auto conflicting_input_payload =
"{\"session_id\":\"" + interactive_created->session_id + "\",\"bytes\":[120]}";
auto input_conflict_frame = (*protocol)->handle_frame(
make_request(
"input-interactive-conflict",
"write_stdin",
conflicting_input_payload,
"input-session-interactive",
interactive_now_ms + 10'000U
),
interactive_now_ms + 4U
);
REQUIRE(input_conflict_frame.has_value());
auto input_conflict_response = decode_response(*input_conflict_frame);
REQUIRE(input_conflict_response.has_value());
REQUIRE(input_conflict_response->error.has_value());
REQUIRE(input_conflict_response->error->code == "idempotency_conflict");
auto interactive_output =
session_runtime->wait_read(interactive_created->session_id, 0, page, 1'000);
REQUIRE(interactive_output.has_value());
REQUIRE(interactive_output->bytes.find("interactive control") != std::string::npos);
const auto attach_payload = "{\"session_id\":\"" + interactive_created->session_id +
"\",\"cursor\":0,\"max_bytes\":" + std::to_string(page) + "}";
auto attach_frame = (*protocol)->handle_frame(
make_request(
"attach-interactive",
"attach",
attach_payload,
std::nullopt,
interactive_now_ms + 10'000U
),
interactive_now_ms + 5U
);
REQUIRE(attach_frame.has_value());
auto attach_response = decode_response(*attach_frame);
REQUIRE(attach_response.has_value());
REQUIRE(attach_response->result.has_value());
transcript_result attached;
REQUIRE(
!glz::read<glz::opts{.error_on_unknown_keys = true}>(attached, attach_response->result->str)
);
REQUIRE(attached.schema_version == 1);
REQUIRE(attached.session_id == interactive_created->session_id);
REQUIRE(attached.oldest_cursor == interactive_output->oldest_cursor);
REQUIRE(attached.next_cursor >= attached.oldest_cursor);
REQUIRE(attached.next_cursor >= interactive_output->next_cursor);
REQUIRE(!attached.truncated);
const std::string attached_text{attached.bytes.begin(), attached.bytes.end()};
REQUIRE(attached_text.find("interactive control") != std::string::npos);
const auto detach_payload = "{\"session_id\":\"" + interactive_created->session_id +
"\",\"transcript_cursor\":" + std::to_string(attached.next_cursor) +
"}";
auto detach_frame = (*protocol)->handle_frame(
make_request(
"detach-interactive",
"detach",
detach_payload,
"detach-session-interactive",
interactive_now_ms + 10'000U
),
interactive_now_ms + 6U
);
REQUIRE(detach_frame.has_value());
auto detach_response = decode_response(*detach_frame);
REQUIRE(detach_response.has_value());
REQUIRE(detach_response->result.has_value());
session_cursor_result detached;
REQUIRE(
!glz::read<glz::opts{.error_on_unknown_keys = true}>(detached, detach_response->result->str)
);
REQUIRE(detached.schema_version == 1);
REQUIRE(detached.session_id == interactive_created->session_id);
REQUIRE(detached.transcript_cursor == attached.next_cursor);
const auto invalid_signal_payload =
"{\"session_id\":\"" + interactive_created->session_id + "\",\"signal\":\"kill\"}";
auto invalid_signal_frame = (*protocol)->handle_frame(
make_request(
"signal-interactive-invalid",
"signal",
invalid_signal_payload,
"signal-session-interactive-invalid",
interactive_now_ms + 10'000U
),
interactive_now_ms + 7U
);
REQUIRE(invalid_signal_frame.has_value());
auto invalid_signal_response = decode_response(*invalid_signal_frame);
REQUIRE(invalid_signal_response.has_value());
REQUIRE(invalid_signal_response->error.has_value());
REQUIRE(invalid_signal_response->error->code == "invalid_request");
const auto resize_payload =
"{\"session_id\":\"" + interactive_created->session_id + "\",\"rows\":50,\"columns\":140}";
auto resize_frame = (*protocol)->handle_frame(
make_request(
"resize-interactive",
"resize",
resize_payload,
"resize-session-interactive",
interactive_now_ms + 10'000U
),
interactive_now_ms + 6U
);
REQUIRE(resize_frame.has_value());
auto resize_response = decode_response(*resize_frame);
REQUIRE(resize_response.has_value());
REQUIRE(resize_response->result.has_value());
session_mutation_result resize_result;
REQUIRE(!glz::read<glz::opts{.error_on_unknown_keys = true}>(
resize_result, resize_response->result->str
));
REQUIRE(resize_result.schema_version == 1);
REQUIRE(resize_result.session_id == interactive_created->session_id);
auto resize_replay_frame = (*protocol)->handle_frame(
make_request(
"resize-interactive-replay",
"resize",
resize_payload,
"resize-session-interactive",
interactive_now_ms + 10'000U
),
interactive_now_ms + 7U
);
REQUIRE(resize_replay_frame.has_value());
auto resize_replay_response = decode_response(*resize_replay_frame);
REQUIRE(resize_replay_response.has_value());
REQUIRE(resize_replay_response->result.has_value());
REQUIRE(resize_replay_response->result->str == resize_response->result->str);
const auto stop_payload = "{\"session_id\":\"" + interactive_created->session_id + "\"}";
auto stop_frame = (*protocol)->handle_frame(
make_request(
"stop-interactive",
"stop_session",
stop_payload,
"stop-session-interactive",
interactive_now_ms + 10'000U
),
interactive_now_ms + 3U
);
REQUIRE(stop_frame.has_value());
auto stop_response = decode_response(*stop_frame);
REQUIRE(stop_response.has_value());
REQUIRE(stop_response->result.has_value());
session_record_result stop_record;
REQUIRE(!glz::read<glz::opts{.error_on_unknown_keys = true}>(
stop_record, stop_response->result->str
));
REQUIRE(stop_record.session_id == interactive_created->session_id);
REQUIRE(stop_record.state == "stopping" || stop_record.state == "exited");
REQUIRE(stop_record.profile_digest == interactive_running.profile_digest);
auto interactive_stopping = shared_registry->stopping_status(interactive_created->session_id);
std::uint64_t stopping_at_ms = 0;
if (interactive_stopping) {
REQUIRE(interactive_stopping->stopping_at_ms >= interactive_stopping->running_at_ms);
stopping_at_ms = interactive_stopping->stopping_at_ms;
} else {
auto early_exit = shared_registry->exited_status(interactive_created->session_id);
REQUIRE(early_exit.has_value());
REQUIRE(early_exit->stopping_at_ms >= early_exit->running_at_ms);
stopping_at_ms = early_exit->stopping_at_ms;
}
auto stop_replay_frame = (*protocol)->handle_frame(
make_request(
"stop-interactive-replay",
"stop_session",
stop_payload,
"stop-session-interactive",
interactive_now_ms + 10'000U
),
interactive_now_ms + 4U
);
REQUIRE(stop_replay_frame.has_value());
auto stop_replay_response = decode_response(*stop_replay_frame);
REQUIRE(stop_replay_response.has_value());
REQUIRE(stop_replay_response->result.has_value());
session_record_result stop_replay_record;
REQUIRE(!glz::read<glz::opts{.error_on_unknown_keys = true}>(
stop_replay_record, stop_replay_response->result->str
));
REQUIRE(stop_replay_record.session_id == interactive_created->session_id);
REQUIRE(stop_replay_record.state == "stopping" || stop_replay_record.state == "exited");
auto interactive_exited = session_runtime->wait(interactive_created->session_id);
REQUIRE(interactive_exited.has_value());
REQUIRE(interactive_exited->session.state == glove::control::session_state::exited);
REQUIRE(
interactive_exited->termination_cause ==
glove::container::resource_termination_cause::signaled
);
REQUIRE(interactive_exited->stopping_at_ms == stopping_at_ms);
auto durable_interactive = shared_registry->exited_status("session-interactive");
REQUIRE(durable_interactive.has_value());
REQUIRE(durable_interactive->session == interactive_exited->session);
REQUIRE(durable_interactive->profile_digest == interactive_exited->profile_digest);
REQUIRE(durable_interactive->finished_at_ms == interactive_exited->finished_at_ms);
REQUIRE(durable_interactive->receipt_digest == interactive_exited->receipt_digest);
REQUIRE(durable_interactive->termination_cause == interactive_exited->termination_cause);
// Degrade-path invariant (Workflow #218): the stop path durably marks the
// session stopping (linux_session_executor stop: mark_stopping before
// any child release) and the transition must survive an independent
// registry reopen. The control delivery degradation path tears a guest
// down through exactly this stop path after a broken control pipe, so
// the durable stopping ordering is asserted here, not inferred. The
// reopen is deferred until every registry lock holder (runtime, protocol,
// registry) has been destroyed: open_or_create takes a nonblocking
// exclusive flock, so a reopen while the runtime/protocol still hold
// shared registry ownership would fail instead of proving durability.
const auto cleanup_payload = "{\"session_id\":\"" + interactive_created->session_id + "\"}";
auto cleanup_frame = (*protocol)->handle_frame(
make_request(
"cleanup-interactive",
"cleanup_session",
cleanup_payload,
"cleanup-session-interactive",
interactive_now_ms + 10'000U
),
interactive_now_ms + 10U
);
REQUIRE(cleanup_frame.has_value());
auto cleanup_response = decode_response(*cleanup_frame);
REQUIRE(cleanup_response.has_value());
REQUIRE(cleanup_response->result.has_value());
session_mutation_result cleanup_result;
REQUIRE(!glz::read<glz::opts{.error_on_unknown_keys = true}>(
cleanup_result, cleanup_response->result->str
));
REQUIRE(cleanup_result.schema_version == 1);
REQUIRE(cleanup_result.session_id == interactive_created->session_id);
auto cleanup_replay_frame = (*protocol)->handle_frame(
make_request(
"cleanup-interactive-replay",
"cleanup_session",
cleanup_payload,
"cleanup-session-interactive",
interactive_now_ms + 10'000U
),
interactive_now_ms + 11U
);
REQUIRE(cleanup_replay_frame.has_value());
auto cleanup_replay_response = decode_response(*cleanup_replay_frame);
REQUIRE(cleanup_replay_response.has_value());
REQUIRE(cleanup_replay_response->result.has_value());
REQUIRE(cleanup_replay_response->result->str == cleanup_response->result->str);
REQUIRE(session_runtime->list()->empty());
REQUIRE(!session_runtime->stop(interactive_created->session_id).has_value());
REQUIRE(
session_runtime
->start(
*shared_producer,
interactive_authorization,
"execute-session-interactive",
interactive_now_ms + 3U
)
.value()
.record == interactive_exited->session
);
// The start after cleanup is a registry replay of the committed start
// (the reservation exists and the session is no longer preparing): the
// runtime must report the explicit replay disposition, not a fresh
// launch.
REQUIRE(!session_runtime
->start(
*shared_producer,
interactive_authorization,
"execute-session-interactive",
interactive_now_ms + 3U
)
.value()
.fresh_launch);
REQUIRE(shared_producer->anchor().sequence == 2);
REQUIRE(std::filesystem::is_empty(materializations));
REQUIRE(std::filesystem::exists(source / "tracked.txt"));
// Independent registry reopen: every registry lock holder (runtime,
// protocol, registry) is destroyed first so the nonblocking exclusive
// flock in open_or_create can be acquired and the durable journal is
// re-read from scratch.
(*protocol).reset();
session_runtime.reset();
shared_registry.reset();
auto replayed_registry_result = glove::control::session_registry::open_or_create(
tree.root() / "sessions.journal", shared_validator
);
REQUIRE(replayed_registry_result.has_value());
auto replayed_registry =
std::shared_ptr<glove::control::session_registry>{std::move(*replayed_registry_result)};
auto replayed_interactive = replayed_registry->exited_status("session-interactive");
REQUIRE(replayed_interactive.has_value());
REQUIRE(replayed_interactive->session == durable_interactive->session);
REQUIRE(replayed_interactive->stopping_at_ms >= replayed_interactive->running_at_ms);
REQUIRE(replayed_interactive->stopping_at_ms == stopping_at_ms);
const auto capability_bundle_root = tree.root() / "capability-bundles";
REQUIRE(std::filesystem::create_directory(capability_bundle_root));
REQUIRE(::chmod(capability_bundle_root.c_str(), 0700) == 0);
auto capability_store = glove::supervisor::library_bundle_store::open(capability_bundle_root);
REQUIRE(capability_store.has_value());
auto capability_registry = glove::control::session_registry::open_or_create(
tree.root() / "capability-sessions.journal",
shared_validator,
std::make_shared<const glove::supervisor::library_bundle_store>(
std::move(*capability_store)
)
);
REQUIRE(capability_registry.has_value());
auto capability_registry_shared =
std::shared_ptr<glove::control::session_registry>{std::move(*capability_registry)};
auto capability_runtime = glove::control::linux_detail::linux_session_runtime::create(
capability_registry_shared, preparer, {}, 1
);
REQUIRE(capability_runtime.has_value());
auto capability_runtime_shared =
std::shared_ptr<glove::control::linux_detail::linux_session_runtime>{
std::move(*capability_runtime)
};
REQUIRE(capability_runtime_shared->refinement_evaluation_protocol_schema_version() == 1);
auto capability_protocol = glove::control::receipt_audit_protocol::create(
bootstrap_secret,
shared_producer,
shared_validator,
capability_registry_shared,
capability_runtime_shared,
{},
materializations.string()
);
REQUIRE(capability_protocol.has_value());
auto refinement_capability_frame = (*capability_protocol)
->handle_frame(
make_request(
"refinement-runtime-capabilities",
"capabilities",
"null",
std::nullopt,
interactive_now_ms + 20'000U
),
interactive_now_ms + 12U
);
REQUIRE(refinement_capability_frame.has_value());
auto refinement_capability_response = decode_response(*refinement_capability_frame);
REQUIRE(refinement_capability_response.has_value());
REQUIRE(refinement_capability_response->result.has_value());
supervisor_capabilities refinement_capabilities;
REQUIRE(!glz::read<glz::opts{.error_on_unknown_keys = true}>(
refinement_capabilities, refinement_capability_response->result->str
));
REQUIRE(refinement_capabilities.refinement_evaluation_protocol_schema_version == 1);
REQUIRE(refinement_capabilities.observation_intent_channel_schema_version == 0);
const std::string candidate_bytes = R"({"candidate":"exact"})";
const auto candidate_digest = glove::container::sha256_hex(
std::span<const unsigned char>{
reinterpret_cast<const unsigned char*>(candidate_bytes.data()), candidate_bytes.size()
}
);
REQUIRE(candidate_digest.has_value());
const auto placeholder_plan = refinement_plan(
interactive_now_ms, page, std::string(64, 'f'), *candidate_digest, std::string(64, '0')
);
auto plan_context =
shared_validator->refinement_plan_context_digest_json(placeholder_plan, interactive_now_ms);
REQUIRE(plan_context.has_value());
glove::container::refinement_fixture_manifest fixture{
.schema = std::string{glove::container::refinement_fixture_schema},
.evaluation_run_id = "run-control",
.pair_id = "pair-control",
.session_id = "session-refinement",
.variant = glove::container::refinement_variant::candidate,
.proposal_digest = std::string(64, 'a'),
.base_projection_digest = std::string(64, 'b'),
.candidate_projection_digest = *candidate_digest,
.fixture_id = "fixture-control",
.fixture_digest = std::string(64, 'd'),
.dataset_ref = "fixture:control",
.dataset_fingerprint = std::string(64, 'e'),
.seed = 7,
.model =
{
.provider = "test",
.model_id = "synthetic",
.model_family = "synthetic",
.model_revision = std::nullopt,
.tier = "local",
.normalizer_version = 1,
},
.harness = "synthetic",
.module = std::nullopt,
.skill_projection_id = "candidate",
.skill_projection_digest = *candidate_digest,
.matched_context_digest = std::string(64, '0'),
.assertions = {
.expected_termination = glove::container::resource_termination_cause::exited,
.expected_exit_code = 0,
.required_transcript_literals = {"alpha beta", "done"},
.forbidden_transcript_literals = {"forbidden"},
.max_latency_ms = 2'000,
},
};
fixture.matched_context_digest =
glove::container::refinement_fixture_context_digest(fixture, *plan_context).value();
const auto fixture_bytes = glove::container::canonical_refinement_fixture_bytes(fixture);
REQUIRE(fixture_bytes.has_value());
const auto fixture_digest = glove::container::sha256_hex(
std::span<const unsigned char>{
reinterpret_cast<const unsigned char*>(fixture_bytes->data()), fixture_bytes->size()
}
);
REQUIRE(fixture_digest.has_value());
for (const auto& [digest, contents] : std::array{
std::pair{*candidate_digest, candidate_bytes},
std::pair{*fixture_digest, *fixture_bytes},
}) {
std::ofstream output{
capability_bundle_root / (digest + ".json"), std::ios::binary | std::ios::trunc
};
output << contents;
output.flush();
REQUIRE(output.good());
REQUIRE(::chmod((capability_bundle_root / (digest + ".json")).c_str(), 0600) == 0);
}
const auto final_refinement_plan = refinement_plan(
interactive_now_ms, page, *fixture_digest, *candidate_digest, fixture.matched_context_digest
);
auto refinement_created =
(*capability_protocol)
->handle_frame(
make_request(
"create-refinement",
"create_session",
std::string{
"{\"session_id\":\"session-refinement\",\"controller_plan_digest\":\""
} + std::string{controller_digest} +
"\",\"plan\":" + final_refinement_plan + "}",
"create-session-refinement",
interactive_now_ms + 20'000U
),
interactive_now_ms
);
REQUIRE(refinement_created.has_value());
auto refinement_created_response = decode_response(*refinement_created);
REQUIRE(refinement_created_response.has_value());
REQUIRE(refinement_created_response->result.has_value());
session_record_result refinement_record;
REQUIRE(!glz::read<glz::opts{.error_on_unknown_keys = true}>(
refinement_record, refinement_created_response->result->str
));
REQUIRE(
capability_runtime_shared->reconcile(*shared_producer, interactive_now_ms + 1U).has_value()
);
const glove::control::session_start_authorization refinement_authorization{
.schema_version = 1,
.authorization_id = "approval-session-refinement",
.session_id = "session-refinement",
.controller_plan_digest = std::string{controller_digest},
.plan_content_digest = refinement_record.plan_content_digest,
.approved_at_ms = interactive_now_ms + 2U,
.expires_at_ms = interactive_now_ms + 30'000U,
};
auto refinement_started = capability_runtime_shared->start(
*shared_producer,
refinement_authorization,
"execute-session-refinement",
interactive_now_ms + 3U
);
REQUIRE(refinement_started.has_value());
auto refinement_exited = capability_runtime_shared->wait("session-refinement");
REQUIRE(refinement_exited.has_value());
auto durable_refinement = capability_registry_shared->exited_status("session-refinement");
REQUIRE(durable_refinement.has_value());
REQUIRE(durable_refinement->refinement_receipt);
REQUIRE(shared_producer->anchor().sequence == 3);
return 0;
}
} // namespace glove_test
auto main() -> int {
return glove_test::run();
}