Skip to content

feat: declare and journal step placement with workspace pins (#225) - #227

Merged
kjgbot merged 6 commits into
mainfrom
feat/step-placement-225
Sep 8, 2026
Merged

kjgbot merged 6 commits into
mainfrom
feat/step-placement-225

Conversation

@kjgbot

@kjgbot kjgbot commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

Maintainability repair at 8bbafca

Addresses both requested blockers from the re-review at 9f3b265: the dispatcher pin-source comments now document local Git/filesystem I/O, failure conditions, and remote overrides; routing errors now carry the step and a specific diagnostic for duplicates or the rejected field. Admission and replay share RoutingDecision::validate, including workspace/fallback validation. Existing tests and review gates were not edited.

Repair scope and evidence. Other review concerns are not claimed as resolved by this repair. The PR remains unmerged.

Required workspace gate: literal command and complete captured output
$ cd kernel && cargo test --workspace
   Compiling relayflowd-core v0.1.0 (/Users/khaliqgant/AgentWorkforce/flows-225-placement-wt/kernel/relayflowd-core)
   Compiling relayflowd-journal v0.1.0 (/Users/khaliqgant/AgentWorkforce/flows-225-placement-wt/kernel/relayflowd-journal)
   Compiling relayflowd v0.1.0 (/Users/khaliqgant/AgentWorkforce/flows-225-placement-wt/kernel/relayflowd)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 8.51s
     Running unittests src/lib.rs (target/debug/deps/relayflowd-3287862779f564ca)

running 36 tests
test engine::remote::worker_failure_detail_tests::a_null_or_blank_output_yields_no_detail ... ok
test engine::remote::worker_failure_detail_tests::a_string_output_is_carried_verbatim_and_trimmed ... ok
test engine::remote::worker_failure_detail_tests::an_output_at_the_boundary_is_not_truncated ... ok
test engine::remote::worker_failure_detail_tests::a_non_string_output_is_rendered_rather_than_dropped ... ok
test engine::remote::worker_failure_detail_tests::truncation_does_not_split_a_multi_byte_char ... ok
test server::client::tests::resume_waits_while_the_heartbeat_renewed_lease_is_live ... ok
test server::liveness::tests::sweep_id_buckets_by_the_interval ... ok
test server::channels::tests::unknown_verb_never_falls_through_to_receive ... ok
test engine::boot_identity_tests::every_engine_in_this_process_shares_one_boot_id ... ok
test exec_det::tests::timeout_has_an_explicit_completion_reason ... ok
test exec_det::tests::captures_deterministic_output ... ok
test server::liveness::tests::sweep_pass_healthy_subscription_is_a_noop ... ok
test engine::wake::claim_guard_tests::a_disarmed_guard_leaves_the_claim_alone ... ok
test server::tests::agent::contract::an_agent_worker_attaching_without_pins_is_refused_at_attach ... ok
test server::liveness::tests::sweep_pass_latches_after_journaling_and_next_bucket_is_empty ... ok
test server::tests::agent::contract::an_oversized_trajectory_tail_is_refused_at_step_complete ... ok
test engine::wake::claim_guard_tests::a_panic_between_claim_and_register_still_releases ... ok
test engine::wake::claim_guard_tests::a_guard_only_releases_its_own_run ... ok
test engine::wake::claim_guard_tests::an_armed_guard_releases_the_claim_when_dropped ... ok
test server::tests::agent::contract::agent_without_a_compatible_worker_parks_without_starting ... ok
test server::tests::hello_enforces_protocol_version ... ok
test server::tests::agent::contract::an_agent_worker_missing_a_declared_surface_parks_the_run_instead_of_erroring ... ok
test server::tests::agent::contract::a_replacement_worker_that_never_reported_the_pinned_surface_is_not_dispatched_to ... ok
test server::tests::run_resume_asks_the_registry_instead_of_treating_an_orphan_file_as_a_run ... ok
test server::tests::a_failed_disconnect_journal_append_is_retained_and_retried_not_dropped ... ok
test server::tests::run_start_fails_closed_on_an_unknown_verification_key ... ok
test server::tests::agent::contract::an_llm_completion_claiming_an_effect_fails_closed_with_the_reason_journaled ... ok
test server::tests::agent::pins::reset_worker_reporting_a_revision_other_than_its_pin_fails_closed_as_worker_error ... ok
test server::tests::run_resume_refuses_a_journal_that_never_recorded_its_run ... ok
test server::tests::run_resume_adopts_a_real_journal_whose_registry_row_is_missing ... ok
test server::tests::agent::pins::consecutive_agent_steps_on_different_surfaces_each_start_from_their_own_pins ... ok
test server::tests::run_resume_refuses_a_valid_journal_that_belongs_to_another_run ... ok
test server::tests::stopped_heartbeats_past_the_deadline_journal_lease_expired_and_release_the_step ... ok
test exec_det::tests::timeout_kills_the_whole_process_group ... ok
test server::tests::agent::pins::a_replacement_worker_at_a_different_revision_is_not_dispatched_the_stale_pins ... ok
test server::tests::an_entry_appended_during_watch_registration_is_delivered_exactly_once ... ok

test result: ok. 36 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.61s

     Running unittests src/main.rs (target/debug/deps/relayflowd-ae3ceb16e5d803ec)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running tests/crash_resume.rs (target/debug/deps/crash_resume-4619905d2726c36c)

running 40 tests
test concurrency::cancel_closes_the_lease_and_rejects_a_late_completion ... ok
test agent::rung_c_sigkill_after_final_effect_replays_results_without_redispatch ... ok
test agent::resume_without_a_worker_parks_immediately_instead_of_timing_out ... ok
test concurrency::cancel_and_completion_race_has_one_terminal_fact ... ok
test concurrency::concurrent_resumes_lease_exactly_one_attempt ... ok
test agent::rung_c_sigkill_between_agent_completion_and_final_effect_memoizes_the_agent ... ok
test channels::channels_reject_foreign_workers_stale_attempts_and_invalid_acknowledgements ... ok
test concurrency::run_start_dispatches_every_independent_lane_before_any_completion ... ok
test concurrency::live_resume_leaves_an_active_lease_running ... ok
test agent::rung_c_reset_sigkill_mid_edit_restores_pins_dedupes_effect_and_explains_attempts ... ok
test agent::rung_c_crash_between_effect_election_and_the_provider_call_performs_it_exactly_once ... ok
test llm::serve_plumbs_watch_events_and_replayable_stream_verbs ... ok
test llm::completed_llm_output_is_memoized_when_serve_dies_during_the_next_step ... ok
test llm::sigkill_after_the_final_rung_b_effect_resumes_without_redispatching_llm ... ok
test llm::llm_verification_exhaustion_is_a_declared_failure_kind ... ok
test llm::failing_llm_verification_schedules_a_durable_retry_and_succeeds ... ok
test concurrency::server_restart_recovers_every_parallel_lease_without_duplicate_success ... ok
test agent::rung_c_sigkill_boundaries_resume_only_unfinished_steps_via_real_cli ... ok
test llm::sigkill_under_serve_mid_llm_releases_the_lease_and_finishes_via_cli_resume ... ok
test pin_projection::rejected_completion_cannot_forge_inspect_retry_pins_over_the_real_socket ... ok
test memory::memory_sigkill_after_injection_replays_pack_and_charges_it_once ... ok
test llm::worker_killed_while_holding_a_lease_is_explained_and_released_on_cli_resume ... ok
test placement::declared_placement_keeps_one_source_tree_across_resume ... ok
test sigkill_after_cancel_request_resumes_to_one_canceled_fact ... ok
test protocol_admission::every_mutating_run_verb_refuses_terminal_before_changing_state ... ok
test parallel_lifecycle::overlapping_agent_conflict_survives_server_crash_and_resume ... ok
test sigkill_mid_step_replaces_and_explains_the_dead_attempt ... ok
test placement::sigkill_before_first_step_preserves_the_submitted_workspace ... ok
test placement::sigkill_mid_step_keeps_the_route_and_source_tree ... ok
test parallel_lifecycle::overlapping_agent_lanes_serialize_while_disjoint_lanes_merge_in_either_order ... ok
test surface_identity::aliases_are_rejected_and_external_ancestors_serialize_over_real_sockets ... ok
test parallel_lifecycle::terminal_failure_drains_or_explains_every_live_sibling ... ok
test llm::sigkill_sweep_covers_before_and_between_the_rung_b_steps ... ok
test sigkill_under_serve_resumes_the_socket_started_run ... ok
test worker_capacity::two_workers_receive_a_deterministic_fair_capacity_bounded_batch ... ok
test workspace_identity::workspace_aliases_are_refused_and_canonical_subtrees_serialize_over_real_sockets ... ok
test worker_capacity::default_capacity_one_reopens_only_after_durable_completion_or_crash ... ok
test channels::channels_sigkill_resume_redelivers_unacked_messages_with_exactly_once_effects ... ok
test sigkill_sweep_covers_every_hello_step_boundary ... ok
test parallel_lifecycle::renewed_parallel_leases_survive_the_original_grant_and_remain_distinct ... ok

test result: ok. 40 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 37.95s

     Running tests/event_wake.rs (target/debug/deps/event_wake-bc602276d1cebcd0)

running 3 tests
test matching_event_wakes_once_with_fresh_context ... ok
test two_racing_deliveries_of_one_event_produce_exactly_one_run ... ok
test a_resumed_run_dispatches_the_original_wake_context ... ok

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s

     Running tests/hn_monitor_integration.rs (target/debug/deps/hn_monitor_integration-6b22693ad979dd80)

running 1 test
test hn_story_event_wakes_monitor_once_with_story_context ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.03s

     Running tests/invalid_schema_preflight.rs (target/debug/deps/invalid_schema_preflight-2c352b9351d5acf8)

running 3 tests
test invalid_json_schema_is_refused_before_journal_or_command ... ok
test unbounded_json_schema_is_refused_before_journal_or_command ... ok
test legitimately_recursive_json_schema_still_starts ... ok

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 7.36s

     Running tests/memory.rs (target/debug/deps/memory-a74848a8274a290e)

running 5 tests
test rejected_journal_fact_releases_reservation_and_never_dispatches ... ok
test llm_dispatch_receives_same_pack_after_resume_without_provider ... ok
test semantic_retry_reuses_memory_without_a_second_charge ... ok
test replay_and_resume_need_no_provider_and_script_receives_recorded_pack ... ok
test over_budget_and_provider_errors_fail_without_dispatch_or_charge ... ok

test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.03s

     Running tests/memory_epoch.rs (target/debug/deps/memory_epoch-edee30a47cad81b5)

running 1 test
test epoch_carries_pack_and_exact_charge_and_refuses_duplicate_injection ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s

     Running tests/parallel_driver.rs (target/debug/deps/parallel_driver-74d9d10149c534bd)

running 4 tests
test stop_after_one_holds_for_an_independent_deterministic_batch ... ok
test backpressured_or_mismatched_lane_does_not_drop_a_later_dispatch ... ok
test crash_boundaries_resume_the_real_driver_with_one_effect_per_lane ... ok
test pause_before_second_independent_step_holds_the_driver_boundary ... ok

test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.18s

     Running tests/placement_pins.rs (target/debug/deps/placement_pins-893a8773fbe94aee)

running 2 tests
test unsupported_local_pty_is_refused_before_an_earlier_step_can_run ... ok
test default_worker_pins_the_declared_worktree_base_commit_and_refuses_missing_source ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.05s

     Running tests/placement_routing.rs (target/debug/deps/placement_routing-aabf5947f3ca76b4)

running 3 tests
test a_failed_routing_append_never_starts_or_dispatches_work ... ok
test crash_between_routing_and_start_does_not_redecide ... ok
test worker_retry_consumes_the_original_routing_fact ... ok

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s

     Running tests/routing_diagnostics.rs (target/debug/deps/routing_diagnostics-ddddee3a427ee9ae)

running 2 tests
test duplicate_routes_have_a_distinct_diagnostic_and_leave_the_original_fact_intact ... ok
test malformed_routes_name_the_same_field_at_append_replay_and_epoch_replay ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s

     Running tests/subscription_liveness.rs (target/debug/deps/subscription_liveness-9506d11f678a9586)

running 3 tests
test submit_event_upserts_subscription_row_and_sweep_flags_it_stale_after_budget ... ok
test stale_transition_is_journaled_as_subscription_stale_entry_in_the_last_known_run ... ok
test a_fresh_arrival_re_arms_the_latch_and_the_next_silence_can_stale_again ... ok

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s

     Running unittests src/lib.rs (target/debug/deps/relayflowd_core-b1fe3b3250e9e7a2)

running 60 tests
test clock::tests::simulated_clock_is_explicitly_advanced ... ok
test journal::tests::memory_journal_assigns_sequences_and_rolls_epochs ... ok
test channel::tests::send_retry_is_stable_and_conflicting_content_is_rejected ... ok
test channel::tests::forged_deliveries_and_acknowledgements_fail_closed ... ok
test channel::tests::malformed_payloads_and_invalid_new_channel_appends_leave_state_unchanged ... ok
test channel::tests::delivery_replay_and_independent_acknowledged_offsets ... ok
test machine::parallel_tests::external_ancestor_and_descendant_paths_conflict_but_siblings_do_not ... ok
test machine::parallel_tests::every_declared_mutable_surface_participates_in_conflict_selection ... ok
test machine::parallel_tests::workspace_ancestor_and_descendant_paths_conflict_but_siblings_do_not ... ok
test machine::parallel_tests::failed_run_drains_open_siblings_before_terminal_entry ... ok
test machine::tests::all_backing_off_steps_return_timers ... ok
test machine::tests::cancel_request_closes_the_active_lease_before_the_terminal_fact ... ok
test machine::parallel_tests::crash_resume_preserves_each_parallel_lease_exactly_once ... ok
test machine::parallel_tests::machine_starts_every_runnable_step_in_authored_order ... ok
test machine::parallel_tests::parallel_lanes_do_not_cross_the_dependency_barrier_early ... ok
test machine::tests::every_reason_label_matches_its_serialized_form ... ok
test machine::parallel_tests::overlapping_agent_surfaces_are_serialized_in_authored_order ... ok
test machine::tests::durable_cancel_request_outranks_crash_recovery ... ok
test machine::tests::machine_starts_runnable_step_with_stable_effect_key ... ok
test machine::tests::crashed_attempt_does_not_consume_an_iteration ... ok
test machine::parallel_tests::disjoint_agent_lanes_merge_pins_in_either_completion_order ... ok
test machine::tests::repeated_cancel_request_is_idempotent ... ok
test machine::tests::manual_recovery_parks_needs_human_and_never_redispatches ... ok
test machine::tests::worker_reported_failure_without_detail_still_records_a_verification ... ok
test machine::tests::successful_memo_is_never_scheduled_again ... ok
test retry::tests::jitter_is_repeatable_and_bounded ... ok
test machine::tests::every_failed_run_terminates_with_declared_completion_reasons ... ok
test machine::tests::verification_failure_schedules_a_durable_retry ... ok
test memory::tests::caps_compare_exact_decimals_and_each_token_dimension ... ok
test machine::tests::inspect_recovery_injects_the_dirty_pin_completion_reason_and_tail ... ok
test machine::tests::reset_recovery_dispatches_the_original_pinned_revision ... ok
test schema::tests::in_document_uri_references_resolve_to_the_node_they_name ... ok
test schema::tests::refusal_names_the_cycle_it_found ... ok
test spec::tests::a_misspelled_step_level_key_is_a_parse_error ... ok
test spec::tests::a_misspelled_verification_gate_key_is_a_parse_error_not_a_dropped_gate ... ok
test spec::tests::cycles_are_rejected ... ok
test spec::tests::external_surface_paths_must_have_one_canonical_spelling ... ok
test spec::tests::preflight_data_is_fail_closed ... ok
test spec::tests::spec_version_is_semver_and_gated ... ok
test schema::tests::references_the_bound_leaves_opaque_are_refused_by_the_engine ... ok
test spec::tests::unknown_root_and_nested_fields_are_rejected ... ok
test spec::tests::zero_agent_flow_is_valid ... ok
test state::budget::tests::adds_costs_exactly_beyond_machine_decimal_precision ... ok
test state::budget::tests::overflow_and_malformed_cost_leave_total_unchanged ... ok
test spec::tests::workspace_mounts_and_worktrees_must_have_one_canonical_spelling ... ok
test state::tests::budget_decimal_strings_add_without_floats ... ok
test state::tests::end_pin_chain_is_enforced_and_a_broken_chain_is_a_hard_error ... ok
test state::tests::a_completion_that_omits_a_surface_does_not_drop_it_from_the_pin_chain ... ok
test verify::tests::an_unbounded_schema_in_a_journal_fails_its_gate_instead_of_aborting ... ok
test state::tests::journal_replays_data_gate_verdict_without_rerunning_completed_code ... ok
test verify::tests::deterministic_output_requires_successful_exit_and_content ... ok
test spec::tests::the_full_ladder_parses_in_the_one_dialect ... ok
test verify::tests::json_schema_is_a_control_gate ... ok
test schema::tests::a_property_named_ref_is_not_a_reference ... ok
test schema::tests::shared_declarations_and_boolean_schemas_are_validated ... ok
test schema::tests::every_accepted_corpus_schema_is_accepted ... ok
test schema::tests::every_refused_corpus_schema_compiles_but_is_refused_by_the_bound ... ok
test spec::tests::sdk_boundary_rejects_a_10_000_step_cycle_with_a_typed_error ... ok
test spec::tests::sdk_boundary_accepts_a_valid_10_000_step_reverse_chain ... ok
test schema::tests::deeply_nested_schemas_do_not_overflow_the_checker ... ok

test result: ok. 60 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.61s

     Running tests/spec_parity.rs (target/debug/deps/spec_parity-bbda6cf1e1cf1c19)

running 9 tests
test placement_requirements_have_identical_canonical_bytes_and_hash ... ok
test the_kernel_parses_the_deterministic_rung_and_stamps_the_same_hash ... ok
test the_kernel_parses_the_event_triggered_spec_and_stamps_the_same_hash ... ok
test step_memory_has_identical_canonical_bytes_and_hash ... ok
test the_kernel_parses_the_rung_c_agent_spec_and_stamps_the_same_hash ... ok
test memory_declaration_acceptance_matches_the_sdk_corpus ... ok
test placement_declaration_acceptance_matches_the_sdk_corpus ... ok
test the_kernel_parses_the_sdk_compiled_spec_and_stamps_the_same_hash ... ok
test the_kernel_parses_the_rung_b_spec_and_stamps_the_same_hash ... ok

test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s

     Running unittests src/lib.rs (target/debug/deps/relayflowd_journal-d13cb7954335385c)

running 28 tests
test registry::tests::a_same_boot_claim_with_no_run_yet_is_a_duplicate_not_wreckage ... ok
test registry::tests::a_previous_boots_claim_with_no_run_is_repaired ... ok
test registry::tests::registry_is_a_rebuildable_run_locator ... ok
test registry::tests::a_registered_run_dedupes_across_boots ... ok
test registry::tests::releasing_a_claim_lets_the_same_boot_retry ... ok
test registry::tests::a_pre_migration_registry_gains_boot_id_and_its_claims_are_repairable ... ok
test channel::tests::stale_attempts_and_raw_forged_acknowledgements_cannot_change_offsets ... ok
test subscriptions::tests::detect_without_latch_stays_available_for_the_next_sweep ... ok
test registry::tests::releasing_is_scoped_to_the_claiming_run ... ok
test subscriptions::tests::last_run_for_subscription_returns_none_before_first_arrival ... ok
test subscriptions::tests::last_run_for_subscription_returns_the_lex_greatest_ulid_regardless_of_insertion ... ok
test subscriptions::tests::latch_is_a_no_op_if_a_fresh_event_arrived_between_detect_and_latch ... ok
test subscriptions::tests::prune_sweep_claims_deletes_only_rows_older_than_cutoff ... ok
test channel::tests::channels_cross_segment_boundaries_and_terminal_runs_reject_mutations ... ok
test subscriptions::tests::sweep_election_gives_the_first_caller_the_result_and_second_gets_empty ... ok
test subscriptions::tests::sweep_ignores_subscriptions_whose_silence_is_still_within_budget ... ok
test subscriptions::tests::sweep_marks_row_stale_when_silence_exceeds_budget ... ok
test subscriptions::tests::sweep_does_not_re_emit_the_same_stale_row_on_a_later_tick ... ok
test subscriptions::tests::upsert_after_stale_re_arms_and_next_silence_can_re_emit ... ok
test subscriptions::tests::upsert_is_idempotent_across_bumps_and_preserves_event_type_updates ... ok
test tests::append_is_durable_and_monotonic_after_reopen ... ok
test tests::failed_commit_is_returned_not_swallowed ... ok
test tests::an_unconfirmed_election_is_reclaimed_by_the_next_attempt_not_treated_as_done ... ok
test tests::terminal_run_refuses_every_later_entry_atomically ... ok
test tests::effects_are_deduplicated_at_the_journal_boundary ... ok
test tests::rollover_is_atomic_scaffolding_for_epoch_resume ... ok
test channel::tests::failed_channel_writes_never_expose_delivery_or_advance_acknowledged_offset ... ok
test channel::tests::independent_connections_serialize_send_receive_and_acknowledgement ... ok

test result: ok. 28 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.20s

   Doc-tests relayflowd

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests relayflowd_core

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests relayflowd_journal

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s


exit_code=0

Steps could not declare placement needs, and local commands inherited the daemon cwd again after resume. This adds placement requirements in both spec dialects, a durable step.routed decision, and Git base-commit pins for declared local workspaces. Two deterministic steps share the recorded source tree even when resume starts elsewhere.

The first commit (fa54257) contains the failing test and captured red output; implementation is in 541d900. Routing is fixed in this slice. Retry and epoch replay retain the recorded profile, provider, attempted fallbacks, and workspace identity. Routing append failures prevent start/dispatch, and duplicate choices are rejected transactionally. SDK compilation and kernel parsing share canonical JSON/hash and validation fixtures.

Refs #225. Integration boundary: this PR exercises local execution and an in-process test-cloud-adapter dispatcher. It does not wire remote deterministic execution to cloud or claim a live Daytona/source-sync/sandbox-destruction result. Cloud's existing code-sync, per-run sandbox, leases, and provider runtimes remain the integration path. The default attached-worker dispatcher refuses unsupported placement declarations. Git pins identify base commits, not snapshots of uncommitted edits. Existing specs without requirements keep their execution behavior.

Evidence and scope: kernel/evidence/225/README.md.

The required gate command and its full captured output follow. SDK parity and type-check commands/output are also included. No merge requested.

Failing test before implementation
$ cd kernel && cargo test --workspace --test crash_resume placement:: -- --nocapture
   Compiling proc-macro2 v1.0.107
   Compiling quote v1.0.47
   Compiling unicode-ident v1.0.24
   Compiling libc v0.2.189
   Compiling cfg-if v1.0.4
   Compiling stable_deref_trait v1.2.1
   Compiling version_check v0.9.5
   Compiling autocfg v1.5.1
   Compiling serde_core v1.0.229
   Compiling getrandom v0.3.4
   Compiling zerocopy v0.8.56
   Compiling serde v1.0.229
   Compiling smallvec v1.15.2
   Compiling litemap v0.8.3
   Compiling writeable v0.6.4
   Compiling num-traits v0.2.19
   Compiling memchr v2.8.3
   Compiling generic-array v0.14.7
   Compiling icu_properties_data v2.3.0
   Compiling utf8_iter v1.0.4
   Compiling icu_normalizer_data v2.3.0
   Compiling parking_lot_core v0.9.12
   Compiling typenum v1.20.1
   Compiling zmij v1.0.23
   Compiling ref-cast v1.0.27
   Compiling syn v3.0.4
   Compiling syn v2.0.119
   Compiling synstructure v0.13.2
   Compiling num-integer v0.1.47
   Compiling num-bigint v0.4.8
   Compiling aho-corasick v1.1.5
   Compiling ahash v0.8.12
   Compiling zerofrom-derive v0.1.7
   Compiling yoke-derive v0.8.2
   Compiling shlex v2.0.1
   Compiling zerofrom v0.1.8
   Compiling serde_json v1.0.151
   Compiling once_cell v1.21.4
   Compiling regex-syntax v0.8.11
   Compiling scopeguard v1.2.0
   Compiling find-msvc-tools v0.1.11
   Compiling lock_api v0.4.14
   Compiling cc v1.4.4
   Compiling num-rational v0.4.2
   Compiling zerovec-derive v0.11.6
   Compiling displaydoc v0.2.7
   Compiling serde_derive v1.0.229
   Compiling ref-cast-impl v1.0.27
   Compiling yoke v0.8.3
   Compiling num-iter v0.1.46
   Compiling num-complex v0.4.6
   Compiling rand_core v0.9.5
   Compiling bit-vec v0.8.0
   Compiling pkg-config v0.3.34
   Compiling itoa v1.0.18
   Compiling borrow-or-share v0.2.4
   Compiling regex-automata v0.4.18
   Compiling vcpkg v0.2.15
   Compiling ppv-lite86 v0.2.21
   Compiling bit-set v0.8.0
   Compiling num v0.4.3
   Compiling parking_lot v0.12.5
   Compiling block-buffer v0.10.4
   Compiling rand_chacha v0.9.0
   Compiling crypto-common v0.1.7
   Compiling uuid v1.26.0
   Compiling libsqlite3-sys v0.35.0
   Compiling vsimd v0.8.0
   Compiling utf8parse v0.2.2
   Compiling zerovec v0.11.8
   Compiling zerotrie v0.2.5
   Compiling thiserror v2.0.20
   Compiling lazy_static v1.5.0
   Compiling foldhash v0.1.5
   Compiling percent-encoding v2.3.2
   Compiling outref v0.5.2
   Compiling bitflags v2.13.1
   Compiling uuid-simd v0.8.0
   Compiling hashbrown v0.15.5
   Compiling fraction v0.15.4
   Compiling anstyle-parse v1.0.0
   Compiling rand v0.9.5
   Compiling digest v0.10.7
   Compiling thiserror-impl v2.0.20
   Compiling cpufeatures v0.2.17
   Compiling anstyle v1.0.14
   Compiling num-cmp v0.1.0
   Compiling bytecount v0.6.9
   Compiling colorchoice v1.0.5
   Compiling is_terminal_polyfill v1.70.2
   Compiling anstyle-query v1.1.5
   Compiling base64 v0.22.1
   Compiling sha2 v0.10.9
   Compiling anstream v1.0.0
   Compiling hashlink v0.10.0
   Compiling clap_lex v1.1.0
   Compiling fallible-iterator v0.3.0
   Compiling strsim v0.11.1
   Compiling heck v0.5.0
   Compiling anyhow v1.0.104
   Compiling fallible-streaming-iterator v0.1.9
   Compiling clap_builder v4.6.6
   Compiling tinystr v0.8.4
   Compiling potential_utf v0.1.6
   Compiling clap_derive v4.6.4
   Compiling getrandom v0.4.3
   Compiling icu_locale_core v2.3.0
   Compiling icu_collections v2.3.0
   Compiling rustix v1.1.4
   Compiling fancy-regex v0.16.2
   Compiling icu_provider v2.3.1
   Compiling fluent-uri v0.3.2
   Compiling icu_normalizer v2.3.0
   Compiling icu_properties v2.3.0
   Compiling email_address v0.2.9
   Compiling regex v1.13.1
   Compiling ulid v1.2.1
   Compiling clap v4.6.6
   Compiling referencing v0.33.0
   Compiling wait-timeout v0.2.1
   Compiling errno v0.3.14
   Compiling fastrand v2.5.0
   Compiling idna_adapter v1.2.2
   Compiling idna v1.1.0
   Compiling jsonschema v0.33.0
   Compiling tempfile v3.27.0
   Compiling rusqlite v0.37.0
   Compiling relayflowd-core v0.1.0 (/Users/khaliqgant/AgentWorkforce/flows-225-placement-wt/kernel/relayflowd-core)
   Compiling relayflowd-journal v0.1.0 (/Users/khaliqgant/AgentWorkforce/flows-225-placement-wt/kernel/relayflowd-journal)
   Compiling relayflowd v0.1.0 (/Users/khaliqgant/AgentWorkforce/flows-225-placement-wt/kernel/relayflowd)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 16.12s
     Running tests/crash_resume.rs (target/debug/deps/crash_resume-e6635a3f0d48512c)

running 1 test

thread 'placement::declared_placement_keeps_one_source_tree_across_resume' (84534924) panicked at relayflowd/tests/crash_resume/placement.rs:40:5:
run failed: Error: parse run spec /var/folders/yv/nbp9l2c55wlbj1x0gml37s7c0000gn/T/.tmpsHRxJK/flow.json

Caused by:
    unknown field "requirements" at steps[0] — refusing to guess (fail closed)

note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
test placement::declared_placement_keeps_one_source_tree_across_resume ... FAILED

failures:

failures:
    placement::declared_placement_keeps_one_source_tree_across_resume

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 37 filtered out; finished in 0.65s

error: test failed, to rerun pass `-p relayflowd --test crash_resume`

exit_code=101
Required kernel gate
$ cd kernel && cargo test --workspace
   Compiling relayflowd-core v0.1.0 (/Users/khaliqgant/AgentWorkforce/flows-225-placement-wt/kernel/relayflowd-core)
   Compiling relayflowd-journal v0.1.0 (/Users/khaliqgant/AgentWorkforce/flows-225-placement-wt/kernel/relayflowd-journal)
   Compiling relayflowd v0.1.0 (/Users/khaliqgant/AgentWorkforce/flows-225-placement-wt/kernel/relayflowd)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 5.66s
     Running unittests src/lib.rs (target/debug/deps/relayflowd-3287862779f564ca)

running 36 tests
test engine::remote::worker_failure_detail_tests::a_null_or_blank_output_yields_no_detail ... ok
test engine::remote::worker_failure_detail_tests::a_string_output_is_carried_verbatim_and_trimmed ... ok
test engine::remote::worker_failure_detail_tests::a_non_string_output_is_rendered_rather_than_dropped ... ok
test engine::remote::worker_failure_detail_tests::an_output_at_the_boundary_is_not_truncated ... ok
test engine::remote::worker_failure_detail_tests::truncation_does_not_split_a_multi_byte_char ... ok
test server::client::tests::resume_waits_while_the_heartbeat_renewed_lease_is_live ... ok
test engine::boot_identity_tests::every_engine_in_this_process_shares_one_boot_id ... ok
test server::liveness::tests::sweep_id_buckets_by_the_interval ... ok
test server::channels::tests::unknown_verb_never_falls_through_to_receive ... ok
test exec_det::tests::timeout_has_an_explicit_completion_reason ... ok
test exec_det::tests::captures_deterministic_output ... ok
test engine::wake::claim_guard_tests::a_disarmed_guard_leaves_the_claim_alone ... ok
test server::tests::agent::contract::an_agent_worker_attaching_without_pins_is_refused_at_attach ... ok
test server::liveness::tests::sweep_pass_healthy_subscription_is_a_noop ... ok
test server::liveness::tests::sweep_pass_latches_after_journaling_and_next_bucket_is_empty ... ok
test engine::wake::claim_guard_tests::a_guard_only_releases_its_own_run ... ok
test server::tests::agent::contract::an_oversized_trajectory_tail_is_refused_at_step_complete ... ok
test engine::wake::claim_guard_tests::an_armed_guard_releases_the_claim_when_dropped ... ok
test engine::wake::claim_guard_tests::a_panic_between_claim_and_register_still_releases ... ok
test server::tests::agent::contract::an_agent_worker_missing_a_declared_surface_parks_the_run_instead_of_erroring ... ok
test server::tests::hello_enforces_protocol_version ... ok
test server::tests::agent::contract::agent_without_a_compatible_worker_parks_without_starting ... ok
test server::tests::run_resume_asks_the_registry_instead_of_treating_an_orphan_file_as_a_run ... ok
test server::tests::agent::contract::an_llm_completion_claiming_an_effect_fails_closed_with_the_reason_journaled ... ok
test server::tests::a_failed_disconnect_journal_append_is_retained_and_retried_not_dropped ... ok
test server::tests::run_start_fails_closed_on_an_unknown_verification_key ... ok
test server::tests::agent::contract::a_replacement_worker_that_never_reported_the_pinned_surface_is_not_dispatched_to ... ok
test server::tests::run_resume_refuses_a_journal_that_never_recorded_its_run ... ok
test server::tests::agent::pins::reset_worker_reporting_a_revision_other_than_its_pin_fails_closed_as_worker_error ... ok
test server::tests::run_resume_adopts_a_real_journal_whose_registry_row_is_missing ... ok
test server::tests::agent::pins::consecutive_agent_steps_on_different_surfaces_each_start_from_their_own_pins ... ok
test server::tests::run_resume_refuses_a_valid_journal_that_belongs_to_another_run ... ok
test server::tests::stopped_heartbeats_past_the_deadline_journal_lease_expired_and_release_the_step ... ok
test exec_det::tests::timeout_kills_the_whole_process_group ... ok
test server::tests::agent::pins::a_replacement_worker_at_a_different_revision_is_not_dispatched_the_stale_pins ... ok
test server::tests::an_entry_appended_during_watch_registration_is_delivered_exactly_once ... ok

test result: ok. 36 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.57s

     Running unittests src/main.rs (target/debug/deps/relayflowd-ae3ceb16e5d803ec)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running tests/crash_resume.rs (target/debug/deps/crash_resume-4619905d2726c36c)

running 40 tests
test agent::resume_without_a_worker_parks_immediately_instead_of_timing_out ... ok
test agent::rung_c_sigkill_after_final_effect_replays_results_without_redispatch ... ok
test channels::channels_reject_foreign_workers_stale_attempts_and_invalid_acknowledgements ... ok
test concurrency::cancel_and_completion_race_has_one_terminal_fact ... ok
test agent::rung_c_sigkill_between_agent_completion_and_final_effect_memoizes_the_agent ... ok
test concurrency::run_start_dispatches_every_independent_lane_before_any_completion ... ok
test concurrency::cancel_closes_the_lease_and_rejects_a_late_completion ... ok
test agent::rung_c_crash_between_effect_election_and_the_provider_call_performs_it_exactly_once ... ok
test agent::rung_c_reset_sigkill_mid_edit_restores_pins_dedupes_effect_and_explains_attempts ... ok
test concurrency::concurrent_resumes_lease_exactly_one_attempt ... ok
test llm::serve_plumbs_watch_events_and_replayable_stream_verbs ... ok
test concurrency::live_resume_leaves_an_active_lease_running ... ok
test llm::completed_llm_output_is_memoized_when_serve_dies_during_the_next_step ... ok
test concurrency::server_restart_recovers_every_parallel_lease_without_duplicate_success ... ok
test llm::llm_verification_exhaustion_is_a_declared_failure_kind ... ok
test llm::failing_llm_verification_schedules_a_durable_retry_and_succeeds ... ok
test llm::sigkill_after_the_final_rung_b_effect_resumes_without_redispatching_llm ... ok
test memory::memory_sigkill_after_injection_replays_pack_and_charges_it_once ... ok
test llm::sigkill_under_serve_mid_llm_releases_the_lease_and_finishes_via_cli_resume ... ok
test llm::worker_killed_while_holding_a_lease_is_explained_and_released_on_cli_resume ... ok
test pin_projection::rejected_completion_cannot_forge_inspect_retry_pins_over_the_real_socket ... ok
test agent::rung_c_sigkill_boundaries_resume_only_unfinished_steps_via_real_cli ... ok
test parallel_lifecycle::overlapping_agent_conflict_survives_server_crash_and_resume ... ok
test placement::declared_placement_keeps_one_source_tree_across_resume ... ok
test parallel_lifecycle::terminal_failure_drains_or_explains_every_live_sibling ... ok
test sigkill_mid_step_replaces_and_explains_the_dead_attempt ... ok
test protocol_admission::every_mutating_run_verb_refuses_terminal_before_changing_state ... ok
test sigkill_after_cancel_request_resumes_to_one_canceled_fact ... ok
test placement::sigkill_mid_step_keeps_the_route_and_source_tree ... ok
test parallel_lifecycle::overlapping_agent_lanes_serialize_while_disjoint_lanes_merge_in_either_order ... ok
test surface_identity::aliases_are_rejected_and_external_ancestors_serialize_over_real_sockets ... ok
test placement::sigkill_before_first_step_preserves_the_submitted_workspace ... ok
test llm::sigkill_sweep_covers_before_and_between_the_rung_b_steps ... ok
test workspace_identity::workspace_aliases_are_refused_and_canonical_subtrees_serialize_over_real_sockets ... ok
test sigkill_under_serve_resumes_the_socket_started_run ... ok
test worker_capacity::two_workers_receive_a_deterministic_fair_capacity_bounded_batch ... ok
test sigkill_sweep_covers_every_hello_step_boundary ... ok
test worker_capacity::default_capacity_one_reopens_only_after_durable_completion_or_crash ... ok
test channels::channels_sigkill_resume_redelivers_unacked_messages_with_exactly_once_effects ... ok
test parallel_lifecycle::renewed_parallel_leases_survive_the_original_grant_and_remain_distinct ... ok

test result: ok. 40 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 37.79s

     Running tests/event_wake.rs (target/debug/deps/event_wake-bc602276d1cebcd0)

running 3 tests
test matching_event_wakes_once_with_fresh_context ... ok
test two_racing_deliveries_of_one_event_produce_exactly_one_run ... ok
test a_resumed_run_dispatches_the_original_wake_context ... ok

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s

     Running tests/hn_monitor_integration.rs (target/debug/deps/hn_monitor_integration-6b22693ad979dd80)

running 1 test
test hn_story_event_wakes_monitor_once_with_story_context ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.03s

     Running tests/invalid_schema_preflight.rs (target/debug/deps/invalid_schema_preflight-2c352b9351d5acf8)

running 3 tests
test invalid_json_schema_is_refused_before_journal_or_command ... ok
test unbounded_json_schema_is_refused_before_journal_or_command ... ok
test legitimately_recursive_json_schema_still_starts ... ok

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 7.30s

     Running tests/memory.rs (target/debug/deps/memory-a74848a8274a290e)

running 5 tests
test rejected_journal_fact_releases_reservation_and_never_dispatches ... ok
test llm_dispatch_receives_same_pack_after_resume_without_provider ... ok
test replay_and_resume_need_no_provider_and_script_receives_recorded_pack ... ok
test semantic_retry_reuses_memory_without_a_second_charge ... ok
test over_budget_and_provider_errors_fail_without_dispatch_or_charge ... ok

test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.03s

     Running tests/memory_epoch.rs (target/debug/deps/memory_epoch-edee30a47cad81b5)

running 1 test
test epoch_carries_pack_and_exact_charge_and_refuses_duplicate_injection ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s

     Running tests/parallel_driver.rs (target/debug/deps/parallel_driver-74d9d10149c534bd)

running 4 tests
test stop_after_one_holds_for_an_independent_deterministic_batch ... ok
test backpressured_or_mismatched_lane_does_not_drop_a_later_dispatch ... ok
test crash_boundaries_resume_the_real_driver_with_one_effect_per_lane ... ok
test pause_before_second_independent_step_holds_the_driver_boundary ... ok

test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.14s

     Running tests/placement_pins.rs (target/debug/deps/placement_pins-893a8773fbe94aee)

running 2 tests
test unsupported_local_pty_is_refused_before_an_earlier_step_can_run ... ok
test default_worker_pins_the_declared_worktree_base_commit_and_refuses_missing_source ... ok

test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.05s

     Running tests/placement_routing.rs (target/debug/deps/placement_routing-aabf5947f3ca76b4)

running 3 tests
test a_failed_routing_append_never_starts_or_dispatches_work ... ok
test crash_between_routing_and_start_does_not_redecide ... ok
test worker_retry_consumes_the_original_routing_fact ... ok

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s

     Running tests/subscription_liveness.rs (target/debug/deps/subscription_liveness-9506d11f678a9586)

running 3 tests
test submit_event_upserts_subscription_row_and_sweep_flags_it_stale_after_budget ... ok
test stale_transition_is_journaled_as_subscription_stale_entry_in_the_last_known_run ... ok
test a_fresh_arrival_re_arms_the_latch_and_the_next_silence_can_stale_again ... ok

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s

     Running unittests src/lib.rs (target/debug/deps/relayflowd_core-b1fe3b3250e9e7a2)

running 60 tests
test clock::tests::simulated_clock_is_explicitly_advanced ... ok
test journal::tests::memory_journal_assigns_sequences_and_rolls_epochs ... ok
test channel::tests::malformed_payloads_and_invalid_new_channel_appends_leave_state_unchanged ... ok
test channel::tests::send_retry_is_stable_and_conflicting_content_is_rejected ... ok
test channel::tests::delivery_replay_and_independent_acknowledged_offsets ... ok
test channel::tests::forged_deliveries_and_acknowledgements_fail_closed ... ok
test machine::tests::all_backing_off_steps_return_timers ... ok
test machine::parallel_tests::machine_starts_every_runnable_step_in_authored_order ... ok
test machine::parallel_tests::failed_run_drains_open_siblings_before_terminal_entry ... ok
test machine::parallel_tests::parallel_lanes_do_not_cross_the_dependency_barrier_early ... ok
test machine::parallel_tests::crash_resume_preserves_each_parallel_lease_exactly_once ... ok
test machine::parallel_tests::every_declared_mutable_surface_participates_in_conflict_selection ... ok
test machine::tests::every_reason_label_matches_its_serialized_form ... ok
test machine::parallel_tests::overlapping_agent_surfaces_are_serialized_in_authored_order ... ok
test machine::tests::cancel_request_closes_the_active_lease_before_the_terminal_fact ... ok
test machine::parallel_tests::external_ancestor_and_descendant_paths_conflict_but_siblings_do_not ... ok
test machine::tests::durable_cancel_request_outranks_crash_recovery ... ok
test machine::tests::crashed_attempt_does_not_consume_an_iteration ... ok
test machine::parallel_tests::workspace_ancestor_and_descendant_paths_conflict_but_siblings_do_not ... ok
test machine::tests::repeated_cancel_request_is_idempotent ... ok
test machine::tests::machine_starts_runnable_step_with_stable_effect_key ... ok
test machine::tests::inspect_recovery_injects_the_dirty_pin_completion_reason_and_tail ... ok
test machine::tests::successful_memo_is_never_scheduled_again ... ok
test machine::tests::manual_recovery_parks_needs_human_and_never_redispatches ... ok
test machine::tests::verification_failure_schedules_a_durable_retry ... ok
test machine::tests::worker_reported_failure_without_detail_still_records_a_verification ... ok
test retry::tests::jitter_is_repeatable_and_bounded ... ok
test memory::tests::caps_compare_exact_decimals_and_each_token_dimension ... ok
test machine::tests::every_failed_run_terminates_with_declared_completion_reasons ... ok
test machine::parallel_tests::disjoint_agent_lanes_merge_pins_in_either_completion_order ... ok
test machine::tests::reset_recovery_dispatches_the_original_pinned_revision ... ok
test schema::tests::in_document_uri_references_resolve_to_the_node_they_name ... ok
test schema::tests::refusal_names_the_cycle_it_found ... ok
test spec::tests::a_misspelled_step_level_key_is_a_parse_error ... ok
test spec::tests::a_misspelled_verification_gate_key_is_a_parse_error_not_a_dropped_gate ... ok
test spec::tests::cycles_are_rejected ... ok
test spec::tests::preflight_data_is_fail_closed ... ok
test spec::tests::spec_version_is_semver_and_gated ... ok
test spec::tests::external_surface_paths_must_have_one_canonical_spelling ... ok
test schema::tests::references_the_bound_leaves_opaque_are_refused_by_the_engine ... ok
test spec::tests::unknown_root_and_nested_fields_are_rejected ... ok
test spec::tests::zero_agent_flow_is_valid ... ok
test state::budget::tests::adds_costs_exactly_beyond_machine_decimal_precision ... ok
test spec::tests::workspace_mounts_and_worktrees_must_have_one_canonical_spelling ... ok
test state::budget::tests::overflow_and_malformed_cost_leave_total_unchanged ... ok
test state::tests::budget_decimal_strings_add_without_floats ... ok
test state::tests::a_completion_that_omits_a_surface_does_not_drop_it_from_the_pin_chain ... ok
test state::tests::journal_replays_data_gate_verdict_without_rerunning_completed_code ... ok
test verify::tests::an_unbounded_schema_in_a_journal_fails_its_gate_instead_of_aborting ... ok
test verify::tests::deterministic_output_requires_successful_exit_and_content ... ok
test state::tests::end_pin_chain_is_enforced_and_a_broken_chain_is_a_hard_error ... ok
test verify::tests::json_schema_is_a_control_gate ... ok
test spec::tests::the_full_ladder_parses_in_the_one_dialect ... ok
test schema::tests::a_property_named_ref_is_not_a_reference ... ok
test schema::tests::shared_declarations_and_boolean_schemas_are_validated ... ok
test schema::tests::every_accepted_corpus_schema_is_accepted ... ok
test schema::tests::every_refused_corpus_schema_compiles_but_is_refused_by_the_bound ... ok
test spec::tests::sdk_boundary_rejects_a_10_000_step_cycle_with_a_typed_error ... ok
test spec::tests::sdk_boundary_accepts_a_valid_10_000_step_reverse_chain ... ok
test schema::tests::deeply_nested_schemas_do_not_overflow_the_checker ... ok

test result: ok. 60 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.64s

     Running tests/spec_parity.rs (target/debug/deps/spec_parity-bbda6cf1e1cf1c19)

running 9 tests
test the_kernel_parses_the_event_triggered_spec_and_stamps_the_same_hash ... ok
test placement_requirements_have_identical_canonical_bytes_and_hash ... ok
test the_kernel_parses_the_deterministic_rung_and_stamps_the_same_hash ... ok
test step_memory_has_identical_canonical_bytes_and_hash ... ok
test the_kernel_parses_the_rung_c_agent_spec_and_stamps_the_same_hash ... ok
test memory_declaration_acceptance_matches_the_sdk_corpus ... ok
test placement_declaration_acceptance_matches_the_sdk_corpus ... ok
test the_kernel_parses_the_sdk_compiled_spec_and_stamps_the_same_hash ... ok
test the_kernel_parses_the_rung_b_spec_and_stamps_the_same_hash ... ok

test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s

     Running unittests src/lib.rs (target/debug/deps/relayflowd_journal-d13cb7954335385c)

running 28 tests
test registry::tests::a_registered_run_dedupes_across_boots ... ok
test registry::tests::registry_is_a_rebuildable_run_locator ... ok
test registry::tests::a_same_boot_claim_with_no_run_yet_is_a_duplicate_not_wreckage ... ok
test registry::tests::a_previous_boots_claim_with_no_run_is_repaired ... ok
test registry::tests::releasing_a_claim_lets_the_same_boot_retry ... ok
test registry::tests::a_pre_migration_registry_gains_boot_id_and_its_claims_are_repairable ... ok
test channel::tests::stale_attempts_and_raw_forged_acknowledgements_cannot_change_offsets ... ok
test subscriptions::tests::detect_without_latch_stays_available_for_the_next_sweep ... ok
test registry::tests::releasing_is_scoped_to_the_claiming_run ... ok
test subscriptions::tests::latch_is_a_no_op_if_a_fresh_event_arrived_between_detect_and_latch ... ok
test subscriptions::tests::last_run_for_subscription_returns_none_before_first_arrival ... ok
test subscriptions::tests::last_run_for_subscription_returns_the_lex_greatest_ulid_regardless_of_insertion ... ok
test subscriptions::tests::prune_sweep_claims_deletes_only_rows_older_than_cutoff ... ok
test channel::tests::channels_cross_segment_boundaries_and_terminal_runs_reject_mutations ... ok
test subscriptions::tests::sweep_does_not_re_emit_the_same_stale_row_on_a_later_tick ... ok
test subscriptions::tests::sweep_marks_row_stale_when_silence_exceeds_budget ... ok
test subscriptions::tests::sweep_election_gives_the_first_caller_the_result_and_second_gets_empty ... ok
test subscriptions::tests::sweep_ignores_subscriptions_whose_silence_is_still_within_budget ... ok
test subscriptions::tests::upsert_is_idempotent_across_bumps_and_preserves_event_type_updates ... ok
test subscriptions::tests::upsert_after_stale_re_arms_and_next_silence_can_re_emit ... ok
test channel::tests::failed_channel_writes_never_expose_delivery_or_advance_acknowledged_offset ... ok
test tests::append_is_durable_and_monotonic_after_reopen ... ok
test tests::effects_are_deduplicated_at_the_journal_boundary ... ok
test tests::an_unconfirmed_election_is_reclaimed_by_the_next_attempt_not_treated_as_done ... ok
test tests::rollover_is_atomic_scaffolding_for_epoch_resume ... ok
test tests::failed_commit_is_returned_not_swallowed ... ok
test tests::terminal_run_refuses_every_later_entry_atomically ... ok
test channel::tests::independent_connections_serialize_send_receive_and_acknowledgement ... ok

test result: ok. 28 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.45s

   Doc-tests relayflowd

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests relayflowd_core

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests relayflowd_journal

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s


exit_code=0
SDK parity and types
$ cd packages/sdk && node node_modules/vitest/vitest.mjs run tests/placement.test.ts tests/spec-parity.test.ts

 RUN  v2.1.9 /Users/khaliqgant/AgentWorkforce/flows-225-placement-wt/packages/sdk

 ✓ tests/placement.test.ts (54 tests) 11ms
 ✓ tests/spec-parity.test.ts (31 tests) 209ms

 Test Files  2 passed (2)
      Tests  85 passed (85)
   Start at  13:58:43
   Duration  819ms (transform 206ms, setup 0ms, collect 489ms, tests 221ms, environment 0ms, prepare 129ms)


exit_code=0
$ cd packages/sdk && node node_modules/typescript/bin/tsc --noEmit

exit_code=0
$ cd packages/sdk && node node_modules/typescript/bin/tsc -p tsconfig.type-tests.json

exit_code=0

Review infrastructure blocker

At head 541d90078d23f3037cbc373dc550ba6e91fea66e, the review swarm failed its cloud authentication check before review because CLOUD_API_KEY is empty. This needs the repository CI secret restored by its owner. No gate or workflow was changed to bypass it. The PR remains unmerged.

Captured failing review job
$ gh run view 34119825675 --repo AgentWorkforce/flows --log-failed
review	Validate cloud authentication	2026-09-07T12:03:50.6350206Z ##[group]Run test -n "$CLOUD_API_URL"
review	Validate cloud authentication	2026-09-07T12:03:50.6350664Z ^[[36;1mtest -n "$CLOUD_API_URL"^[[0m
review	Validate cloud authentication	2026-09-07T12:03:50.6350996Z ^[[36;1mtest -n "$CLOUD_API_KEY"^[[0m
review	Validate cloud authentication	2026-09-07T12:03:50.6351528Z ^[[36;1mecho "CLOUD_API_URL and CLOUD_API_KEY present; interactive login is unreachable from here."^[[0m
review	Validate cloud authentication	2026-09-07T12:03:50.6392003Z shell: /usr/bin/bash -e {0}
review	Validate cloud authentication	2026-09-07T12:03:50.6392349Z env:
review	Validate cloud authentication	2026-09-07T12:03:50.6392659Z   CLOUD_API_URL: https://agentrelay.com/cloud
review	Validate cloud authentication	2026-09-07T12:03:50.6393025Z   CLOUD_API_KEY: 
review	Validate cloud authentication	2026-09-07T12:03:50.6393516Z   RELAY_WORKSPACE_KEY: ***
review	Validate cloud authentication	2026-09-07T12:03:50.6393868Z   RELAY_API_KEY: ***
review	Validate cloud authentication	2026-09-07T12:03:50.6394163Z ##[endgroup]
review	Validate cloud authentication	2026-09-07T12:03:50.6487291Z ##[error]Process completed with exit code 1.
review	Enforce swarm result	2026-09-07T12:03:50.6630291Z ##[group]Run echo "Review swarm did not complete successfully: " >&2
review	Enforce swarm result	2026-09-07T12:03:50.6630920Z ^[[36;1mecho "Review swarm did not complete successfully: " >&2^[[0m
review	Enforce swarm result	2026-09-07T12:03:50.6631341Z ^[[36;1mexit 1^[[0m
review	Enforce swarm result	2026-09-07T12:03:50.6668110Z shell: /usr/bin/bash -e {0}
review	Enforce swarm result	2026-09-07T12:03:50.6668437Z env:
review	Enforce swarm result	2026-09-07T12:03:50.6668753Z   CLOUD_API_URL: https://agentrelay.com/cloud
review	Enforce swarm result	2026-09-07T12:03:50.6669148Z   CLOUD_API_KEY: 
review	Enforce swarm result	2026-09-07T12:03:50.6669628Z   RELAY_WORKSPACE_KEY: ***
review	Enforce swarm result	2026-09-07T12:03:50.6669999Z   RELAY_API_KEY: ***
review	Enforce swarm result	2026-09-07T12:03:50.6670265Z ##[endgroup]
review	Enforce swarm result	2026-09-07T12:03:50.6728122Z Review swarm did not complete successfully: 
review	Enforce swarm result	2026-09-07T12:03:50.6731515Z ##[error]Process completed with exit code 1.

@coderabbitai

coderabbitai Bot commented Sep 7, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: c814eccf-cbda-4426-b4a9-6b570f1287de


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@kjgbot

kjgbot commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor Author

Review swarm: maintainability

No fresh transcript was produced for run ad1e97f9-debd-41ae-8ba3-13f175c08802 (MISSING).

@kjgbot

kjgbot commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor Author

Review swarm: history

No fresh transcript was produced for run ad1e97f9-debd-41ae-8ba3-13f175c08802 (MISSING).

@kjgbot

kjgbot commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor Author

Review swarm: structure

No fresh transcript was produced for run ad1e97f9-debd-41ae-8ba3-13f175c08802 (MISSING).

@kjgbot

kjgbot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

🎯 review-swarm: FAILED (M:fail H:pass S:pass)

Lens transcripts posted as sibling comments above.

@kjgbot

kjgbot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

3-lens review passes; CI does not. Held, not merged.

PRESWARM_structure:       REVIEW_PASSED
PRESWARM_history:         REVIEW_PASSED
PRESWARM_maintainability: REVIEW_PASSED
linux-x64-artifact:       FAILURE     <- blocks

I did not author this change, so the lenses above are an independent read. They liked it — drive.rs stayed lean at 441 lines, the routing invariant is defended at the SQLite append layer, no speculative abstraction.

The one thing to fix

FAIL tests/verb-field-lint.test.ts > closed per-verb step fields
  > pins the per-verb descriptor and generates every foreign-field pair from it
AssertionError: expected [ 'id', 'type', 'dependsOn', …(4) ]
                to deeply equal [ 'id', 'type', 'dependsOn', …(3) ]

requirements was added to STEP_COMMON_FIELDS (packages/sdk/src/step-fields.ts:26) without updating the pin that guards that list.

This is the second time this exact trap has fired — #221 hit it with memory and merged red, breaking main for ~90 minutes (fixed in #223). The pin is an acknowledgement gate, not a duplicate of the source: adding the field to the test's expect(STEP_COMMON_FIELDS).toEqual([...]) is the acknowledgement. Worth a comment there recording why requirements is common rather than verb-specific — any step kind may declare placement, so it generates no foreign-field pairs.

That it caught you twice suggests the failure message should say what to do. Worth a follow-up issue against the test itself rather than a third lane rediscovering it.

Note on the gate

This PR is held by the auto-merge loop rather than merged. That is new as of today: the loop previously gated on a review-swarm comment marker with zero CI references, which is how #221 merged red. It now requires linux-x64-artifact == SUCCESS and fails closed. Verified against this PR:

#227 HELD: required check 'linux-x64-artifact' = FAILURE

So the review passing is no longer sufficient, which is the correct outcome here.

`linux-x64-artifact` fails on this branch:

    FAIL tests/verb-field-lint.test.ts > closed per-verb step fields
    AssertionError: expected [ 'id','type','dependsOn', …(4) ]
                    to deeply equal [ 'id','type','dependsOn', …(3) ]

`requirements` was added to STEP_COMMON_FIELDS (step-fields.ts:26) without
updating the pin that guards that list. The pin is an acknowledgement gate
rather than a duplicate of the source, so adding the field to it IS the
acknowledgement.

The comment records why it is common rather than verb-specific, matching the
`memory` entry directly above: any step kind may declare placement
requirements, so it generates no foreign-field pairs.

Second time this trap has fired — #221 hit it with `memory` and merged red,
breaking main for ~90 minutes. Filing a follow-up so the failure message says
what to do rather than a third lane rediscovering it.

Verified:
  vitest tests/verb-field-lint.test.ts   78 passed
  full SDK suite                         741 passed, 3 skipped, 0 failed

Pushed to this PR's own branch rather than a new PR, so the fix lands where the
work is.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR
kjgbot pushed a commit that referenced this pull request Sep 7, 2026
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review completed against the latest diff

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/sdk/src/step-fields.ts
Comment thread kernel/relayflowd-journal/src/placement.rs
Comment thread kernel/relayflowd/src/workspace.rs Outdated
Comment thread kernel/relayflowd-core/src/state.rs Outdated
Comment thread kernel/relayflowd/src/engine/drive.rs
@kjgbot

kjgbot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

Re-ran the lenses at the exact head — maintainability now FAILS

CI is green after the pin fix (linux-x64-artifact: SUCCESS), so I re-ran the gate at the current head 9f3b265 rather than relying on the earlier run at 541d900:

PRESWARM_structure:       REVIEW_PASSED
PRESWARM_maintainability: REVIEW_FAILED   <- blocks

Not merging. Two blockers, both in this PR's own code — my pin commit touched one test file and none of the findings point at it:

Blockers

1. worker.rs:67-72 — the doc comment now lies. It still says pins are "Opaque starting revisions/offsets reported by the selected worker", but the default changed from Ok(Pins::default()) to crate::workspace::starting_pins(step), which shells out to git rev-parse on the local filesystem for every declared surface. A future implementer overriding StepDispatcher reads that comment, keeps the default, and their remote-worker adapter silently starts running git inside the kernel process. Either correct the comment or move the local pin into a separately-named default so remote adapters opt out visibly.

2. state.rs:127-135,443 — three failure modes collapse into one error. Duplicate insert, empty profile and empty provider all become StateError::InvalidRouting(String) reading only "invalid or duplicate routing decision for step X". Meanwhile journal/src/placement.rs:23 has its own, wider definition of invalid via route.is_valid(). Two surfaces, two definitions, one flat variant — an operator debugging a production fold cannot tell which happened. Given the RFC ties completions to declared reasons, split the variant or add a detail field.

Concerns worth reading

  1. engine/placement.rs:103-114 — a step needing a workspace with no journaled route takes the workspace of any prior local route via state.routing.values(), which BTreeMap orders by step id, not dependency order. If two lanes declare different workspaces, first-found silently wins. The invariant it relies on is undocumented.
  2. journal/src/placement.rs:40-50 — every step.routed append re-reads run.spawned, re-parses the full spec, and rescans all routes: O(spec × routes) per append, and it pulls RunSpec::parse into the journal crate.
  3. engine/drive.rs:131-144 — if workspace::pin fails after the executor already failed, the pin error overwrites failure_reason/failure_detail and the original failure is lost. No test pins the precedence.
  4. spec.ts:139 says only deterministic steps with placement requirements pin, but state.rs:312-320 advances current_pins for every successful deterministic step with populated end_pins.

And a note on the gate itself

This same lens passed this same code at 541d900 an hour ago. My commit in between changed one test file. So the earlier PASS was the unreliable verdict, not this FAIL — which is flows#218 (the pre-push and post-push lens definitions have drifted) showing up in the more dangerous direction: a pass that should have been a block.

Worth weighting accordingly — the findings above are specific and checkable, so judge them on their merits rather than on which run produced them.

kjgbot pushed a commit that referenced this pull request Sep 7, 2026
…sm produced a false PASS

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 10 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread kernel/relayflowd-core/src/state/routing.rs
@kjgbot

kjgbot commented Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

Both blockers fixed and verified — but the gate is now self-contradictory

Your fixes landed properly. I checked them rather than taking the commit message for it:

  • state.rs — InvalidRouting { step: String, detail: String } now carries a detail, with state/routing.rs producing distinct ones. That was one of the two options named.
  • worker.rs — the starting_pins doc now says the default "runs git rev-parse --verify HEAD in each declared local worktree, using this process's filesystem" and that "Remote dispatchers must override this or reserved_starting_pins". The comment matches the code again.

CI is green at 8bbafca: linux-x64-artifact: SUCCESS, packed-consumer: SUCCESS, cubic: SUCCESS. (review is the repo-wide CLOUD_API_KEY failure.)

Why this still is not merged

The maintainability lens returned:

### Blockers
None. The invariants that could break silently do fail closed ...
REVIEW_FAILED

It reports no blockers and then emits REVIEW_FAILED. I am not overriding that token — lens-runner.sh makes the exit code the authority precisely so nobody layers a substring match on top, and this is the gate that judges my own work.

Recorded as evidence on flows#218 with the full three-run history on this PR: a false PASS at 541d900, a correct FAIL at 9f3b265, and this self-contradiction at 8bbafca.

Nothing further is asked of this branch. It is blocked on the gate, not on the code.

@kjgbot kjgbot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spec review: original 8bbafca; integrity fixes pushed on this PR branch at 733cf17. BLOCKED: leave open.

P1 — RFC-0001 decision #13 / the explicit vocabulary constraint for this review. kernel/relayflowd-core/src/entry.rs:28 adds the step.routed entry type; entry.rs:393 adds epoch.summary.routing; spec.rs:348 adds the kernel requirements schema, and kernel/relayflowd/src/worker.rs:19 adds dispatch routing. Gate 7 does require journaled routing evidence, but RFC-0001 does not specify these exact additions or their compatibility contract. This is a blocking specification question, not a naming nit. Khaliq/spec owner must settle the contract explicitly or require lowering through existing facts; I have not amended the spec to approve my work.

P1 — kernel/relayflowd/src/engine/placement.rs:163 rereads HEAD instead of preserving the source revision across resume. The route persists only a path, so the run can switch source commits between steps and still complete successfully. This contradicts the source continuity required by Gate 7 / Appendix A's pin chain. The remaining correction depends on the agreed durable representation; I did not invent another field to paper over the vocabulary blocker. Literal reproduction (script and output also committed in kernel/evidence/225/spec-review-source-drift-repro.*):

$ python3 ops/spec-review-0907-evidence/227-source-drift-repro.py /Users/khaliqgant/AgentWorkforce/flows-225-placement-wt/kernel/target/debug/relayflowd
$ git init -q
exit_code=0
$ git add source.txt
exit_code=0
$ git -c user.name=Fixture -c user.email=fixture@example.test -c commit.gpgsign=false commit -qm original
exit_code=0
$ /Users/khaliqgant/AgentWorkforce/flows-225-placement-wt/kernel/target/debug/relayflowd --data-dir /var/folders/yv/nbp9l2c55wlbj1x0gml37s7c0000gn/T/placement-drift-dsqt865h/data run /var/folders/yv/nbp9l2c55wlbj1x0gml37s7c0000gn/T/placement-drift-dsqt865h/flow.json --stop-after 1
{"run_id":"01M1YXR74Z6RWZMCYJ8HW9SY9S","status":"interrupted","completion_reason":null,"completed_steps":1}
exit_code=0
$ git add source.txt
exit_code=0
$ git -c user.name=Fixture -c user.email=fixture@example.test -c commit.gpgsign=false commit -qm changed
exit_code=0
$ /Users/khaliqgant/AgentWorkforce/flows-225-placement-wt/kernel/target/debug/relayflowd --data-dir /var/folders/yv/nbp9l2c55wlbj1x0gml37s7c0000gn/T/placement-drift-dsqt865h/data resume 01M1YXR74Z6RWZMCYJ8HW9SY9S
{"run_id":"01M1YXR74Z6RWZMCYJ8HW9SY9S","status":"completed","completion_reason":"success","completed_steps":2}
exit_code=0
{"entry_type": "step.attempt.started", "step": "first", "pins": {"streams": [], "workspace": [{"revision_id": "c2df7c0316d15be56f8cb282cd67da83a70c7704", "surface": "/private/var/folders/yv/nbp9l2c55wlbj1x0gml37s7c0000gn/T/placement-drift-dsqt865h/tree"}]}, "output": null}
{"entry_type": "step.completed", "step": "first", "pins": null, "output": {"exit_code": 0, "stderr_tail": "", "stdout_tail": "original\n"}}
{"entry_type": "step.attempt.started", "step": "second", "pins": {"streams": [], "workspace": [{"revision_id": "14f735f5866363a1de0b1fef8faa1200c6d0e7f8", "surface": "/private/var/folders/yv/nbp9l2c55wlbj1x0gml37s7c0000gn/T/placement-drift-dsqt865h/tree"}]}, "output": null}
{"entry_type": "step.completed", "step": "second", "pins": null, "output": {"exit_code": 0, "stderr_tail": "", "stdout_tail": "changed-between-steps\n"}}

exit_code=0

Integrity fixes made without further vocabulary additions:

  • kernel/relayflowd-core/src/state/routing.rs:8: reject attempt-scoped routing entries during replay, matching journal admission.
  • kernel/relayflowd-journal/src/placement.rs:47: validate raw epoch routing before commit, including unknown steps/malformed decisions and attempts to drop or replace durable routes.
  • kernel/relayflowd/src/workspace.rs:11: peel HEAD with HEAD^{commit} and refuse non-commit objects.

Four focused regressions failed before these fixes and passed after. Complete literal before/after commands and outputs are committed in kernel/evidence/225/spec-review-regressions-before.txt and spec-review-regressions-after.txt. This is not labeled mutation verification. After-fix captured output:

$ env RUSTC=/Users/khaliqgant/.rustup/toolchains/stable-aarch64-apple-darwin/bin/rustc /Users/khaliqgant/.rustup/toolchains/stable-aarch64-apple-darwin/bin/cargo test --manifest-path kernel/Cargo.toml --locked --offline --test spec_review_routing
   Compiling relayflowd-core v0.1.0 (/Users/khaliqgant/AgentWorkforce/flows-225-placement-wt/kernel/relayflowd-core)
   Compiling relayflowd-journal v0.1.0 (/Users/khaliqgant/AgentWorkforce/flows-225-placement-wt/kernel/relayflowd-journal)
   Compiling relayflowd v0.1.0 (/Users/khaliqgant/AgentWorkforce/flows-225-placement-wt/kernel/relayflowd)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 2.99s
     Running tests/spec_review_routing.rs (kernel/target/debug/deps/spec_review_routing-a37130322a188264)

running 4 tests
test attempt_scoped_route_is_rejected_at_append_and_replay ... ok
test malformed_epoch_routes_are_rejected_before_commit ... ok
test epoch_cannot_drop_or_replace_a_durable_route ... ok
test workspace_pin_peels_tags_and_refuses_non_commit_objects ... ok

test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.10s


exit_code=0

The complete kernel workspace command/output is in kernel/evidence/225/spec-review-kernel-tests-final.txt, including 40 crash/resume tests. The first workspace attempt failed during doctests because ambient rustdoc differed from selected rustc; that failed output remains in spec-review-kernel-tests.txt. Matching RUSTC/RUSTDOC fixed the environment mismatch without code/gate changes. SDK captured output:

$ node node_modules/vitest/vitest.mjs run tests/placement.test.ts tests/spec-parity.test.ts tests/verb-field-lint.test.ts

 RUN  v2.1.9 /Users/khaliqgant/AgentWorkforce/flows-225-placement-wt/packages/sdk

 ✓ tests/placement.test.ts (54 tests) 11ms
 ✓ tests/spec-parity.test.ts (31 tests) 226ms
 ✓ tests/verb-field-lint.test.ts (78 tests) 518ms
   ✓ closed per-verb step fields > carries the llm/agent `output` sugar through every path > flows check accepts output on llm 321ms

 Test Files  3 passed (3)
      Tests  163 passed (163)
   Start at  23:52:12
   Duration  1.23s (transform 359ms, setup 0ms, collect 1.16s, tests 755ms, environment 0ms, prepare 170ms)


exit_code=0

The earlier descriptor snapshot and blank routing-field findings were already addressed at the reviewed head; other outstanding threads need acknowledgement of the actual fixes. CI was previously credential-blocked; the newly pushed head must earn its own checks. Passing tests cannot override the two P1 findings above. No merge.

@github-actions

github-actions Bot commented Sep 7, 2026 •

Copy link
Copy Markdown

Review swarm: FAILED

  • maintainability: MISSING
  • history: MISSING
  • structure: MISSING

Cloud run: ad1e97f9-debd-41ae-8ba3-13f175c08802

@kjgbot kjgbot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Final disposition at 733cf17: LEFT OPEN.

The P1 closed-vocabulary and P1 source-revision drift findings in the earlier review remain blocking at 733cf17. Khaliq/spec owner must settle the exact routing contract under decision #13; placement author must then preserve/reject source drift across resume using the approved durable representation. The four integrity fixes do not establish Gate 7 acceptance. Latest-head artifact and packed-consumer checks succeeded, but the review failed with all fresh lens transcripts MISSING. Review infrastructure owner must restore the swarm. Existing stale descriptor thread also needs acknowledgement of captured SDK evidence; source-pin drift thread remains valid. Leave open regardless of CI.

Captured exact-head check query and output:

$ gh api repos/AgentWorkforce/flows/commits/733cf17a216234cc08f284444910a279c4c1553d/check-runs --jq '[.check_runs[] | {name,head_sha,status,conclusion,details_url}]'
[{"conclusion":"success","details_url":"https://www.cubic.dev/pr/AgentWorkforce/flows/pull/227","head_sha":"733cf17a216234cc08f284444910a279c4c1553d","name":"cubic · AI code reviewer","status":"completed"},{"conclusion":"failure","details_url":"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/AgentWorkforce/flows/actions/runs/34164872298/job/101873821752","head_sha":"733cf17a216234cc08f284444910a279c4c1553d","name":"review","status":"completed"},{"conclusion":"success","details_url":"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/AgentWorkforce/flows/actions/runs/34164872290/job/101873821719","head_sha":"733cf17a216234cc08f284444910a279c4c1553d","name":"packed-consumer","status":"completed"},{"conclusion":"success","details_url":"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/AgentWorkforce/flows/actions/runs/34164872285/job/101873821552","head_sha":"733cf17a216234cc08f284444910a279c4c1553d","name":"linux-x64-artifact","status":"completed"}]

exit_code=0

Current swarm report: #227 (comment)

Review swarm: FAILED

  • maintainability: MISSING
  • history: MISSING
  • structure: MISSING

Cloud run: 75a98cec-c6e5-4cf9-aa87-620f5632ae19

kjgbot pushed a commit that referenced this pull request Sep 8, 2026
…arlier

Corrected #234's header with literal run evidence: the credential works, the
block is Daytona quota. Declined to bank the good news and keep the COMPLETE —
the verdict path is still unverified. Also: #227/#231 worktrees are idle, not
live as I recorded last tick.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR
kjgbot pushed a commit that referenced this pull request Sep 8, 2026
…in backlog

Both settled by running: the lint snapshot already pins `requirements` (78
green) and the routing fold already delegates to RoutingDecision::validate()
with the blank cases pinned at append, replay and epoch replay. Night's tally:
13 findings, 7 real.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR
kjgbot pushed a commit that referenced this pull request Sep 8, 2026
…ake closure

The carried-pin path argues the drift cannot happen, but no test moves the
worktree HEAD between attempts and reading is what was wrong twice on this PR
tonight. Needs a regression test, not my reading.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR
kjgbot pushed a commit that referenced this pull request Sep 8, 2026
reserved_starting_pins is a trait method a double can record; pause_before_step
plus resume reaches a second attempt without the socket harness; with_runtime is
required or the agent step is never admitted. Stopped before writing it rather
than ship a test I would not trust.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR
…AD moves

Closes the open finding on #227. The concern was that a declared local run
resuming after its worktree HEAD changed would record a different source pin
for the same durable route. Nothing tested it: `placement_pins` only called
`starting_pins` directly (which does re-read HEAD, correctly — that is the
worker's job), and `crash_resume/workspace_identity` covers aliases and
canonical subtrees, not a moving HEAD.

The test starts an agent step over a git worktree, lets the dispatcher take
the lease so the run parks, commits again to move HEAD, then resumes under a
fresh Engine — a new boot id, so the leased attempt reads as dead and the step
is retried. It asserts both attempts carry the elected revision, and that a
recording dispatcher was asked for pins exactly once.

Mutation-verified rather than trusted green: forcing the `covered` branch off
in `resolve_agent_pins` makes it fail (pin_requests 2, expected 1).

Worth recording what that mutation also showed. Under it the *revision*
assertion still held, because the projection reads
`carried.workspace.iter().chain(worker.workspace.iter())` and takes the first
match. So carried-first is what actually protects the pin; the `covered`
short-circuit only avoids the needless question. The test now pins both.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR
kjgbot added a commit that referenced this pull request Sep 8, 2026
…on (#229)

* fix(review-gate): derive the lens verdict from its own Blockers section

Two failures in one day, both from the same root: the verdict token is a
separate judgement from the findings, and the two consumers ask different
questions.

**1. A verdict that contradicted its own review.** On #227 the maintainability
lens printed:

    ### Blockers
    None. The invariants that could break silently do fail closed ...
    REVIEW_FAILED

No blockers, and REVIEW_FAILED. A caller cannot appeal that: lens-runner.sh
makes the exit code authoritative on purpose, because a substring gate would be
fail-open. So a broken review blocks finished work with no recourse.

The prompt now makes the token DERIVED rather than chosen: head a section
exactly `### Blockers`, write None when there are none, and the token follows
from that section. Concerns and notes are explicitly not blockers and must not
change it.

The runner also detects the contradiction and labels it:

    PRESWARM_<lens>: CONTRADICTION — review says 'Blockers: None' but emitted
    REVIEW_FAILED; treating as NO_VERDICT (gate defect, not a finding)

This NEVER upgrades a verdict. Exit stays 1. Turning a failure into a pass on a
substring is exactly the fail-open the classifier refuses; relabelling one so a
branch is not blamed for a gate defect is not.

**2. Prompt drift between the two consumers.** `lens-runner.sh` carried detailed
prompts while `review-swarm.yaml` carried one-line summaries with every specific
instruction stripped — and auto-merge acts on the swarm, the weaker of the two.
That is how #215 merged with defects the local run had named. The three roles now
carry the same clauses as the runner, including the Blockers-derivation rule.

Verified the detector against six shapes, including the two that matter:

    "### Blockers\nNone. The invariants..."          -> NONE   (the real #227 text)
    "### Blockers\n1. real\n### Concerns\nNone."     -> HAS    (not fooled by a later None)
    no Blockers section at all                        -> HAS    (fail-closed)
    "### Blockers\n\nNone."                          -> NONE   (blank line tolerated)
    "**None** — nothing blocking"                     -> NONE   (bold tolerated)
    two numbered blockers                             -> HAS

`bash -n` clean; review-swarm.yaml still parses.

Does not consolidate the prompts into one file both consumers read — that is the
end state #218 proposes and needs the swarm spec to load role text from disk.
This makes them agree and adds the derivation rule; the single source of truth
is still open.

Refs #218, #227, #215

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

* fix(review-gate): a PASS that lists blockers is also a contradiction

An independent spec review found a fail-open in my own fix. The REVIEW_PASSED
arm checked only the CLI exit code and never consulted the Blockers section, so
a review that enumerated blockers -- unauthorized writes among them -- and
ended in REVIEW_PASSED exited 0.

The comment above that arm claims the classifier "NEVER upgrades a verdict",
and it does not. That was the wrong safety property to reason about. One-
directional safety guards fail->pass, which fails CLOSED anyway, and leaves the
fail-OPEN direction unguarded, which is the only direction a gate cannot afford
to get wrong. I wrote that comment as a proof of safety; it was a proof about
the harmless half.

`blockers_are_listed` is deliberately NOT the negation of `blockers_say_none`:
an ABSENT Blockers section returns false, so a review that never emitted the
section keeps its previous behaviour rather than newly failing. That closes the
unambiguous hole without changing the blast radius for non-conforming lenses.

Verified across all five arms:

    blockers listed + PASSED   -> CONTRADICTION (exit 1)   was: exit 0
    Blockers: None  + PASSED   -> REVIEW_PASSED  (exit 0)
    no section      + PASSED   -> REVIEW_PASSED  (exit 0)  unchanged
    Blockers: None  + FAILED   -> CONTRADICTION (exit 1)
    blockers listed + FAILED   -> REVIEW_FAILED  (exit 1)

Direction of the change is strictly tightening: it can only turn a pass into a
non-verdict, never a failure into a pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

* fix(review-gate): read the LAST Blockers section, not the first

The P2 from the same spec review, and it defeated the P1 fix I shipped an hour
ago. Both helpers used:

    awk '/^#+[[:space:]]*Blockers[[:space:]]*$/{f=1;next} f&&NF{print;exit}'

which flags on the FIRST matching heading and exits at its first body line. A
review with an early "Blockers: None" summary and a later real section is read
as "None":

    first-match awk -> None                       (guard passes the review)
    last-match awk  -> - unauthorized write       (guard blocks it)

So the fail-open I closed was still reachable through a differently-shaped
review, and `blockers_are_listed` inherited the flaw the moment I wrote it on
top of the same pattern.

The comment above these helpers has said "the LAST `### Blockers` heading"
since the original change. The code never did that. A comment describing
intent rather than behaviour is worse than no comment: I read it twice while
fixing P1 and took it as a description of what the code did.

Both helpers now accumulate to the last matching section. Verified across seven
arms, including the two multi-section cases that motivated this:

    early None + LATER real blockers + PASSED -> CONTRADICTION (exit 1)
    early real + LATER None          + FAILED -> CONTRADICTION (exit 1)
    blockers listed + PASSED                  -> CONTRADICTION (exit 1)
    Blockers: None  + PASSED                  -> REVIEW_PASSED  (exit 0)
    no section      + PASSED                  -> REVIEW_PASSED  (exit 0)
    Blockers: None  + FAILED                  -> CONTRADICTION (exit 1)
    blockers listed + FAILED                  -> REVIEW_FAILED  (exit 1)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

* fix(review-gate): a missing Blockers section is NO_VERDICT, not a pass

cubic P1 on #229. A lens that emits REVIEW_PASSED with no `### Blockers`
section cleared the gate, because `blockers_are_listed` returns false for an
absent section and the PASSED arm read that as "no blockers".

My own comment defended this: an absent section should "keep its previous
behaviour instead of newly failing", to avoid widening the blast radius. That
was protecting a case the prompt already forbids — it requires the heading and
says the first word under it must be `None` when there are none. A review
without it has not answered the question the gate asks.

Add `blockers_section_present` as a separate guard rather than overloading the
existing boolean, so the log distinguishes "the lens contradicted itself" from
"the lens ignored the output contract"; those need different fixes.

Verified against all four cases: no section + PASSED -> NO_VERDICT; None +
PASSED -> PASSED; blockers listed + PASSED -> CONTRADICTION; and a second
Blockers section listing one still reads the LAST section.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

* fix(review-gate): require the exact `### Blockers` heading level

cubic P2 on #229, on the guard I added an hour earlier. `blockers_section_present`
matched `^#+`, so a review headed `# Blockers` or `#### Blockers` satisfied the
missing-section guard and could still pass. A review at the wrong heading level
has not followed the output contract the prompt states.

Deliberately stricter than `blockers_are_listed` and `blockers_say_none`, which
keep matching `^#+`. That asymmetry is the point: this function decides whether
a section COUNTS, so it must fail closed on a wrong level, while those two only
DETECT blockers, where being permissive also fails closed.

Verified: `###` + None passes; `#`, `##`, `####` and no section are all
NO_VERDICT; `###` + a listed blocker is still CONTRADICTION.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

---------

Co-authored-by: kjgbot <kjgbot@agentrelay.dev>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
kjgbot pushed a commit that referenced this pull request Sep 8, 2026
Three new cubic findings on tonight's own fixes arrived first and were valid;
fixed before merging. Verified each collision on review-swarm.yml against the
silent-merge trap by grepping main, not by trusting a clean merge.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR
@kjgbot
kjgbot merged commit be3c95e into main Sep 8, 2026
4 of 5 checks passed
@kjgbot
kjgbot deleted the feat/step-placement-225 branch September 8, 2026 06:12
kjgbot pushed a commit that referenced this pull request Sep 8, 2026
…ero open PRs

Khaliq was right that I had framed effort as capability. Reviewed both against
gate 7's Done-when clauses and the settled decisions, ran the suites, merged.
The kernel carries no provider ranking, which was the check that mattered.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR
kjgbot pushed a commit that referenced this pull request Sep 8, 2026
lsof shows two codex processes holding flows-runtime-0907-wt, pid up ~10h. I
checked the tree was clean but never whether anyone was working in it. Restored
to its branch. Also: #227 merged darwin-arm64 placement green, so the
SCOREBOARD gate 7 row is out of date.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR
kjgbot pushed a commit that referenced this pull request Sep 9, 2026
Both remaining structure blockers, and the first is sharper than the wording fix
I made an hour ago.

P1 - location, not phrasing. I had added a paragraph saying the relayhistory
adapter must not live in relayflowd. Directionally right, but the FILE still sat
at kernel/GATE5-MEMORY-CONTRACT.md, and a document under kernel/ reads as kernel
design authority no matter what its text says. It specifies ai-hist CLI syntax,
JSON output, exit-code behaviour and provider traps -- SDK/control-plane
knowledge that RFC-0001 section 4 and settled decision 13 keep out of the
provider-neutral Rust kernel. Moved to docs/ and added an explicit ownership
header saying why, so location and text now agree.

P2 - the gate-7 scoreboard cell had become a second design report: Rust symbols,
test names, crash behaviour, a mutation claim, commit hashes and suite counts in
one table cell. Reduced 1420 chars to 382: gate state, what is journaled, and
the reason it is not GREEN. The implementation narrative and mutation transcript
belong in the PR #227 review artifacts, which AGENTS.md already requires to
carry the literal transcript -- a row asserting "mutation-verified" was never
evidence.

Documentation only.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR
kjgbot added a commit that referenced this pull request Sep 10, 2026
…ing on (#240)

* docs(scoreboard): gate 7 is AMBER — #227 landed the darwin-arm64 suite it waited on

The row said RED because "regression suite needs darwin-arm64 placement". That
suite merged last night as be3c95e and is green: full kernel run is 205 passed
/ 0 failed on main at c9bf155, on darwin arm64. I merged the work and left the
row that tracks it stale, which is the same staleness this lane spent four
ticks correcting in other files.

AMBER rather than GREEN, deliberately. RFC-0001 gate 7 requires the same flow
YAML to run locally AND in cloud with no placement config, and only the local
half is provable from this repo. Promoting it to GREEN on the strength of a
passing kernel suite would repeat exactly what the gate 2 row already warns
about: its bar is the real workload in production, not a test run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

* docs(gate5): record relayhistory's contract before writing a provider against it

RFC-0001 gate 5 says relayhistory is "consumed over its serialization contract,
not rewritten", so this reads the contract from the repo (3e7df69) rather than
proposing a design that ignores it.

Retrieval is `ai-hist pack --json`, emitting { query, entries }. The `--tokens`
budget is applied as chars = tokens * 4, an approximation and not a tokenizer,
so a provider must not report it back as exact usage — decision 10's per-step
accounting is only checkable if the number means something.

The trap worth having in writing: pack_entries calls std::process::exit(1) when
nothing matches, AFTER printing an empty entries array. Exit 1 means "no memory
matched", not "the call failed". A provider treating nonzero as an error would
report every cold-start step as a memory failure.

Also records what #221 already landed — the MemoryProvider seam and itemized
memory.injected accounting — versus what is still a stub, so nobody re-derives
that gate 5's hard part is done and its retrieval is not.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

* docs: correct a self-contradicting claim and a PR attribution

Two cubic findings on #240, both mine.

The gate 7 row said the kernel "carries no provider names" two sentences after
naming `RoutingDecision (profile, provider, fallbacks_attempted, workspace)`,
and `placement.rs` declares `pub provider: String`. I meant the kernel hardcodes
no provider identities and contains no ranking; what I wrote reads as false
against the struct on the same row. Rewritten to say the chosen provider is the
journaled fact while the kernel holds no provider identities and no ranking.

The gate 5 contract note credited the memory seam to #221. `kernel/MEMORY.md` is
titled "Step memory, slice 1 (#220)"; #221 is a separate PR. Corrected.

Neither changes a verdict — gate 7 stays AMBER and the gate 5 contract is
unchanged — but a scoreboard that contradicts itself is worse than one that is
merely out of date, because the contradiction is what a reader trusts least.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

* docs(gate5): place the relayhistory provider outside the kernel boundary

The structure lens raised a P1 and it is right. Item 1 read "a
RelayhistoryMemoryProvider implementing the existing MemoryProvider trait", and
that trait lives in kernel/relayflowd/src/memory.rs -- so the wording naturally
directs the implementation into relayflowd, where a subprocess/provider
integration would violate RFC-0001 section 4 and settled decision #13.

That is a structural defect in the contract, not a naming quibble: a contract
that reads as an instruction to put ai-hist inside the Rust kernel will
eventually be followed.

The item now states where the adapter lives (SDK/control-plane edge, crossing
the journal protocol boundary), keeps the kernel-side MemoryProvider an injected
protocol seam only, and prohibits an ai-hist dependency, a subprocess call, or
relayhistory-shaped vocabulary in relayflowd.

Documentation only; no product code or tests are touched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

* docs: fix a reversed PR attribution and two unsupported claims

Both blockers the history lens raised are mine, and H1 is the worse kind: a
commit that announced it was correcting an attribution and reversed it instead.

H1 - GATE5-MEMORY-CONTRACT.md:9 said "#220 landed the seam ... #221 is a
separate PR". #220 is the ISSUE; PR #221 implemented it and closed it. The line
now reads "PR #221 (issue #220) landed the seam", and explains that
kernel/MEMORY.md is titled with #220 because it names the issue. The historical
commit stays; the current document is corrected here, as the lens asked.

H2 - SCOREBOARD.md:14 asserted "full kernel suite 205 passed / 0 failed" and
described a case as "mutation-verified" while supplying neither commands nor a
transcript. That is the failure class AGENTS.md rules 1-2 prohibit -- evidence
is captured, not narrated -- and a pass count drifts while a transcript does
not. The row now cites the run rather than restating a number, and says how the
mutation check was performed without claiming the sentence is the proof.

Documentation only.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

* docs: move the gate-5 contract out of kernel/ and shrink the gate-7 row

Both remaining structure blockers, and the first is sharper than the wording fix
I made an hour ago.

P1 - location, not phrasing. I had added a paragraph saying the relayhistory
adapter must not live in relayflowd. Directionally right, but the FILE still sat
at kernel/GATE5-MEMORY-CONTRACT.md, and a document under kernel/ reads as kernel
design authority no matter what its text says. It specifies ai-hist CLI syntax,
JSON output, exit-code behaviour and provider traps -- SDK/control-plane
knowledge that RFC-0001 section 4 and settled decision 13 keep out of the
provider-neutral Rust kernel. Moved to docs/ and added an explicit ownership
header saying why, so location and text now agree.

P2 - the gate-7 scoreboard cell had become a second design report: Rust symbols,
test names, crash behaviour, a mutation claim, commit hashes and suite counts in
one table cell. Reduced 1420 chars to 382: gate state, what is journaled, and
the reason it is not GREEN. The implementation narrative and mutation transcript
belong in the PR #227 review artifacts, which AGENTS.md already requires to
carry the literal transcript -- a row asserting "mutation-verified" was never
evidence.

Documentation only.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FtQSAcGDta5VH9xiZFT4sR

---------

Co-authored-by: kjgbot <kjgbot@agentrelay.dev>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant