Repository navigation
Apply, clamp or refuse launch controls on every route - #2064
Merged
Merged
Conversation
ppXD
force-pushed
the
fix/dispose-every-launch-control
branch
from
October 11, 2026 11:37
0bcfcc1 to
39a5961
Compare
3 of 4 tasks
ppXD
force-pushed
the
fix/dispose-every-launch-control
branch
from
October 12, 2026 01:14
39a5961 to
20da248
Compare
ppXD
force-pushed
the
fix/dispose-every-launch-control
branch
3 times, most recently
from
November 7, 2026 17:08
4946ca7 to
4bb812d
Compare
An Auto launch that routed to plan-map or single-agent silently dropped the operator's allowed model pool, persona pool, decision critic, delivery spec and integrate opt-in (only the supervisor builder read them), and plan-map dropped the acceptance floor while the composer demanded one. The planner was shown the whole team pool, dispatch never bounded the model it ran, and a failing check could escalate past the pool. Every route-dependent control now gets a disposition - Applied, Clamped, NotApplicable or Refused, with the reason - from one resolver the launch and its route preview share, so a preview can no longer promise a launch it will not get. A refusal stops the launch before any session or run exists, with task_launch_control_refused; the quality tier's floor mandate is one of those dispositions, so the preview predicts it too. The preview also runs the launch's own pool checks, so a pool member gone since the composer loaded is the same not-found. - The model pool bounds the plan-map planner's catalog and rides every agent.run. Dispatch runs a bounded task on the pooled row its pin names - by row, then model on credential, then model, then the credential's best row, then the pool default - names any move by cause on the run, fails with agent_model_pool_unresolvable (not retried) when nothing in the pool runs, and never escalates outside the pool. The engine leaves an unusable pinned row to that choice instead of failing the node at staging. Quick makes the same choice at launch, so the frozen config shows the row that runs. - An escalation stamps the row it picked beside the model, and every later revise round and a cold-launch record carry that row, so the output review avoids the model that did the work, live and after a re-attach. - A persona the pool excludes refuses a plan-map or quick launch, and is named, not shown as an id. - The acceptance floor follows the route and workspace's own verdict. The single agent grades an argv in its own workspace, repository or not, so a repository-free floor there is applied; the supervisor grades against the branch it publishes, so without a repository its floor is refused, and a Delivery tier there is refused with the same remedies the floor names (bind a repository, or launch at Quick effort); with no floor carried it says to bind a repository or launch at Prototype. Plan-map refuses the floor, and a malformed one is refused by name. On Auto the floor keeps the route on a lane that grades it in this workspace, and the confirm card suggests the tier the route took. - Grounding is resolved before routing, so a chat follow-up is classified as one, and an ambiguous classification asks to confirm.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
SupervisorDefinitionBuilderread them), and plan-map dropped the acceptance floor the composer demanded. Every route-dependent control (allowedModelIds,allowedAgentDefinitionIds,acceptanceChecks,tier,decisionReviewMode,deliverySpec,integrateBranches,requirePlanConfirmation,plannerReviewMode) now gets aLaunchControlDisposition(Applied/Clamped/NotApplicable/Refused, with the reason) from oneILaunchControlResolverthatTaskLaunchServiceandTaskRoutePreviewServiceshare; both results carryControlDispositions. AnyRefusedstops the launch before a session or run exists withtask_launch_control_refused(422), listing each refused control. The preview also runs the launch's own pool checks (EnsureModelsInTeamAsync/EnsureAgentDefinitionsInTeamAsync), so a pooled row disabled or a persona deleted since the composer loaded is the same not-found on both.WorkflowPlanRequest.AllowedModelIds) and every branchagent.run; single-agent bakes it on its agent; theagent.runmanifest declaresallowedModelIds. At dispatchHarnessModelReconcilerchooses throughAllowedModelPool: the pinned row, the authored model on the pinned credential, that model on any pooled credential, the pinned credential's best pooled row, then the pool default. That order now holds at the engine level: under a non-empty poolAgentDefinitionResolverleaves an unusable pinned row on the task instead of failing the node at staging (an unbounded task still fails closed).AgentRunExecutorruns and persists the chosen row and names any move on the timeline by cause, with model and credential names. A pool with nothing runnable fails the run withagent_model_pool_unresolvable(not retried, logged at Warning without a stack trace). Every escalation stays inside the pool and stamps the row it picked beside the model (every later revise round and the cold-launch record carry that row), so the output review avoids the model that did the work, live and after a re-attach. Quick makes the same choice at launch, so the frozen config shows the row that runs. A persona the pool excludes is refused on single-agent and plan-map, named by its name.TaskProjectionAcceptanceContract.RequiresRepository; defaults to true). The single agent grades the argv in its own workspace (the executor's local acceptance verifier), so a repository-free floor on the single-agent lane is Applied. The supervisor grades against the branch it publishes, so without a repository its floor is refused; at Delivery/Unattended thetierdisposition refuses once, with the remedies the floor names (bind a repository, or launch at Quick effort; Prototype when no floor is carried), never "author a check". Plan-map refuses the floor; a malformed argv (blank executable, a null or NUL argument) is refused by name and counts as absent. On Auto, a well-formed floor keeps the route on a lane that grades it in this workspace: a repo-less floor lands on the single agent, never Deep or plan-map. The move is named inRoutePlan.DegradedReason, and the confirm card suggests the tier the route took. Grounding is resolved before routing in both launch and route snapshot, andSignals.Ambiguousasks for confirmation.Clampedwithout rewriting the profile: each branch is held at dispatch, and the synthesis keeps its model.IsWellFormedFloorkeeps its own copy of the argv rule; the sharedAcceptanceArgvnormaliser is not on main yet.EnsureSessionCanContinueAsync, so a Continue preview can update the session summary and call the summarising model, as the launch always did.ResultJson, not stored on the run.ReviewerModelId, which the pool does not bound (unchanged here).TaskLaunchAutobenchmark arm now confirms an ambiguous classification, as it already did a low-confidence one.LaunchTaskModal.tsx~:323, the stalemissing.push("an acceptance check")gate) still asks a repo-less Deep launch for a check, where the backend's remedy is to bind a repository or launch at Quick effort.RealSupervisorActionExecutor.Spawn.cs:1131resolves a spawn's model throughModelPoolSelector.ResolveDispatchAsync, which breaks same-name ties by row id (OrderBy(m => m.Id)) and overwrites a persona's pinned credential.AllowedModelIdson the spawnedAgentTask, so their revise-loop escalation is bounded by credential only.Test plan
Unit, full suite: 13,241 passed, 1 skipped (the environment-gated cross-device case in
LocalRwxAtomicCreateTests), 0 failed. Focused classes:LaunchControlResolverTests30,AllowedModelPoolTests10,LlmEffortClassifierTests42,TaskRoutePreviewServiceTests24,TaskLaunchServiceQualityTierTests6,EffortRouterTests19,EffortRouterDegradeTests1,EffortRouterGenericityTests4,EffortPolicyTests24,DeploymentAutonomyCeilingTests38,FailureTaxonomyTests8,SingleAgentDefinitionBuilderTests38,PlanMapSynthDefinitionBuilderTests34,SupervisorDefinitionBuilderTests30,AgentCodeNodeTests151,PlanAuthorNodeTests13,AgentRunExecutorEscalationTests10,AgentRunExecutorOutputReviewTests70,AgentDefinitionResolverTests22Integration (real Postgres):
TaskLaunchFlowTests59,TaskRoutePreviewFlowTests8,TaskLaunchContractFlowTests11,TaskRouteSnapshotFlowTests11,EffortRouterFlowTests3,PlannerModelPoolFlowTests1,PlannerCassetteDriftTests2 (ExpectedPlannerKeyunchanged)HarnessModelReconcilerFlowTests19,AgentDefinitionResolverCredentialedModelFlowTests9,AgentDefinitionResolverFlowTests22,AgentRunExecutorTests152 (engine-staged pool and escalation-row tests included),AgentRunReviseLoopFlowTests17,AgentNodeFlowTests26,LocalAcceptanceExecutorFlowTests14,AgentReviewerLoopFlowTests2,CriticObservedModelFlowTests20,AgentRunReattachFlowTests15,SupervisorRichSpawnFlowTests13E2E:
TaskRouteSnapshotEndpointTests20,TaskLaunchEndpointE2ETests6,SpecPreviewArgvVocabularyTests10,SupervisorWholeLoopE2ETests14,PlanMapSynthFanoutFlowTests2,PlanMapDynamicFanoutFlowTests1, all green41 mutations. Each reverts one decision's key line, and the named test went red; every file was restored by copy and checked by hash.
LaunchControlResolverTests.A_repo_less_argv_floor_on_the_single_agent_lane_is_applied_…;TaskLaunchFlowTests.A_repo_less_argv_floor_on_the_single_agent_lane_launches_and_its_agent_carries_the_checkLaunchControlResolverTests.A_repo_less_argv_floor_on_the_supervisor_lane_is_refused_…LlmEffortClassifierTests.A_repo_less_operator_floor_moves_a_deep_route_to_the_single_agent_that_grades_itTaskLaunchFlowTests.An_auto_route_with_a_repo_less_floor_lands_on_the_single_agent_that_grades_it_never_on_deepLaunchControlResolverTests.A_repo_less_supervisor_launch_at_delivery_has_one_remedy_…;TaskLaunchFlowTests.A_launch_at_delivery_quality_without_an_acceptance_check_is_refused_as_its_preview_predicts(deep, no repo)AgentRunExecutorTests.An_engine_staged_run_pinned_to_a_row_whose_credential_died_runs_another_pooled_row_and_says_soand…whose_pool_has_nothing_runnable_fails_typed_once_and_is_not_respawnedAgentRunExecutorTests.An_escalated_run_is_persisted_and_reviewed_as_the_row_that_did_the_work(three separate mutations)TaskRoutePreviewFlowTests.A_pool_member_gone_since_the_composer_loaded_…(the matching row each time)TaskLaunchFlowTests.An_auto_route_to_quick_refuses_a_persona_the_allowed_pool_excludes_before_any_runAgentRunExecutorTests.An_engine_staged_run_whose_pool_has_nothing_runnable_…allowedModelIdsAgentCodeNodeTests.The_manifest_declares_the_allowed_model_pool_the_node_readsAgentRunReviseLoopFlowTests.A_run_bound_to_an_allowed_model_pool_never_escalates_outside_itAllowedModelPoolTests.A_pinned_row_is_honoured_over_a_same_named_row_on_another_credentialHarnessModelReconcilerFlowTests.The_same_model_on_another_pooled_credential_is_named_as_a_credential_moveAgentRunExecutorTests.A_bounded_run_whose_pool_resolves_nothing_fails_rather_than_run_outside_it;AgentCodeNodeTests.A_resumed_failure_carries_the_retry_verdict_for_the_engine(that row)LaunchControlResolverTests.A_malformed_floor_is_refused_by_name_…;LlmEffortClassifierTests.A_malformed_floor_is_no_routing_signalTaskLaunchFlowTests.A_repo_less_argv_floor_on_the_supervisor_lane_is_refused_with_the_verdict_its_preview_givesTaskLaunchFlowTests.A_launch_at_delivery_quality_without_an_acceptance_check_is_refused_as_its_preview_predictsTaskRoutePreviewServiceTests.Preview_reports_the_same_control_dispositions_…;TaskRoutePreviewFlowTests.The_preview_reports_the_same_control_dispositions_the_launch_then_applies;TaskLaunchFlowTests.An_explicit_standard_launch_with_an_operator_floor_is_refused_…TaskLaunchFlowTests.An_auto_route_to_quick_clamps_a_pinned_model_…;PlannerModelPoolFlowTests.The_planners_catalog_lists_only_the_allowed_pool_…SingleAgentDefinitionBuilderTests.The_allowed_model_pool_rides_…,PlanMapSynthDefinitionBuilderTests.The_allowed_model_pool_bounds_…;AgentCodeNodeTests.The_allowed_model_pool_a_projection_baked_is_carried_onto_the_taskHarnessModelReconcilerFlowTests.A_bounded_task_naming_a_model_outside_its_pool_runs_the_pools_default_row_and_says_so;AgentRunExecutorTests.A_bounded_run_naming_a_model_outside_its_pool_runs_the_pooled_row_…TaskLaunchFlowTests.An_auto_route_to_quick_refuses_a_persona_…;LaunchControlResolverTests.Every_control_the_launch_carries_gets_exactly_one_disposition_per_laneTaskLaunchContractFlowTests.A_standard_launch_carrying_an_operator_floor_is_refused_and_records_nothing;TaskLaunchFlowTests.An_auto_route_with_an_operator_floor_never_lands_on_plan_map_…TaskLaunchFlowTests.A_continue_is_routed_on_the_threads_grounding_by_both_the_preview_and_the_launch;LlmEffortClassifierTests.The_router_STILL_confirms_an_AMBIGUOUS_task_…Pins changed deliberately:
TaskRoutePreviewServiceTests.Adapter_compatibility_comes_from_the_actual_builder_and_workspace: repo-less Quick is now Compatible, andRequiresRepositoryis true only on Deep.TaskLaunchServiceQualityTierTestsmoved toLaunchControlResolverTests.The_tiers_floor_mandate_is_a_disposition_the_preview_can_predict.TaskLaunchFlowTests, the DeepArgumentExceptionpin andA_quick_launch_at_delivery_quality_is_never_rejected_for_a_missing_acceptance_checkbecame one Theory: Deep with a repository and Quick without one are told to author a check; Deep without a repository is told to bind one.TaskLaunchContractFlowTests.Every_launch_lane_records_original_controls_in_the_frozen_run_detailbinds a repository only on the Deep row (Deep's capability key becomesGitBranch); its Standard row sends no floor, with that refusal pinned in its own test.Added in the final round (each mutation of its line went red): later revise rounds and the cold-launch record keep the escalated row (
AgentRunReviseLoopFlowTests.A_later_round_that_does_not_escalate_…,AgentRunExecutorTests.A_cold_launch_that_escalated_at_dispatch_…, the stored row in…A_round_that_over_claims_…); the persona tenancy filter (TaskLaunchFlowTests.A_persona_of_another_team_outside_the_pool_…); the dispatch team barrier (AgentDefinitionResolverCredentialedModelFlowTestsTheory over revoked / disabled / foreign-team / missing,HarnessModelReconcilerFlowTests.A_pinned_row_of_another_team_…).