Skip to content

release: Codewhale v0.9.4 release train - #5135

Merged
Hmbown merged 513 commits into
mainfrom
agent/v094-release-train-20260802
Aug 6, 2026
Merged

release: Codewhale v0.9.4 release train#5135
Hmbown merged 513 commits into
mainfrom
agent/v094-release-train-20260802

Conversation

@Hmbown

@Hmbown Hmbown commented Aug 3, 2026

Copy link
Copy Markdown
Owner

What this is

The v0.9.4 integration train. Supersedes #5044 (its head 92128e66 is an ancestor of this branch — all of the 2026-08-01 source candidate is contained here).

Currently 77 commits ahead of main: the 18 train commits on 6860a40eb556 (see FINISH-0.9.4.md §0 in codewhale-ops) plus the main-line work they build on.

Landing strategy

Per-concern stacked PR chains land on this branch (never directly on main), per docs/RELEASE_QUEUE.md:

Each stack PR carries its own gates + receipts. This train PR merges to main only when the release gates in docs/RELEASE_CHECKLIST.md are all green.

Gates so far

  • cargo test -p codewhale-tui --bin codewhale-tui baseline: 9589 pass / 2 fail (pre-existing config::credential_scope_tests::* only)
  • cargo clippy --workspace --all-targets --all-features --locked -- -D warnings clean at 82b6c27d6
  • cargo check --workspace --all-targets --locked PASS
  • Full cargo test --workspace --all-features --locked: pending (tracked in the 0.9.4 program ledger)

Nothing here is tagged or released; 0.9.4 has not shipped.

No-Issue: release train container; each stacked PR closes its own issue.

Copilot AI review requested due to automatic review settings August 3, 2026 01:54
@cursor

cursor Bot commented Aug 3, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@gitguardian

gitguardian Bot commented Aug 3, 2026

Copy link
Copy Markdown

⚠️ GitGuardian has uncovered 1 secret following the scan of your pull request.

Please consider investigating the findings and remediating the incidents. Failure to do so may lead to compromising the associated services or software components.

🔎 Detected hardcoded secret in your pull request
GitGuardian id GitGuardian status Secret Commit Filename
35796438 Triggered JSON Web Token 918aa8c crates/lane/src/control.rs View secret
🛠 Guidelines to remediate hardcoded secrets
  1. Understand the implications of revoking this secret by investigating where it is used in your code.
  2. Replace and store your secret safely. Learn here the best practices.
  3. Revoke and rotate this secret.
  4. If possible, rewrite git history. Rewriting git history is not a trivial act. You might completely break other contributing developers' workflow and you risk accidentally deleting legitimate data.

To avoid such incidents in the future consider


🦉 GitGuardian detects secrets in your source code to help developers and security teams secure the modern development process. You are seeing this because you or someone else with access to this repository has authorized GitGuardian to scan your pull request.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot wasn't able to review this pull request because it exceeds the maximum number of lines (20,000). Try reducing the number of changed lines and requesting a review from Copilot again.

This was referenced Aug 3, 2026
@claude

claude Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Reviewing PR #5135 (v0.9.4 release train)

Todo list

  • Gather context (diff vs origin/main, repo guidance)
  • Review correctness/panics in core changes (config, provider, engine, fleet)
  • Check provider/model/route safety invariant (EPIC v0.8.65 EPIC: Separate provider facts, model facts, offerings, and route resolution #2608)
  • Check reuse/simplification opportunities
  • Check test coverage for new behavior
  • Check security (secrets, execpolicy deletion, tests.rs secret findings from GitGuardian)
  • Post final review summary

View job run

@claude

claude Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Reviewing PR #5135 (v0.9.4 release train)

Todo list

  • Gather context (diff vs origin/main, repo guidance)
  • Review correctness/panics in core changes (config, provider, engine, fleet)
  • Check provider/model/route safety invariant (EPIC v0.8.65 EPIC: Separate provider facts, model facts, offerings, and route resolution #2608)
  • Check reuse/simplification opportunities
  • Check test coverage for new behavior
  • Check security (secrets, execpolicy deletion, tests.rs secret findings from GitGuardian)
  • Post final review summary

View job run

@claude

claude Bot commented Aug 3, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Hmbown's task in 10m 30s —— View job


Reviewing PR #5135 (v0.9.4 release train)

Todo list

  • Gather context (diff vs origin/main, repo guidance)
  • Review correctness/panics in core changes (config, provider, engine, fleet)
  • Check provider/model/route safety invariant (EPIC v0.8.65 EPIC: Separate provider facts, model facts, offerings, and route resolution #2608)
  • Check reuse/simplification opportunities
  • Check test coverage for new behavior
  • Check security (secrets, execpolicy deletion, tests.rs secret findings from GitGuardian)
  • Post final review summary

Summary

Large but disciplined release train (219 files, +23355/-8848 vs origin/main). Reviewed by area: config/provider/route, core/engine/fleet/compaction, client/cli/app-server, and secrets/paths/mcp/config persistence. Overall this is well-tested, well-documented work — one confirmed blocking regression, everything else is non-blocking or nits.

Blocking

Silent Anthropic-dialect regression via codewhale config set provider / CODEWHALE_PROVIDERinline comment on crates/config/src/provider.rs:614.

The new "one vendor row" alias-collapse design moves legacy dialect strings (deepseek-anthropic, deepseek_claude, mini-max-anthropic, modelstudio-*-anthropic, modelstudio-coding-plan*, …) onto the base provider's aliases() list and empties the dialect struct's own list. ProviderKind::parse() (provider_kind.rs:257) matches providers in registry order, so these strings now resolve to the base kind (e.g. Deepseek) instead of the dialect kind (DeepseekAnthropic). That path feeds ConfigToml::set_value("provider", …) (config/src/lib.rs:2497, called from codewhale config set provider … and CODEWHALE_PROVIDER), which only sets self.provider — it never sets wire. Since wire_prefers_anthropic() (lib.rs:3828) only fires for the literal dialect enum kinds or an explicit wire = "anthropic", a user who previously selected the Anthropic Messages endpoint this way now silently gets the default (non-Anthropic) endpoint instead, with no error. The TOML config-file path is unaffected (separate #[serde(alias = …)] mechanism on the enum, still correct) — only the CLI/env alias path regressed, and it has no test coverage for this specific gap (the new test deepseek_anthropic_aliases_collapse_onto_primary_with_wire_toggle covers parse() in isolation and TOML/legacy-enum resolution, but not set_valueresolve_runtime_options end-to-end). Verified directly by reading the code, not just static analysis.

Non-blocking

  • crates/core/src/lib.rs:647 — resume-history dedup uses a HashSet<String> content fingerprint; legitimately repeated identical history items could be silently dropped alongside the intended idempotent-resume case. Inline comment.
  • crates/tui/src/client/chat.rs:1749 — the "already has a spillover footer" passthrough check is a literal substring match against tool output; a tool result whose data happens to contain that exact string would skip truncation.
  • crates/tui/src/config_persistence.rs:648-668config_toml_path no longer falls back from a missing CODEWHALE_CONFIG_PATH target to an existing home config; the env override is now authoritative even when the file doesn't exist yet. Intentional fail-closed behavior change, but user-visible (a stale/typo'd env var now creates a new file there instead of using the existing home config) — worth a changelog line if not already present.
  • crates/tui/src/config/subagent_limits.rs:20-36 — default/max subagent API timeout raised 120s→600s, cap 1800s→3600s. Reasonable but reads as scope creep against the stated "secrets/paths" concern grouping; fine if intentional.
  • crates/config/assets/models_dev.bundled.json — minor data drift: Coding Plan rows list qwen3.7-plus output as 64000 vs Token Plan's 65536, but mode=coding-plan doesn't get its own bundled_offerings() row in route/offering.rs, so a Coding Plan user via the mode toggle gets Token Plan's (slightly different) limits from the hand-seam while the JSON fallback catalog has the more accurate number. Low impact.
  • crates/tui/src/core/engine/turn_loop.rs (~1525/3397) — no_progress_warning_started_at isn't reset when stuck_guard.observe() internally resets an episode via the assistant-no-tool branch; a later warning can report an inflated elapsed time. Cosmetic/diagnostic only.
  • crates/tui/src/core/engine/stuck_guard.rs:257-268reset_episode() no longer clears alternation_repeats/step_history (documented, intentional). Harmless at the default alternation_warn_threshold = 1, but would accumulate unboundedly across unrelated cycles if that threshold were ever raised — worth a comment noting the dependency.
  • crates/paths/src/lib.rsvalidate_absolute_path treats a ~\ prefix as home-expansion on all platforms including Unix, where \ isn't a path separator. Harmless in practice.

Nits

  • crates/config/src/provider.rs:1247inline comment: ModelstudioTokenPlan/ModelstudioCodingPlan hand-roll impl Provider boilerplate the provider! macro already covers (neither overrides wire_policy); only the two *Anthropic dialect variants need the hand-written form, matching the DeepseekAnthropic/MinimaxAnthropic pattern.
  • crates/tui/src/core/engine/turn_loop.rs:2587-2593compact_no_progress_diagnostic hand-builds a JSON string with a single "\" replacement instead of serde_json::json!; a reason containing a backslash would produce invalid JSON in a log line. Log-only, not parsed.

Security / secrets

  • GitGuardian's two flagged lines in crates/tui/src/doctor/tests.rs (233/245): confirmed false positive. config_credential_shaped_keys (doctor.rs:162) is a key-name-only lint — it never returns or logs values, and its only caller (main.rs:3682) prints key names, not secrets. The flagged strings are deliberately low-entropy test fixtures for the new detector, already documented with a comment explaining why.
  • crates/tui/src/execpolicy/{decision,error,execpolicycheck,parser,parser_ohos,policy,rule}.rs deletion: confirmed intentional (documented in execpolicy/mod.rs as removal of an unenforced legacy Starlark policy engine, matches the PR description's "execpolicy deletion"). Grepped the workspace — no remaining references to the deleted types/modules outside the deleted files themselves.
  • crates/secrets/src/lib.rs: real fixes — the macOS/Windows keyring probe previously short-circuited to Ok(()) without touching the backend (dead code, made the file-store fallback unreachable); now does a genuine no-op-safe probe read. Plaintext-fallback-on-secret-store-failure was removed in favor of failing loudly. file_backed_read_only() now fails closed instead of reading a cwd-relative store (prevents a planted-file attack), with a direct regression test.
  • crates/paths/src/lib.rs: the home-dir bug fix correctly rejects relative CODEWHALE_HOME/CODEWHALE_CONFIG_PATH and expands ~ before validating absoluteness; API changed to Result, and every caller checked handles the Err case rather than unwrapping.
  • crates/mcp/src/lib.rs: call_tool now enforces ToolFilter at invocation time, not just at listing — closes a real bypass where a denylisted tool was callable directly.
  • crates/cli/src/lib.rs: credential writes correctly redirected away from workspace-scoped .codewhale/config.toml to the user-global config; logout now clears keyring entries for all providers.
  • No command-injection, unsafe deserialization, or secret-logging issues found in the reviewed files.

Good design worth calling out

  • authority.rs's workspace write carve-out fails closed at every uncertain step and checks lexical containment before resolving symlinks (TOCTOU-aware, with a symlink-escape regression test).
  • capability_badges.rs's exact_builtin_provider requires an exact canonical id match rather than prefix/substring matching — correct pattern for the provider-safety invariant.
  • anthropic.rs's is_native_anthropic_base_url/compat_thinking_budget gate behavior on base_url host, not model name — compliant with the provider/route safety invariant.
  • cloud.rs token-refresh fix: only wipes the local session on a genuine 401 now, preserving it across transient errors (403/429/5xx/transport failures) — previously any non-200 nuked the login.
  • state/lib.rs SQLite migration steps are now guarded by column_exists, fixing a real crash on restored/racing databases with a stale user_version.
  • No provider/model-prefix-inference violations found anywhere else in the diff — the rest of the new Modelstudio/dialect-collapse work correctly routes wire/mode selection through explicit config fields.

Could not run cargo build/cargo test/clippy in this sandbox (network/tooling restricted); all findings are from direct code reading, not automated tooling. The one blocking finding was independently verified by tracing the actual call path, not just flagged by static review.
· branch

"deepseekcn",
"deepseek-china",
// Dialect is wire=anthropic on this provider, not a second catalog row.
"deepseek-anthropic",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Blocking — silently drops the user's explicit Anthropic-dialect selection via codewhale config set provider / CODEWHALE_PROVIDER.

ProviderKind::parse() (provider_kind.rs:257) does .find() over PROVIDER_REGISTRY in order and returns the first provider whose id() or aliases() matches. Since Deepseek (this struct) is registered before DeepseekAnthropic, and "deepseek-anthropic"/"deepseek_anthropic"/"deepseek-claude"/"deepseek_claude" were moved here from DeepseekAnthropic::aliases() (now &[] at line 662), ProviderKind::parse("deepseek-anthropic") now resolves to ProviderKind::Deepseek, never DeepseekAnthropic.

That matters because the CLI/env path (ConfigToml::set_value("provider", …) in config/src/lib.rs:2497-2508, used by codewhale config set provider deepseek-anthropiccli/src/lib.rs:3684 — and by CODEWHALE_PROVIDER=deepseek-anthropic, lib.rs:6355-6357) only does self.provider = provider; it never sets the wire field. wire_prefers_anthropic() (lib.rs:3828) only returns true for the literal DeepseekAnthropic/MinimaxAnthropic/ModelstudioTokenPlanAnthropic/ModelstudioCodingPlanAnthropic kinds or an explicit wire = "anthropic" config value — neither is set here. So a user who previously ran codewhale config set provider deepseek-anthropic (or set the env var) got the Anthropic Messages endpoint; after this change they silently get the default DeepSeek (Chat Completions) endpoint instead, with no error or warning. Same issue applies to mini-max-anthropic/mini_max_anthropic on MinimaxAnthropic and all the modelstudio-*-anthropic/modelstudio-coding-plan* aliases collapsed onto ModelstudioTokenPlan.

Note the TOML config-file path (toml::from_str("provider = \"deepseek-anthropic\"")) is not affected — that uses the separate #[serde(alias = …)] attributes on the ProviderKind enum (provider_kind.rs:22-27), which still correctly map to DeepseekAnthropic. Only the Provider::aliases()-driven ProviderKind::parse() path used by the CLI config set command and env var regressed.

The new test deepseek_anthropic_aliases_collapse_onto_primary_with_wire_toggle (config/src/tests.rs:4321) asserts the collapsed parse() result as intended design, and separately tests wire = "anthropic" via TOML and the legacy ProviderKind::DeepseekAnthropic enum value directly — but never exercises set_value("provider", "deepseek-anthropic") end-to-end through resolve_runtime_options, so this regression has no test coverage either way.

Suggested fix: have set_value's "provider" branch (and the CODEWHALE_PROVIDER env parsing) additionally derive and persist wire/mode when the matched alias belongs to a legacy dialect/plan kind, or keep ProviderKind::parse resolving those specific alias strings to the dialect kind and translate to wire at the ConfigTomlresolve_runtime_options boundary instead of at parse time.

Fix this →

Comment thread crates/core/src/lib.rs
// fingerprint (the item's JSON, matching what append_message
// stores as content) against the persisted chain and against
// items already appended in this loop.
let mut seen: HashSet<String> = self

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Non-blocking: resume-history dedup uses a HashSet<String> content fingerprint rather than a multiset/positional check. Legitimately repeated identical history items (e.g. two separate identical no-arg tool calls, or repeated short user replies) can be silently dropped instead of appended, on top of the intended (and correctly working) idempotent-resume case. Worth a follow-up if repeated-identical-turns are a real occurrence in practice.

aliases: ["telecom-js", "telecom_js", "telecomjs-cn", "tokenhub"]
);

/// Alibaba Cloud Model Studio — Token Plan (OpenAI-compatible Chat Completions).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit / reuse: ModelstudioTokenPlan and ModelstudioCodingPlan don't override wire_policy() (both default to Fixed(ChatCompletions), same as the provider! macro's default), yet are hand-written impl Provider blocks duplicating the same boilerplate the macro already generates for NvidiaNim, Telecomjs, etc. Only the two *Anthropic variants need a hand-written impl (to override wire_policy), matching the existing DeepseekAnthropic/MinimaxAnthropic pattern — consider using the provider! macro for the two base variants.

The one-way ownership ratchet failed the release train on the v0.9.4
Fleet work. Reviewed and re-baselined:

- aggregate owned Rust 662389 -> 668365
- three new 1000-line modules admitted: fleet/store.rs (1114, the named
  Fleet store v2), tui/transcript.rs (1062, the release-quality
  transcript pass), views/fleet_detail.rs (1056, the secondary
  named-Fleet editor behind /fleet fleets)

Also tightened rather than left slack: seven modules have since split
below the threshold (cli/cloud.rs, client/responses.rs,
session_control_acceptance.rs, skills/system.rs, tui/footer_ui.rs,
widgets/footer.rs, widgets/header.rs) and are dropped from the
allowlist, so the count lands at 168 and the largest module at 19087.
All three new modules are recorded as v0.9.5 split candidates.

Verified: scripts/check-source-structure-budget.py -> PASS with no
"can tighten" remainder.
@claude

claude Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Hmbown's task in 1m 17s —— View job


Reviewing PR #5135 (v0.9.4 release train) — re-review after synchronize

Todo list

  • Identify what changed since the last review (07:02 UTC, tip b17748137)
  • Re-verify the previously-fixed provider-alias/wire regression still holds
  • Scan newly added commits for new issues
  • Post final review summary

What changed since the last review

Three small commits landed on top of b17748137, all CI/hygiene bookkeeping — no runtime logic changes:

  • 0e0edb937 — splits nested_repo_and_symlinked_worktree_do_not_change_readiness (crates/tui/src/config/scope_tests.rs) into a portable nested_repo_does_not_change_readiness and a #[cfg(unix)]-gated symlinked_worktree_does_not_change_readiness, sharing a fixture writer and assertion helper. Fixes a real Windows compile failure (std::os::unix::fs::symlink was called unconditionally). Verified both legs still exercise the same assertion (assert_user_global_survives_workspace), so no coverage was lost splitting them.
  • 1ef865cb7 — pure cargo fmt output on fleet.rs/fleet_list.rs/fleet_roster/tests.rs; diffed each hunk and confirmed it's reformatting only (list wrapping, single-line collapse), no logic touched.
  • b2b4bb4af — re-baselines scripts/source-structure-budget.json for the Fleet rebuild: aggregate ceiling 662389→668365, admits three new >1000-line modules (fleet/store.rs, tui/transcript.rs, views/fleet_detail.rs), and drops seven modules from the allowlist that shrank back under the threshold. Spot-checked the numbers directly against the tree: the three admitted modules are exactly 1114/1062/1056 lines as claimed, and all seven dropped modules (cloud.rs 995, client/responses.rs 926, session_control_acceptance.rs 978, skills/system.rs 480, footer_ui.rs 160, widgets/header.rs 110, widgets/footer.rs deleted) are now genuinely under 1000 lines or gone — the ratchet update is honest, not just asserted.

Blocking issue: still fixed, untouched by this batch

crates/config/src/provider.rs, lib.rs, secrets, paths, mcp, and execpolicy/lane are all untouched in this diff (confirmed empty). The Anthropic-dialect alias-collapse fix (4a5fc257) still stands as verified in every review since 04:44 UTC.

Previously-flagged items

Unchanged and not touched by this batch — resume-history dedup (crates/core/src/lib.rs), the spillover-footer substring check (crates/tui/src/client/chat.rs), the provider! macro reuse nit (crates/config/src/provider.rs:1247), and the sanitize_line over-redaction finding in crates/lane/src/control.rs (flagged 06:05 UTC) all still stand as originally written.

Net assessment

This synchronize is pure CI/hygiene housekeeping (a Windows-compile fix, a formatting pass, and a reviewed structure-budget update) — no new findings, no logic changed. The train's substantive state is unchanged since the last content review.

cargo build/cargo test/clippy remain non-runnable in this sandbox (command execution requires interactive approval not available here) — findings are from direct code reading, diffing, and manually cross-checking the budget file's line-count claims against the actual tree.
· branch

Hmbown added 2 commits August 5, 2026 00:39
The #5123 fail-closed check keyed on `agent_type_explicit`, which is set
by both `type` and `role`. That rejected every read-only spawn that names
a role, and broke four tests plus the whole read-only Workflow leaf path:

  spawn rejected: builder implies write capability;
  write_authority=read_only is a contradiction.

`role` and `type` are not the same claim:

- `role: "release_lead"` is a roster id. It is copied into `profile` only
  as a lookup key and the member is not resolved until
  `apply_spawn_profile`, so at validation time the agent type is still the
  default Worker and the role says nothing about write capability.
- `role: "implementer"` is a type alias, but still an identity. A Fleet
  role and its authority posture are independent — an acceptance workflow
  resolves `implementer` to its saved profile while narrowing that child
  to the read-only tool set, which is what `leaf_allowed_tools` enforces.
- `type: "builder"` *is* a capability claim, and pairing it with
  read_only is the #5123 lie: a child labeled builder holding only recon
  tools, which self-BLOCKs after burning a turn.

So the guard now keys on a new `agent_type_named` (the caller wrote
`type`), leaving `agent_type_explicit` alone for profile-conflict
resolution. The Workflow driver correspondingly stops emitting a
write-capable `type` on a read_only leaf that already carries a
role/profile — the role resolves the identity and the redundant type was
the only thing making the leaf look like a write claim.

Contract change worth naming: `type: "implementer"` + read_only now fails
where it used to parse. That spelling is an alias of `builder`, so the
older "implementers can be narrowed" contract and #5123 asserted opposite
verdicts on the same role. Narrowing is preserved through `role`; the two
tests that covered it now use `role`, with their assertions unchanged.

Verified: the 4 tests #5123 broke plus 3 new/updated ones pass
(builder_or_worker_plus_read_only_authority_fails_closed,
roster_role_plus_read_only_authority_still_spawns,
read_only_roles_reject_write_authority_but_implementers_can_be_narrowed,
declared_read_only_write_roles_derive_without_mutating_shell,
terminal_blocked_gate_fails_workflow_finalization,
stopship_acceptance_fixture_emits_role_gate_and_terminal_receipts).
cargo fmt + cargo clippy --workspace --all-targets -D warnings clean.
The heading said 2026-08-04; the release is cut on 2026-08-05. Also
record what the spawn contract actually landed as: `type` is the
capability claim that can contradict write_authority, `role` is an
identity that may still be narrowed to read-only, and callers who wrote
`type: "implementer"` for that narrowing need `role: "implementer"`.

Verified: web tests 250/250 (they assert a dated section), and
scripts/release/generate-release-body.sh 0.9.4 renders.
@claude

claude Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Hmbown's task in 1m 53s —— View job


Reviewing PR #5135 (v0.9.4 release train) — re-review after synchronize

Todo list

  • Identify what changed since the last review (07:18 UTC, tip b2b4bb4af)
  • Re-verify the previously-fixed provider-alias/wire regression still holds
  • Scan newly added commits for new issues
  • Post final review summary

What changed since the last review

Two commits landed on top of b2b4bb4af:

  • 5d6b6e413 "fix(agent): a Fleet role is an identity, not a claim of write capability" — a real fix to the Agent spawn surface has too many knobs — labeled builder runs read-only and self-BLOCKED #5123 fail-closed check reviewed earlier in this cycle. That check had keyed on agent_type_explicit, which is set by either type or role, so it wrongly rejected every read-only spawn that names a role (breaking 4 tests and the entire read-only Workflow-leaf path). This commit adds a narrower agent_type_named flag (true only when the caller wrote type itself) and re-keys the contradiction check on that instead, since role is an identity/roster-lookup key (resolved later in apply_spawn_profile) while type is the actual capability claim that can contradict write_authority=read_only. The companion workflow.rs change stops the Workflow driver from emitting a redundant write-capable type on a read-only leaf that already carries a role/profile.
  • 994aaf951 "docs(changelog): date 0.9.4 to the ship date and refine the Agent spawn surface has too many knobs — labeled builder runs read-only and self-BLOCKED #5123 entry" — changelog-only: corrects the release date 2026-08-04→2026-08-05 and documents the type vs role distinction from the commit above. No code change.

Verified the fix directly

  • named_write_role = request.agent_type_named (subagent/mod.rs) correctly narrows the Agent spawn surface has too many knobs — labeled builder runs read-only and self-BLOCKED #5123 guard to only fire when type was explicitly written and resolves to Builder/Worker — an explicit type: "worker" + read_only still correctly fails (unaffected by this change, since agent_type_named is still true in that case), while role: "release_lead" + read_only and role: "implementer" + read_only now correctly parse.
  • The intentional contract change is called out clearly in both the commit message and changelog: type: "implementer" + read_only now fails (it's an alias of builder, a real capability claim), where the equivalent narrowing must now be spelled role: "implementer".
  • New/updated tests directly pin the distinction: roster_role_plus_read_only_authority_still_spawns (new) asserts both a bare roster id and a type-alias role parse successfully with agent_type_named == false; read_only_roles_reject_write_authority_but_implementers_can_be_narrowed and declared_read_only_write_roles_derive_without_mutating_shell were updated from type: "implementer" to role: "implementer" to match the new contract, with assertions otherwise unchanged.
  • workflow.rs's leaf_subagent_type correctly scopes the type-suppression to exactly the read-only + Implementer + (role or profile present) case — it doesn't broadly suppress type for other agent types, so this doesn't weaken the guard elsewhere.

No correctness, provider/route-safety, or security concerns in either commit — this is a well-diagnosed, well-tested tightening of a fail-closed check that had over-fired, not a loosening of it.

Blocking issue: still fixed, untouched by this batch

Neither commit touches crates/config/src/provider.rs, lib.rs, secrets, paths, mcp, execpolicy, or lane (confirmed empty diff on those paths). The Anthropic-dialect alias-collapse fix (4a5fc257) still stands as verified in every review since 04:44 UTC on 2026-08-04.

Previously-flagged non-blocking items and nits

Unchanged and not touched by this batch — resume-history dedup (crates/core/src/lib.rs), the spillover-footer substring check (crates/tui/src/client/chat.rs), the provider! macro reuse nit (crates/config/src/provider.rs:1247), and the sanitize_line over-redaction finding in crates/lane/src/control.rs (flagged 2026-08-05T06:05 UTC) all still stand as originally written.

Net assessment

This synchronize is a small, well-scoped fix to the #5123 guard (it had over-fired on role-named read-only spawns, breaking the read-only Workflow-leaf path) plus a matching changelog update. No new findings. The train's overall state is unchanged since the last content review: the originally-blocking provider/wire issue remains fixed with solid regression coverage.

cargo build/cargo test/clippy remain non-runnable in this sandbox (command execution requires interactive approval not available here) — all findings are from direct code reading and test-diff verification.
· branch

Hmbown added 2 commits August 5, 2026 00:42
The README body already said v0.9.4 while the three eval examples still
passed --harness.version 0.9.1, which would resolve a two-release-old
runtime companion set.
scripts/sync-changelog.sh regenerates crates/tui/CHANGELOG.md from the
root file; the refined #5123 wording had only landed in the root, which
failed the Version drift gate.
@claude

claude Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Hmbown's task in 50s —— View job


Reviewing PR #5135 (v0.9.4 release train) — re-review after synchronize

Todo list

  • Identify what changed since the last review (07:41 UTC, tip 5d6b6e413/994aaf951)
  • Re-verify the previously-fixed provider-alias/wire regression still holds
  • Scan newly added commits for new issues
  • Post final review summary

What changed since the last review

Two small docs-only commits landed on top of 994aaf951:

Both are pure documentation/changelog sync — zero Rust/TypeScript source touched (confirmed via git diff b2b4bb4af..HEAD --stat: only CHANGELOG.md, crates/tui/CHANGELOG.md, and integrations/verifiers-codewhale/README.md, plus the already-reviewed 5d6b6e413/994aaf951 diffs to crates/tui/src/tools/subagent/{mod.rs,tests.rs} and workflow.rs).

Blocking issue: still fixed, untouched by this batch

crates/config/src/provider.rs, lib.rs, secrets, paths, mcp, execpolicy, and lane all show zero diff since the last review (git diff b2b4bb4af..HEAD --stat scoped to those crates is empty). The Anthropic-dialect alias-collapse fix (4a5fc257) still stands as verified in every review since 2026-08-04T04:44 UTC.

Previously-flagged non-blocking items and nits

Unchanged and not touched by this batch — resume-history dedup (crates/core/src/lib.rs), the spillover-footer substring check (crates/tui/src/client/chat.rs), the provider! macro reuse nit (crates/config/src/provider.rs:1247), and the sanitize_line over-redaction finding in crates/lane/src/control.rs (flagged 2026-08-05T06:05 UTC) all still stand as originally written.

Net assessment

This synchronize is pure documentation/version-string bookkeeping — no code changed, no new findings. The train's substantive state is unchanged since the last content review: the originally-blocking provider/wire issue remains fixed with solid regression coverage.

cargo build/cargo test/clippy remain non-runnable in this sandbox (command execution requires interactive approval not available here) — findings are from direct code/diff reading.

· branch

`refresh_system_prompt_is_noop_when_unchanged` failed once in a full
`cargo test --workspace` run and passed in isolation. The composed stable
prompt reads ambient process state, and this test was the odd one out
among the 38 env-sensitive tests in the file: without `lock_test_env()` a
concurrent test mutating the environment between the two refreshes shifts
the hash, so the no-op assertion fails for reasons unrelated to the code
under test.

Not a product regression — a test-isolation defect that made the release
CI nondeterministic.
@claude

claude Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Hmbown's task in 48s —— View job


Reviewing PR #5135 (v0.9.4 release train) — re-review after synchronize

Todo list

  • Identify what changed since the last review (07:45 UTC, tip 4f69cd9ae)
  • Re-verify the previously-fixed provider-alias/wire regression still holds
  • Scan the new commit for new issues
  • Post final review summary

What changed since the last review

One new commit landed on top of 4f69cd9ae:

  • e6f8245ac "test(engine): serialize the system-prompt no-op test with the env lock" — adds a single let _lock = lock_test_env(); to refresh_system_prompt_is_noop_when_unchanged (crates/tui/src/core/engine/tests.rs:13109). The commit message diagnoses a real, well-understood CI flake: the composed system prompt reads ambient process env state, and this test was the one env-sensitive test in the file that wasn't serialized against the other 38 via the existing lock_test_env() helper — a concurrent test mutating the environment mid-test could shift the hash and fail the no-op assertion for reasons unrelated to the code under test.

Verified directly: lock_test_env() is a real, already-used helper (crates/tui/src/test_env_lock.rs:159) used identically elsewhere in this file. The change is a pure test-isolation fix — no production code touched, no assertions weakened, matches the pattern used by every other env-sensitive test in the same file.

Blocking issue: still fixed, untouched by this batch

crates/config/src/provider.rs, lib.rs, secrets, paths, mcp, execpolicy, and lane all show zero diff since the last review (git diff 4f69cd9ae..HEAD scoped to those crates is empty). The Anthropic-dialect alias-collapse fix (4a5fc257) still stands as verified in every review since 2026-08-04T04:44 UTC.

Previously-flagged non-blocking items and nits

Unchanged and not touched by this commit — resume-history dedup (crates/core/src/lib.rs), the spillover-footer substring check (crates/tui/src/client/chat.rs), the provider! macro reuse nit (crates/config/src/provider.rs:1247), and the sanitize_line over-redaction finding in crates/lane/src/control.rs (flagged 2026-08-05T06:05 UTC) all still stand as originally written.

Net assessment

This synchronize is a single, well-diagnosed test-flake fix — no production code changed, no new findings. The train's substantive state is unchanged since the last content review: the originally-blocking provider/wire issue remains fixed with solid regression coverage.

cargo build/cargo test/clippy remain non-runnable in this sandbox (command execution requires interactive approval not available here) — this finding is from direct code reading.
· branch

The earlier re-baseline was measured before the role/type spawn fix, its
regression tests, and the env-lock line landed, so the ratchet caught
this lane's own growth: 668365 -> 668400.

Verified: scripts/check-source-structure-budget.py -> PASS.
@claude

claude Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Hmbown's task in 40s —— View job


Reviewing PR #5135 (v0.9.4 release train) — re-review after synchronize

Todo list

  • Identify what changed since the last review (07:49 UTC, tip e6f8245ac)
  • Re-verify the previously-fixed provider-alias/wire regression still holds
  • Scan the new commit for new issues
  • Post final review summary

What changed since the last review

One new commit landed on top of e6f8245ac:

  • c02280ff7 "fix(ci): fold this lane's own 35 lines into the structure ceiling" — bumps scripts/source-structure-budget.json's max_total_owned_rust_lines 668365→668400 (+35) and updates the accompanying _todo_2026_08_03 note to match. The prior re-baseline (b2b4bb4af, reviewed at 07:18 UTC) was measured before the Agent spawn surface has too many knobs — labeled builder runs read-only and self-BLOCKED #5123 role/type spawn fix (5d6b6e413, +79/-5 across subagent/mod.rs/tests.rs/workflow.rs) and the env-lock test-isolation fix (e6f8245ac, +4) landed on top of it, so the ratchet caught this lane's own growth. Commit message states scripts/check-source-structure-budget.py was re-run and passes.

Pure numeric bookkeeping — no logic, tests, or behavior touched. Both source commits being accounted for (5d6b6e413, e6f8245ac) were already reviewed in full at 07:41 and 07:49 UTC with no issues found.

Blocking issue: still fixed, untouched by this commit

crates/config/src/provider.rs, lib.rs, secrets, paths, mcp, execpolicy, and lane all show zero diff since the last review. The Anthropic-dialect alias-collapse fix (4a5fc257) still stands as verified in every review since 2026-08-04T04:44 UTC.

Previously-flagged non-blocking items and nits

Unchanged and not touched by this commit — resume-history dedup (crates/core/src/lib.rs), the spillover-footer substring check (crates/tui/src/client/chat.rs), the provider! macro reuse nit (crates/config/src/provider.rs:1247), and the sanitize_line over-redaction finding in crates/lane/src/control.rs (flagged 2026-08-05T06:05 UTC) all still stand as originally written.

Net assessment

This synchronize is a single-line budget-ceiling correction with no code, logic, or test changes — no new findings. The train's substantive state is unchanged since the last content review: the originally-blocking provider/wire issue remains fixed with solid regression coverage.

cargo build/cargo test/clippy remain non-runnable in this sandbox (command execution requires interactive approval not available here) — this finding is from direct code/diff reading.
· branch

Third and last conflict from the #5123 fail-closed rule. The release QA
contract `release_four_read_only_fleet_roles_launch_with_canonical_prompts`
launches worker, scout, reviewer, and verifier — it calls that set "the
four canonical read-only Fleet roles" — and the worker stopped launching
at all (`worker did not launch once: 0 != 1`).

Worker is the unnamed default role; it renders as `general` and takes its
capability from `write_authority`, not from its name, so a read-only
worker is an ordinary general-purpose child. Builder is the role that
actually claims write capability, and it is the one the #5123 transcript
shows self-BLOCKING — the worker in that same transcript ran fine.

So the check is now exactly: caller wrote `type`, that type resolves to
Builder (either spelling), and authority is read_only. Everything else —
`type=worker` + read_only, any `role` + read_only — stays legal.

Verified: release_four_read_only_fleet_roles_launch_with_canonical_prompts
passes; builder_plus_read_only_authority_fails_closed covers both the
`builder` and `implementer` spellings; new
read_only_worker_is_an_ordinary_general_child pins the canonical case;
the workflow, roster-role, and narrowing contracts still pass (8 total).
cargo fmt clean; structure ceiling folded to 668406.
@claude

claude Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Hmbown's task in 1m 9s —— View job


Reviewing PR #5135 (v0.9.4 release train) — re-review after synchronize

Todo list

  • Identify what changed since the last review (07:58 UTC, tip c02280ff7)
  • Re-verify the previously-fixed provider-alias/wire regression still holds
  • Review the new commit for correctness/safety issues
  • Post final review summary

What changed since the last review

One new commit landed on top of c02280ff7:

  • 2dede1391 "fix(agent): only type=builder contradicts read_only, not type=worker" — the third and last narrowing of the Agent spawn surface has too many knobs — labeled builder runs read-only and self-BLOCKED #5123 fail-closed rule. The release QA contract release_four_read_only_fleet_roles_launch_with_canonical_prompts (crates/tui/tests/release_runtime_qa.rs:799) calls worker/scout/reviewer/verifier "the four canonical read-only Fleet roles," and the previous check (named_write_role && matches!(agent_type, Builder | Worker)) was rejecting type=worker + read_only, breaking that contract (worker did not launch once: 0 != 1).

Verified the fix directly

  • FleetRole::Worker (crates/tui/src/tools/subagent/mod.rs:426) is the #[default] variant, distinct from Builder (line 437) — Worker is the unnamed default role whose capability comes from write_authority, not from its name, so a read-only worker is legitimately just an ordinary general-purpose child, not a contradiction.
  • The check is now exactly request.agent_type_named && request.agent_type == FleetRole::Builder && request.write_authority == Some(SpawnWriteAuthority::ReadOnly) (mod.rs:10790 area) — narrower than before, but still correctly catches both type: "builder" and its implementer alias (both resolve to FleetRole::Builder), which is the actual capability claim that contradicted read_only in the original Agent spawn surface has too many knobs — labeled builder runs read-only and self-BLOCKED #5123 transcript.
  • No dangling/duplicated test functions: grep confirms builder_plus_read_only_authority_fails_closed (renamed from builder_or_worker_plus_read_only_authority_fails_closed) and the new read_only_worker_is_an_ordinary_general_child each appear exactly once, and the previously-added roster_role_plus_read_only_authority_still_spawns / declared_read_only_write_roles_derive_without_mutating_shell tests (from the earlier Agent spawn surface has too many knobs — labeled builder runs read-only and self-BLOCKED #5123 narrowing) are untouched and still correct under this further-narrowed check.
  • read_only_worker_is_an_ordinary_general_child directly pins the fixed case: type: "worker" + write_authority: "read_only" now parses successfully with agent_type == FleetRole::Worker.
  • Changelog (CHANGELOG.md + crates/tui/CHANGELOG.md, kept in sync) and scripts/source-structure-budget.json's ceiling (668400→668406, +6 net lines) were updated consistently with the code change.

This is a correctness fix, not a loosening of the guard in any unsafe direction — type=builder/implementer + read_only still fails closed exactly as before; only the previously over-broad worker case is corrected. No provider/route-safety or security concerns.

Blocking issue: still fixed, untouched by this commit

crates/config/src/provider.rs, lib.rs, secrets, paths, mcp, execpolicy, and lane all show zero diff since the last review (confirmed empty). The Anthropic-dialect alias-collapse fix (4a5fc257) still stands as verified in every review since 2026-08-04T04:44 UTC.

Previously-flagged non-blocking items and nits

Unchanged and not touched by this commit — resume-history dedup (crates/core/src/lib.rs), the spillover-footer substring check (crates/tui/src/client/chat.rs), the provider! macro reuse nit (crates/config/src/provider.rs:1247), and the sanitize_line over-redaction finding in crates/lane/src/control.rs (flagged 2026-08-05T06:05 UTC) all still stand as originally written.

Net assessment

This synchronize is a single, well-diagnosed, well-tested correctness fix that completes the #5123 fail-closed narrowing (three iterations total: role vs. type, then this worker-vs-builder split). No new findings. The train's overall state is unchanged since the last content review: the originally-blocking provider/wire issue remains fixed with solid regression coverage.

cargo build/cargo test/clippy remain non-runnable in this sandbox (command execution requires interactive approval not available here) — all findings are from direct code reading and test-diff verification.
· branch

`Test (windows-latest)` failed `codewhale-telemetry` `golden_payload_v1`
with a diff that was nothing but \r\n vs \n. The golden is pulled in with
include_str!() and compared byte for byte against
serde_json::to_string_pretty output, which always emits LF, so the
`* text=auto` default corrupted it on a Windows checkout.

This file already documents that hazard for the prompt assets; the newer
include_str!() inputs (telemetry golden, bundled catalogs and skills under
crates/*/assets, the locale packs, the workflow scripts) had never been
added. The others survived only because they are parsed or
substring-matched rather than compared exactly.

`git add --renormalize .` reports no content change: every one of these is
already stored with LF, so this only affects what Windows checks out.
@claude

claude Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Hmbown's task in 1m 14s —— View job


Reviewing PR #5135 (v0.9.4 release train) — re-review after synchronize

Todo list

  • Identify what changed since the last review (08:03 UTC, tip 2dede1391)
  • Re-verify the previously-fixed provider-alias/wire regression still holds
  • Review the new commit for correctness/safety issues
  • Post final review summary

What changed since the last review

One new commit landed on top of 2dede1391:

  • 870106e00 "fix(ci): keep the include_str! assets on LF so Windows matches" — adds five new .gitattributes patterns (crates/telemetry/tests/golden/*.json, crates/*/assets/**/*.json, crates/*/assets/**/*.md, crates/*/locales/*.json, workflows/*.js) forcing text eol=lf, on top of the existing prompt-asset/*.rs rules. Fixes a real Test (windows-latest) failure: codewhale-telemetry's golden_payload_v1 test does a byte-for-byte comparison of include_str!("tests/golden/v1.json") against serde_json::to_string_pretty output (which always emits LF), so the repo-wide * text=auto default was checking that file out as CRLF on Windows and corrupting the comparison.

Verified directly

  • crates/telemetry/tests/golden/*.json is the line that actually fixes the reported failure — confirmed the golden file is compared with a plain assert_eq!/similar exact-string check against LF-only serde_json output (per the commit message, and consistent with crates/telemetry/src/tests.rs added earlier in this train).
  • The broader crates/*/assets/**/*.json, crates/*/assets/**/*.md, crates/*/locales/*.json patterns correctly cover every asset actually pulled in via include_str! under those directories (config/assets/models_dev.bundled.json, tui/assets/model_catalog.bundled.json, tui/assets/user-theme.schema.json, tui/assets/skills-catalog-matrix.json, every tui/assets/skills/*/SKILL.md, tui/locales/*.json) — verified by grepping all include_str! call sites in the tree.
  • git add --renormalize . reporting no content change (per the commit message) is consistent with these files already being stored as LF in the repo — this is a checkout-behavior fix only, not a content change.
  • Not fully covered by the new patterns: crates/tui/src/runtime_api/web.rs's include_str! of runtime_web/{index.html,styles.css,app.mjs} and runtime_api.rs's runtime_mobile.html live outside assets/ and aren't matched by any new rule. This is harmless for the commit's actual goal — nothing byte-compares those files in tests, they're served as web/mobile UI content where CRLF has no functional effect — so not a defect, just noting the pattern set isn't fully exhaustive of every include_str! input in the tree.

Blocking issue: still fixed, untouched by this commit

crates/config/src/provider.rs, lib.rs, secrets, paths, mcp, execpolicy, and lane all show zero diff since the last review (confirmed empty). The Anthropic-dialect alias-collapse fix (4a5fc257) still stands as verified in every review since 2026-08-04T04:44 UTC.

Previously-flagged non-blocking items and nits

Unchanged and not touched by this commit — resume-history dedup (crates/core/src/lib.rs), the spillover-footer substring check (crates/tui/src/client/chat.rs), the provider! macro reuse nit (crates/config/src/provider.rs:1247), and the sanitize_line over-redaction finding in crates/lane/src/control.rs (flagged 2026-08-05T06:05 UTC) all still stand as originally written.

Net assessment

This synchronize is a single, well-diagnosed, correctly-scoped Windows CI fix (.gitattributes config only, no source code touched) — no new findings rise to blocking. The train's substantive state is unchanged since the last content review: the originally-blocking provider/wire issue remains fixed with solid regression coverage.

cargo build/cargo test/clippy remain non-runnable in this sandbox (command execution requires interactive approval not available here) — all findings are from direct code/diff reading and grep-based cross-referencing of include_str! call sites against the new .gitattributes patterns.
· branch

Records the three cross-platform regressions blocking the release, the
credit-gate fix Hunter approved, and the verified state of everything
already landed, so the next agent does not re-derive it.

Also names the two traps that hid the regressions: a piped cargo test
reports grep's exit code rather than the suite's, and the 2026-08-04
release evidence called the macOS paste failure 'pre-existing on base'
when the base was the train rather than main.
@claude

claude Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Hmbown's task in 27s —— View job


Claude finished @Hmbown's task in 1m —— View job


Reviewing PR #5135 (v0.9.4 release train) — re-review after synchronize

Todo list

  • Identify what changed since the last review (08:20 UTC, tip 870106e00)
  • Re-verify the previously-fixed provider-alias/wire regression still holds
  • Review the new commit for issues
  • Post final review summary

What changed since the last review

One new commit landed on top of 870106e00:

  • cf00a2155 "docs(ops): handoff to finish and ship v0.9.4" — adds docs/ops/HANDOFF_2026-08-05_v094-ship-it.md (265 lines). Pure documentation, zero source files touched (git diff 870106e00..HEAD --stat shows only this one new file).

Blocking issue: still fixed, untouched by this commit

crates/config/src/provider.rs, lib.rs, secrets, paths, mcp, execpolicy, and lane all show zero diff since the last review (confirmed empty). The Anthropic-dialect alias-collapse fix (4a5fc257) still stands as verified in every review since 2026-08-04T04:44 UTC.

Previously-flagged non-blocking items and nits

Unchanged and not touched by this commit — resume-history dedup (crates/core/src/lib.rs), the spillover-footer substring check (crates/tui/src/client/chat.rs), the provider! macro reuse nit (crates/config/src/provider.rs:1247), and the sanitize_line over-redaction finding in crates/lane/src/control.rs (flagged 2026-08-05T06:05 UTC) all still stand as originally written.

Net assessment

This synchronize is a single docs/ops handoff-note commit — no code changed, no new findings. The train's substantive state is unchanged since the last content review: the originally-blocking provider/wire issue remains fixed with solid regression coverage.

cargo build/cargo test/clippy remain non-runnable in this sandbox (command execution requires interactive approval not available here) — this finding is from direct diff reading.
· branch

Hmbown added 6 commits August 5, 2026 06:30
ff97641 moved composer wrapping (wrap_text) from hard margin breaks to
word-boundary breaks, but cursor_row_col kept recomputing the cursor row
with the old margin model, so the two desynchronised: a long paste landed
one row short of its marker (paste_matrix_lands_in_the_composer_without_
autosubmitting, timed out at 6s) and the caret drifted behind fast typing.

Derive the cursor (row, col) from the SAME wrapped lines the renderer draws
so a desync is impossible by construction; the end-of-full-line wrap
convention falls out of wrap_text's trailing empty line.

Bisected to ff97641 (parent bb0be84 good). Verified: paste test ok in
3.50s, plus cursor_basic_ascii / at_wrap_boundary / with_cjk /
cjk_at_wrap_boundary / and_wrap_consistency / composer_wrap_boundary.
The monolithic async start_turn holds two full Config clones plus several
records, the Op::SendMessage, and the inlined ensure_engine_loaded
sub-future (which builds a full EngineConfig), all across ~8 sequential
.awaits. On Windows the runtime thread stack is ~1 MiB (vs ~8 MiB on
Linux/macOS), so this frame overflowed it: start_turn_accepts_dynamic_
tools_and_environment_id aborted the whole test binary (0xc00000fd
STATUS_STACK_OVERFLOW), masking other Windows failures.

Box::pin(async move { ... }).await heap-allocates the whole state machine;
behavior- and Send-preserving, protects every caller. Verified no macOS
regression (start_turn test passes).
safety_summary deliberately reports "<policy> requested, not enforced" on
platforms with no OS sandbox (Windows), the honest behavior from the
2026-08-04 audit. The test asserted the enforced string, which only appears
when a sandbox backend is present, so it failed on Windows. Branch the
assertions on app.sandbox_backend.is_none() (the same signal safety_summary
uses) so the test holds on both enforced and unenforced platforms.
The 2026-08-04 evidence called the paste failure "pre-existing on base",
but that base was the v0.9.4 train (the rebuild candidate sat on top of
it), NOT main. The paste failure passes on main and is a regression against
the last released line. Root-caused 2026-08-05 by git bisect to ff97641
and fixed. Name which base; do not let "pre-existing" stand without it.
When `[providers.xai]` selects OAuth and points `oauth_credential_generation`
at a Codewhale-owned credential file that no longer exists, `credentials_valid`
returns false and cannot fall through (a configured generation is
authoritative). So `needs_api_key` stayed true and the generic provider picker
reopened on EVERY launch — the dogfood bricked state (#5032).

Detect that specific corrupted state, best-effort clear the stale pointer from
the persisted config, and surface a truthful xAI-specific re-auth message. The
repair never blocks or aborts launch; after it the state is the normal
"needs auth", not a bricked loop. Leaves `auth_mode = "oauth"` intact.

- `owned_generation_is_dangling(config)`: detector for the missing-file state.
- `clear_dangling_xai_oauth_generation(path)`: best-effort persisted repair.
- init.rs: detect + repair in the launch path, set an xAI status message,
  keep the in-memory route consistent with the repaired file.
- regression test mirroring `activation_recovers_from_a_dangling_generation_pointer`.

Verified: cargo check -p codewhale-tui; cargo test xai_oauth::tests (38 passed).
Re-ships the Fleet/operate constitution; existing users (whose setup_state
records 0.8.67) re-acknowledge it once on next launch. Point the prompts
regression test at the const so it tracks the version instead of hardcoding it.
@Hmbown
Hmbown force-pushed the agent/v094-release-train-20260802 branch from cf00a21 to e44c60d Compare August 5, 2026 13:39
@claude

claude Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Hmbown's task in 1m 19s —— View job


Reviewing PR #5135 (v0.9.4 release train) — re-review after synchronize

Todo list

  • Identify exact diff since last review (3c5987e6c/cf00a2155e44c60d90)
  • Review d2fb791bb composer cursor/word-wrap fix
  • Review 78afd8d3d Box::pin start_turn Windows stack-overflow fix
  • Review af874d776 platform-aware /status test
  • Review 6d3b60816 xAI OAuth pointer fix
  • Re-verify the previously-fixed provider-alias/wire regression still holds
  • Post final review summary

What changed since the last review

Six commits landed on top of cf00a2155 (matched by message to local 3c5987e6c after the earlier history rewrite), 8 files, +259/-50:

  • d2fb791bb "unify composer cursor with word-boundary wrapping" — a real desync fix. ff97641b7 moved wrap_text to word-boundary breaks but left cursor_row_col computing rows with the old hard-margin model, so a long paste landed one row short of its marker and the caret drifted during fast typing. The fix derives cursor (row, col) from the same wrapped lines the renderer draws (cursor_row_col_in_lines), so a desync is impossible by construction. Verified the new helper directly: it walks lines_with_indices to find the containing line by starting char index, then computes col via str::width() on a byte-boundary-safe slice (char_indices().nth(offset), falling back to line.len() past the end) — no panic risk on multibyte/CJK input, and the empty-lines case correctly returns (0, 0). Well-diagnosed via bisect, with existing cursor/paste tests cited as passing.
  • 78afd8d3d "Box::pin start_turn state machine (Windows stack overflow)" — wraps the monolithic start_turn async body in Box::pin(async move {...}).await, heap-allocating the state machine instead of holding it on the ~1 MiB Windows runtime-thread stack (vs ~8 MiB on Linux/macOS), which was overflowing (STATUS_STACK_OVERFLOW) and aborting the whole test binary, masking other Windows failures. Purely a memory-layout change (Box::pin around the existing body, same return value) — behavior- and Send-preserving, no logic touched.
  • af874d776 "make /status safety-policy test platform-aware (Windows)" — the test now branches assertions on app.sandbox_backend.is_none(), the same signal safety_summary itself uses to decide between the enforced string and the honest "requested, not enforced" string on sandbox-less platforms (Windows). Correctly mirrors the production code's own branching signal rather than hardcoding a platform check.
  • 6d3b60816 "repair the v0.9.4: xAI device login cannot activate the persisted provider configuration #5032 dangling OAuth generation pointer on launch" — fixes a real bricked-launch loop: when [providers.xai] selects OAuth and oauth_credential_generation points at a Codewhale-owned credential file that's gone, credentials_valid returns false with no fallback (a configured generation is authoritative), so the provider picker reopened on every launch. owned_generation_is_dangling narrowly detects this exact state (OAuth selected + generation configured + file missing — explicitly not the normal "no generation configured" or "malformed pointer" states, which are different code paths), and clear_dangling_xai_oauth_generation best-effort-clears the stale pointer from the persisted config, non-fatally logging on error rather than blocking launch. auth_mode = "oauth" is deliberately preserved so the user isn't silently switched to a different auth mode. Has a direct regression test covering detection, specificity (unconfigured ≠ dangling), and repair-preserves-auth-mode.
  • 7fbb5b620 "correct release-evidence paste base (train, not main)" — docs-only correction: the paste failure documented in docs/ops/RELEASE_EVIDENCE_2026-08-04.md was called "pre-existing on base" without naming that "base" was the unreleased train, not main — it's actually a regression against the last released line (root-caused to ff97641b7, fixed by d2fb791bb above). No code.
  • e44c60d90 "bump constitution checkpoint to 0.9.4" — re-ships the Fleet/operate constitution so existing users re-acknowledge it once; also points the prompts regression test at the CONSTITUTION_CHECKPOINT_VERSION const instead of a hardcoded "0.8.67" string, so the test can't silently drift from the real value again.

No correctness, provider/route-safety, or security issues found in this batch — all six are small, well-diagnosed, and either directly tested or mechanically verified against their stated cause.

Blocking issue: still fixed, untouched by this batch

crates/config/src/provider.rs, crates/config/src/lib.rs, crates/secrets, crates/paths, crates/mcp, crates/execpolicy, and crates/lane all show zero diff since the last review (confirmed empty). The Anthropic-dialect alias-collapse fix (4a5fc257) still stands as verified in every review since 2026-08-04T04:44 UTC.

Previously-flagged non-blocking items and nits

Unchanged and not touched by this batch — resume-history dedup (crates/core/src/lib.rs), the spillover-footer substring check (crates/tui/src/client/chat.rs), the provider! macro reuse nit (crates/config/src/provider.rs:1247), and the sanitize_line over-redaction finding in crates/lane/src/control.rs (flagged 2026-08-05T06:05 UTC) all still stand as originally written.

Net assessment

This synchronize is a tight, well-tested batch fixing a real Windows-only regression pair (composer cursor desync + stack overflow), an xAI OAuth-recovery bug (#5032), a platform-aware test correction, and honest doc/version bookkeeping. No new findings. The train's substantive state is unchanged since the last content review: the originally-blocking provider/wire issue remains fixed with solid regression coverage.

cargo build/cargo test/clippy remain non-runnable in this sandbox (command execution requires interactive approval not available here) — all findings are from direct code reading and diff verification.
· branch

…fixes

The v0.9.4 must-fix items (composer cursor/wrap unification, Windows
start_turn Box::pin, the #5032 dangling xAI OAuth repair + its test, the
platform-aware /status safety-policy test, the 0.9.4 checkpoint bump) grew
owned Rust by +201 lines (668406 -> 668607), tripping the one-way aggregate
ratchet. No new 1000-line modules; growth is in existing files. Re-baseline
the ceiling and record why; pay the aggregate back down in v0.9.5.
@claude

claude Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Claude finished @Hmbown's task in 35s —— View job


Reviewing PR #5135 (v0.9.4 release train) — re-review after synchronize

Todo list

  • Identify what changed since the last review (13:39 UTC, tip e44c60d90)
  • Re-verify the previously-fixed provider-alias/wire regression still holds
  • Review the new commit for issues
  • Post final review summary

What changed since the last review

One new commit landed on top of e44c60d90:

  • 56ceb09c6 "chore(gates): re-baseline source-structure aggregate for v0.9.4 ship fixes" — bumps scripts/source-structure-budget.json's max_total_owned_rust_lines 668406→668607 (+201) to account for the five ship-fix commits reviewed in the prior pass (composer cursor/wrap unification, the Windows start_turn Box::pin fix, the v0.9.4: xAI device login cannot activate the persisted provider configuration #5032 dangling xAI OAuth repair + its test, the platform-aware /status test, and the 0.9.4 constitution checkpoint bump — d2fb791bb, 78afd8d3d, af874d776, 6d3b60816, e44c60d90, all already reviewed at 13:39 UTC with no issues found). Single-file diff, pure numeric ceiling adjustment with an explanatory _todo note — no code, logic, or test changes (git show 56ceb09c6 --stat confirms only scripts/source-structure-budget.json touched).

Blocking issue: still fixed, untouched by this commit

crates/config/src/provider.rs, crates/config/src/lib.rs, crates/secrets, crates/paths, crates/mcp, crates/execpolicy, and crates/lane all show zero diff since the last review. The Anthropic-dialect alias-collapse fix (4a5fc257) still stands as verified in every review since 2026-08-04T04:44 UTC.

Previously-flagged non-blocking items and nits

Unchanged and not touched by this commit — resume-history dedup (crates/core/src/lib.rs), the spillover-footer substring check (crates/tui/src/client/chat.rs), the provider! macro reuse nit (crates/config/src/provider.rs:1247), and the sanitize_line over-redaction finding in crates/lane/src/control.rs (flagged 2026-08-05T06:05 UTC) all still stand as originally written.

Net assessment

This synchronize is a single-commit budget-ceiling correction with no code, logic, or test changes — no new findings. The train's substantive state is unchanged since the last content review: the originally-blocking provider/wire issue remains fixed with solid regression coverage.

cargo build/cargo test/clippy remain non-runnable in this sandbox (command execution requires interactive approval not available here) — this finding is from direct code/diff reading.
· branch

@Hmbown
Hmbown merged commit 56ceb09 into main Aug 6, 2026
27 of 29 checks passed
@Hmbown
Hmbown deleted the agent/v094-release-train-20260802 branch August 6, 2026 07:45
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.

2 participants