Conversation
…eep policy prompt out of objective - SSH Fleet host/user starting with '-' or containing whitespace/control characters are refused, and `--` now ends ssh option parsing before the destination. - The per-task wall-clock limit is checked every tick on the live worker, so a hung worker is stopped and finalized as Timeout; a worker whose exit was already observed keeps its real outcome. - [fleet.exec] append_system_prompt is delivered once via --append-system-prompt instead of also being folded into the objective, which made the registered launch spec disagree with its persisted manifest prompt and failed every Fleet task at launch. - Fleet workers keep non-secret proxy, CA bundle, temp-dir, Windows profile and locale variables through env_clear. - save_fleet refuses to replace a same-slug file that is not a saved v2 Fleet (legacy/exact fleet files are left unchanged). Tests: cargo test -p codewhale-tui --lib (fleet::host, fleet::manager, fleet::store, fleet::worker_runtime, fleet::executor): 199 passed, 0 failed. The 7 new/updated regression tests fail (7/7) with production hunks reverted. cargo fmt --check clean. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014ZwqatxgVFxHvovngywnks
…olicy text as a value, tighten save and deadline guards Follow-up to the review of the previous Fleet host/manager/store commit. - Worker base env is now built from the child_env allowlist (sanitized_runtime_env_from) instead of a second hand-written list, so CURL_CA_BUNDLE, REQUESTS_CA_BUNDLE, NODE_EXTRA_CA_CERTS, PATHEXT, WINDIR, ProgramFiles, USER, TERM, CARGO_HOME and the other keys tools already get also reach workers. Proxy URLs keep their userinfo for the worker process itself (it must reach its provider through the same proxy); every tool the worker starts still goes through sanitized_child_env, which strips it. - [fleet.exec] append_system_prompt is emitted as one --append-system-prompt=<value> argument. As a separate argument, a policy that reads exactly like an exec option (e.g. "--hooks") made clap reject the worker command. - A worker whose exit the executor already handed out is no longer "running" (worker_running_for returns None), so a later tick past the deadline cannot stop it and record Timeout over its outcome. Removed the unused marker/trap from the hung-worker test. - save_fleet replaces an existing file only when it parses as a v2 Fleet of the same name. A same-slug Fleet this build cannot read (newer revision, schema = "Fleet", mid-edit TOML) or a legacy/exact file is left unchanged with an accurate message; the shared ".tmp" name is replaced by the repository's write_atomic_workspace, so concurrent savers cannot interleave into one temp file. - SSH Fleet host names are limited to host/IP/alias characters, and user names to printable ASCII without the characters OpenSSH itself refuses on the command line. Tests: cargo test -p codewhale-tui --lib (fleet::host, fleet::manager, fleet::store, fleet::worker_runtime, fleet::executor, child_env): 226 passed, 0 failed, 0 ignored. Save-consumer set (fleet::, config::scope_tests, tools::subagent::tests::roster_routes, tools::workflow::shortlist_tests, tui::views::fleet_): 588 passed, 0 failed. With production hunks reverted (and the previous commit's manager hunk), the 8 new/updated tests fail 8/8. cargo fmt --check clean; check-dead-code-budget, check-blocking-calls-budget, check-runtime-contract-budget, check-lexicon exit 0. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014ZwqatxgVFxHvovngywnks
Move the real worker argv regression into the existing CLI test module, preserving both policy values and parser assertions without a new Fleet dependency on the top-level CLI. No module baseline or production changes. Validation: focused Rust 1 passed, 0 failed, 0 ignored; module boundary Python 23 passed; module graph baseline passed. npm test 670 passed and check:web passed at parent 1b1744b; this follow-up changes tests only. Signed-off-by: Hunter B <hmbown@gmail.com>
This branch has not been deployed
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.
No-Issue: verified bug-hunt findings
Summary
Fleet host/manager/store fixes, each re-verified on
origin/main(606c36f) before fixing, plus a second commit (1b1744b) that addresses the review of the first. Files:crates/tui/src/fleet/{host,manager,store,worker_runtime,executor}.rs,crates/tui/src/child_env.rs, and the existing CLI test module incrates/tui/src/lib.rs.1. SSH Fleet destination could be read as an ssh option (host.rs)
build_ssh_commandpassed the configuredhost/user@hoststraight to ssh as the destination, with no--and no validation. A host or user value beginning with-was parsed by ssh as an option.--is placed immediately before the destination.SshFleetHostConfig::validate(used byfrom_host_specand the adapter constructor) refuses a host or user that starts with-. A host is limited to host-name, IP-literal and alias characters (ASCII alphanumerics and. - _ : %, so no@). A user must be printable ASCII, and the characters OpenSSH itself refuses in a command-line user or host (' ` " $ \ ; & < > | ( ) { }) are rejected. Ordinary values are unchanged:fleet@builder.example.test,alice@corp.example@10.0.0.7andfe80::1%en0are all still accepted.fleet_host_ssh_refuses_option_like_or_malformed_destinationcovers option-like input, whitespace, NUL, shell characters,@in the host, a Unicode format character and non-ASCII users, and checks that ordinary destinations are accepted.fleet_host_ssh_command_uses_sendenv_without_argv_secret_valuesasserts that--directly precedes the destination.2. Per-task wall-clock limit was only checked after the worker exited (manager.rs, executor.rs)
timeout_seconds/budget.max_secondscheck sat insideif let Some(terminal) = poll_terminal_with_status(..). A hung worker never produced a terminal, so it was never stopped and the run never finished. A worker that exited cleanly but was observed just after the limit was recorded as Timeout.worker_running_fornow returnsNoneonce the executor has handed out that worker's exit. A later tick past the deadline therefore cannot stop that worker and write Timeout over the outcome, even if an earlier ledger write for the exit failed.wall_clock_limit_stops_a_hung_worker_and_records_timeout: realsleep 30worker,timeout_seconds = 1. It finishes within 10s as Failed/Timeout. The unused marker/trap scaffolding was removed.worker_exit_observed_after_the_deadline_keeps_its_real_outcome: the worker exits at 0.2s and the next tick runs at 1.5s. The receipt is Pass.consumed_worker_exit_is_not_recorded_as_a_timeout: the exit is consumed first, then a tick runs past the deadline. No terminal is recorded and no Timeout receipt is written.3.
[fleet.exec] append_system_promptfailed every Fleet task at launch (worker_runtime.rs, executor.rs)apply_exec_hardeningappended[Policy]text tospec.objectivebut not tolaunch_manifest.prompt. With the coordination manager attached (always the case forcodewhale fleet run),validate_registered_launch_specrejected the task as "inconsistent persisted prompt". Separately, the policy was passed as a separate--append-system-prompt <value>argument. A policy that reads exactly like an exec option, such as--hooks, made clap reject the worker command.--append-system-prompt=<value>argument, so clap always takes it as the value.configured_policy_prompt_keeps_registered_launch_spec_consistentandexec_hardening_leaves_policy_prompt_out_of_the_objectivecover the objective change.worker_command_policy_prompt_that_looks_like_a_flag_parsesparses the built worker argv through the realCli, once with a Markdown bullet policy and once with--hooks, and asserts the value is kept intact andhooksstays false.-bullet list already parsed with the split form. Only flag-identical text failed, and the test covers that case.4. Fleet workers lost proxy/CA/temp/toolchain environment (host.rs, child_env.rs)
env_clear()and rebuilt the env with only HOME, PATH and the Windows system root/COMSPEC. The first commit added a second hand-written list, which disagreed withchild_env.rs: it lacked PATHEXT, WINDIR, ProgramFiles, CURL_CA_BUNDLE, REQUESTS_CA_BUNDLE, NODE_EXTRA_CA_CERTS, CARGO_HOME, USER, TERM and more.child_envallowlist via the newchild_env::sanitized_runtime_env_from, so there is one allowlist. Provider keys,*_TOKEN,CARGO_REGISTRY_*andDATABASE_URLare still dropped, and telemetry is still forced off.user:password@in proxy URLs because it is Codewhale itself and must reach its provider through the same proxy as the parent. Every tool the worker starts still builds its env throughsanitized_child_env, which strips the userinfo at the model-facing boundary.worker_base_env_uses_the_child_env_allowlist_and_keeps_proxy_routefeeds a parent snapshot tobase_env_from. It asserts that 15 non-secret keys arrive unchanged, that 5 secret-shaped keys are absent, and that telemetry is forced off. It checks values, not only key names.5.
save_fleetcould replace a same-slug file that is not a readable v2 Fleet of that name (store.rs)schema = "Fleet", or a Fleet mid-edit.atomic_writealso used a fixed shared<slug>.tmp.FleetFile::parseaccepts it and its name matches. A readable v2 Fleet with another name returns NameTaken. Anything else is left unchanged, with one of two messages. A file that declares the v2 schema gets "is a Fleet file this build cannot read ()". Anything else gets "holds another fleet file (a legacy roster or exact fleet)". Both messages say to fix or move the file, or save under another name, and no longer point tomigrate.atomic_writenow uses the repository'sutils::write_atomic_workspace, which gives each write its own temp file and keeps ordinary permissions.save_refuses_to_overwrite_a_legacy_fleet_file_of_the_same_slugandsave_leaves_an_unreadable_fleet_file_of_the_same_slug_unchangedcover revision 3,Fleet, mid-edit TOML, the "cannot read" message and replacing an empty file.Testing
lib.rsCLI test module, keeping Fleet independent of the top-level CLI. Its real worker-command assertions are unchanged. After this test-only move, the focused Rust test passed 1/1, all 23 module-boundary Python tests passed, and the checked-in module graph baseline passed without modification.1b1744b:npm test670 passed (68 wrapper, 16 SDK, 50 extension-host, 536 web);npm run check:webpassed. The source and checkout stayed unchanged during these checks.cargo test -p codewhale-tui --lib -- fleet::host:: fleet::manager:: fleet::store:: fleet::worker_runtime:: fleet::executor:: child_env::gives 226 passed, 0 failed, 0 ignored (macOS, local).fleet:: config::scope_tests tools::subagent::tests::roster_routes tools::workflow::shortlist_tests tui::views::fleet_) give 588 passed, 0 failed.CURL_CA_BUNDLEmissing,builder;trueaccepted, the--hookspolicy rejected by clap, the combined flag missing, the rev-3 Fleet overwritten, Timeout recorded over a consumed exit, Timeout instead of Pass for a late-observed exit, and the hung worker not stopped within 10s.cargo fmt --all -- --checkis clean.check-dead-code-budget,check-blocking-calls-budget,check-runtime-contract-budget,check-lexiconandcheck-persistence-backlog-budgetall pass.🤖 Generated with Claude Code
https://claude.ai/code/session_014ZwqatxgVFxHvovngywnks