Skip to content

fix(ios): replace failed agent turns on daemon retry - #27314

Merged
teamleaderleo merged 7 commits into
feat-cmux-nextfrom
lane/ios-failed-turn-retry
Oct 29, 2026
Merged

teamleaderleo merged 7 commits into
feat-cmux-nextfrom
lane/ios-failed-turn-retry

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Oct 29, 2026 •

Copy link
Copy Markdown
Collaborator

Failed agent Retry previously sent a fresh user prompt, duplicating the failed turn. It now calls the daemon's retry operation. The daemon records the previous turn id on each retry attempt; the phone folds that attempt into the failed turn on live delivery and history replay, retaining the original prompt, attachments and partial work. A refused request leaves the failed turn intact, and repeated taps are suppressed while the request is pending.

Leo's request:

well i just think we can improve it a f***ton more than it is now... it's slow it's bad it belabours the point in too many tooltips it's ugly in places it doesn't even work in places there's lots of functionality that needs to get in

Scope: ordinary Retry on a failed latest agent turn. Chief attachments are covered separately by #27286. Edit and Resend still needs the historical-fork path and navigation support. #27205's menu additions are preserved; its failed-turn model retry should use the daemon retry operation after the model change.

Validation:

  • Tests cover one turn after retry, kept attachment bytes, no optimistic deletion, refusal followed by retry, repeated taps, and recorded-history replay.
  • Static prepush checks passed at 51c54b8. No local Swift compiler or executed Swift tests.
  • Fleet request: compile the iOS package and run FailedTurnRetryTests|RetriedTurnLiveTests. Regression commit cb7f956 should fail the replay test.
  • Before/after light/dark capture scene: agent=-cmux-mobile-demo kbd-chat; base ba6bbf4, head 51c54b8. Captures pending.
  • Independent review pending coordinator dispatch; the local review box cannot isolate its process scope.

Risk: an older daemon without the retry operation refuses the request and keeps the failed turn. No fallback sends a duplicate prompt. No timers or observers are added. Shared CmuxConversation and CmuxTabsKit are unchanged.

Agent session: agent session
🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Fixes iOS retry of a failed agent turn so it uses the daemon's retry operation instead of sending a duplicate prompt, preserving the original prompt, attachments, and partial work.

  • The daemon records the previous turn id; on retry, the phone folds the new attempt into the existing failed turn for both live delivery and history replay.
  • Retry obeys the same phone-mode and Full-access checks, and the Retry button is disabled once the turn is no longer the latest failed turn.
  • The retried attempt's agent output starts a new message or thought item instead of appending to the failed attempt's partial output.
  • Repeated retry taps are suppressed while the request is pending; a refused request leaves the failed turn intact.
  • A dropped link clears the pending retry state, so a retry lost before the daemon accepted it can be sent again after reconnect.
  • Older daemons without the retry operation refuse the request and keep the failed turn, with no fallback duplicate prompt.

Written for commit 09f4248. Summary will update on new commits.

View guided diff Turn on auto-fix

@blacksmith-sh

blacksmith-sh Bot commented Oct 29, 2026

Copy link
Copy Markdown

Found 1 test failure on Blacksmith runners:

Failure

Test View Logs
test_the_repository_highlights_validate (main.DigestTests.test_the_repository_highl
ights_validate)/
test_the_repository_highlights_validate (main.DigestTests.test_the_repository_highl
ights_validate)
View Logs

Fix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Bead cx-3w3d2x created for this PR by the cmux coordinator.

@teamleaderleo

teamleaderleo commented Oct 29, 2026 •

Copy link
Copy Markdown
Collaborator Author

Fleet receipts for #27314

Current head 2336210

Fleet iOS compile + full CmuxMobileKit simulator tests at 2336210 (scripts/ci/cmux-next-ios-sim.sh test, step 1236ed65, Xcode 26.6, iPhone 17 Pro on iOS 26.5): pass. The Demo app build (FFI + xcodegen + simulator app) succeeded and launched; CmuxMobileKit tests exited 0 with 1406 passed, 0 failed and 1 skipped. FailedTurnRetryTests and RetriedTurnLiveTests are in that suite, including 3d261aa's new tests at this head.

Red/green: not rerun at this head. The latest red/green is at 391bc06 (below): red d4288e3 fails aLostRetryCanBeSentAgainAfterReconnect and the head passes 4 tests in 2 suites. This head adds 3d261aa (tests for retry access checks and attempt-output boundaries) and its fix; those tests have not been run red/green here.

Screenshots (Demo build, CMUX_MOBILE_MOCK_AUTH=1, Xcode 26.6, iPhone 17 Pro on iOS 26.5; base ba6bbf4 and head 2336210 built on the same host, both exit 0). Names are mock fixture data only.

Agent chat, -cmux-mobile-demo kbd-chat:

before (base ba6bbf4) after (head 2336210)
light
dark

Failed turn with Retry, -cmux-mobile-demo kbd-chat -cmux-mobile-failed-turn-demo YES:

before (base ba6bbf4) after (head 2336210)
light
dark

No demo scene shows a failed turn, and the mock session answers retryFailedTurn with "This demo has no failed turn to retry." So these shots use a capture-only DEBUG fixture, applied identically to base and head (product code untouched): it ends the mock chat's history with one failed turn and skips the mock's waiting turn, so the failed turn is the last turn on screen.

The two look the same. Base already showed a Retry button on a failed turn (it re-sent the prompt). The head shows the same card, but its Retry now goes through store.retryFailedTurn, which sends the daemon's retryFailedTurn and is gated by canRetryFailedTurn (last turn, failed, no retry pending). That behaviour change is invisible in a still and is covered by the red/green tests.

Capture fixture (MockAgentSession.swift, same hunks at base and head)
--- a/cmux-next-ios/CmuxMobileKit/Sources/CmuxMobileAgentChat/MockAgentSession.swift	2026-10-29 02:52:38
+++ b/cmux-next-ios/CmuxMobileKit/Sources/CmuxMobileAgentChat/MockAgentSession.swift	2026-10-29 02:52:38
@@ -52,7 +52,7 @@
         // History (and a waiting turn's opening) arrive as one change.
         batch = []
         replayHistory()
-        if summary.status == .waiting { startWaitingTurn() }
+        if summary.status == .waiting, !Self.failedTurnDemo { startWaitingTurn() }
         let replayed = batch ?? []
         batch = nil
         continuation.yield(.batch(replayed))
@@ -164,6 +164,13 @@
             .init(value: "high", name: "High"), .init(value: "max", name: "Max"),
         ])]
     }
+
+    #if DEBUG
+    /// Capture fixture only: `-cmux-mobile-failed-turn-demo YES` ends the history with one failed turn.
+    static var failedTurnDemo: Bool { UserDefaults.standard.bool(forKey: "cmux-mobile-failed-turn-demo") }
+    #else
+    static let failedTurnDemo = false
+    #endif
 
     private func replayHistory() {
         emit(.modelsAvailable(Self.models, current: currentModel))
@@ -193,6 +200,13 @@
         emit(.turnResult(turnId: turnID, status: .completed, errorText: nil, at: start.addingTimeInterval(41)))
         let second = Date().addingTimeInterval(-1800)
         replaySubagentTurn(at: second)
+        if Self.failedTurnDemo {
+            let failedAt = Date().addingTimeInterval(-600)
+            emit(.userMessage(text: "Run the link tests again", promptId: "p-h3", turnId: "history-3", attachments: [], at: failedAt))
+            emit(.turnStarted(turnId: "history-3", at: failedAt))
+            emit(.turnResult(turnId: "history-3", status: .failed, errorText: "The agent process exited unexpectedly.",
+                             at: failedAt.addingTimeInterval(12)))
+        }
         checkpointLog = [
             AgentCheckpoint(id: "cp-h1", title: "What does the link framing look like today?", turnID: turnID, createdAt: start,
                             branch: summary.branch, head: "4d81e0c", files: 0),

Earlier head 391bc06 (superseded)

Red/green (macOS swift test --package-path cmux-next-ios/CmuxMobileKit --filter 'FailedTurnRetryTests|RetriedTurnLiveTests', FFI xcframework built first at each checkout, Xcode 26.6, AWS M4 Pro simulator Mac at load ~25):

commit exit result
red d4288e3 (test only: retry lost on reconnect) 1 FailedTurnRetryTests/aLostRetryCanBeSentAgainAfterReconnect fails at FailedTurnRetryTests.swift:52: source.sent is [.retryFailedTurn], expected [.retryFailedTurn, .retryFailedTurn] (the second retry after reconnect is never sent). The other 3 tests pass. A real assertion failure: no crash, no setup error.
head 391bc06 0 4 tests in 2 suites passed

Fleet iOS compile + full CmuxMobileKit simulator tests at 391bc06 (step 5d4404db): cancelled as superseded, never ran.

Screenshots, agent chat (-cmux-mobile-demo kbd-chat, CMUX_MOBILE_MOCK_AUTH=1, Demo build, Xcode 26.6, iPhone 17 Pro on iOS 26.5; both builds on the same host, exit 0), base ba6bbf4 vs head 391bc06:

before (base ba6bbf4) after (head 391bc06)
light
dark

The kbd-chat fixture has no failed turn, so it shows no Retry button. Before and after match, which shows the chat is unchanged; the Retry behaviour itself is proven by the red/green above.

Earlier head 51c54b8 (superseded; kept for the record)

Red/green (macOS swift test --package-path cmux-next-ios/CmuxMobileKit --filter 'FailedTurnRetryTests|RetriedTurnLiveTests', after build-xcframework.sh at each checkout, on an AWS M4 Pro simulator Mac, Xcode 26.6):

commit exit result
red cb7f956 (test only) 1 RetriedTurnLiveTests/aRetryReplacesTheFailedTurnDuringHistoryReplay fails 3 expectations: store.turns.count 2 == 1 (line 36), turn.prompt "Continue" == "Read this" (line 39), turn.attachments [] == ["image"] (line 40). A real assertion failure: no crash, no setup error.
head 51c54b8 0 3 tests in 2 suites passed (FailedTurnRetryTests, RetriedTurnLiveTests)

Fleet iOS compile + full CmuxMobileKit simulator tests at 51c54b8 (step e5f412fd): cancelled as superseded, never ran.

Screenshots, agent chat (-cmux-mobile-demo kbd-chat, CMUX_MOBILE_MOCK_AUTH=1, Demo build), base ba6bbf4 vs head:

before (base) after (head)
light
dark

Both Demo builds succeeded (exit 0). The kbd-chat fixture has no failed turn, so it shows no Retry: before and after match, which only shows that the chat is unchanged. No demo scene puts a failed turn on screen; the Retry behaviour is covered by the red/green tests above. The host was heavily loaded during these runs: 1-minute load about 36 when the tests started, then 684–849 from 08:39Z. The test results are unaffected (each finished in under 0.06 s). If the head capture fails in a way that looks like a timeout, it will be marked as needing a rerun on a quiet host, not as a PR result.

Agent session: agent session

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex-review-box/gpt-6-astra high, lane-place): BLOCK at 51c54b8

BLOCK

  1. cmux-next-ios/CmuxMobileKit/Sources/CmuxMobileAgentChat/AgentChatStore.swift:313 — retryRequested is never cleared when the live link drops. AcpmuxSessionSource.linkDown() only emits .status(.disconnected), and reconnecting replays the failed turn without a .clientError or .turnRetried if the retry request was lost before acceptance. The failed turn’s Retry action then remains permanently disabled for that chat. Clear the in-flight flag on disconnect/reattach (or emit a retry failure).

@teamleaderleo teamleaderleo removed the risk:normal Risk tier (docs/ci/risk-tiers.md) label Oct 29, 2026
teamleaderleo and others added 2 commits October 29, 2026 09:04
@github-actions github-actions Bot added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Oct 29, 2026
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Review (codex-review-box/gpt-6-astra high, lane-place): BLOCK at 391bc06

BLOCK

  1. [P1] Retry bypasses the phone’s mode restriction — AcpmuxHub.swift:421. _acpmux/retry is absent from the mobile host’s SESSION_CONTROL_METHODS, so it skips the current-mode check and requireFullAccess marking. A Mac-started Codex bridge session in agent-full-access can consequently be retried from a phone with Full access disabled; the daemon permits authenticated bridge control, and full_access_hold excludes locally started sessions. Add retry to the host’s session-control checks.

  2. [P2] Retry output joins the failed attempt’s last message — AgentChatStore.swift:411. The reducer preserves the old items while resetting currentMessageID to nil. applyUpdate treats that value as permission to concatenate the next message chunk onto the last message—even when its message ID differs. A failed partial answer followed by a retry therefore produces one malformed answer. Preserve an explicit attempt boundary so the retry starts a new message.

  3. [P2] Older failed turns retain a nonfunctional Retry button — AgentChatStore.swift:313. TurnView still displays Retry for every failed turn, but this guard silently rejects any turn except the latest. After sending another prompt, tapping an earlier failure’s Retry now does nothing. Hide or disable the action for ineligible turns, or preserve an explicit resend action.

@teamleaderleo teamleaderleo removed the risk:normal Risk tier (docs/ci/risk-tiers.md) label Oct 29, 2026
@github-actions github-actions Bot added the risk:normal Risk tier (docs/ci/risk-tiers.md) label Oct 29, 2026
Apply existing phone mode and Full-access checks to retry. Keep retained attempt output separate and disable Retry when its turn is no longer eligible.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-2209fa76608f5c2d6757
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Rust red/green: acpmux_policy::tests: FAIL at regression-only 3d261aa9, PASS at head 23362100483f2fa1e5cae71ce2cb05c541ef2ead

Both commits ran the same command on Linux:

cargo test --manifest-path cmux-next-ios/rust/Cargo.toml -p cmux-mobile-host acpmux_policy::tests
Commit Run Result
regression-only 3d261aa977add5fd610ff64862af902b0cee3319 40852939159 FAIL, as expected: 6 passed, 1 failed
head 23362100 40852942072 PASS: 7 passed, 0 failed

Red failure:

thread 'acpmux_policy::tests::retry_checks_the_current_mode_and_requires_full_access_when_it_does_not_ask' panicked at crates/cmux-mobile-host/src/acpmux_policy.rs:249:56:
retry controls the existing session

Each snapshot has the same tree as the commit it tested: e49877d7 = 3d261aa9 (8cf51d35); 6f204997 = 23362100 (97a9d8fa).

Still running on the fleet: the iOS simulator compile plus the CmuxMobileKit tests, including FailedTurnRetryTests and RetriedTurnLiveTests. Head step ad17ff35036a21921422ee37, red step 2772fe752d9fede36907535f. I'll add their results here.

Agent session: agent session

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Fleet receipt: head 23362100483f2fa1e5cae71ce2cb05c541ef2ead, iOS simulator: PASS. The red commit 3d261aa977add5fd610ff64862af902b0cee3319 fails as expected.

Commands (fleet Macs, Xcode 26.6, iPhone 17 Pro on iOS 26.5):

cmux-ci run --class simulator --script scripts/ci/cmux-next-ios-sim.sh --ref 23362100483f2fa1e5cae71ce2cb05c541ef2ead --arg test --artifact artifacts/ios-sim/evidence.zip
cmux-ci run --class simulator --script scripts/ci/cmux-next-ios-sim.sh --ref 3d261aa977add5fd610ff64862af902b0cee3319 --arg test --artifact artifacts/ios-sim/evidence.zip

Head 23362100:

  • iOS compile: ** BUILD SUCCEEDED ** (FFI, xcodegen and the Demo simulator app). The app launches.
  • CmuxMobileKit tests: exit 0, 1406 passed, 0 failed, 1 skipped.
  • FailedTurnRetryTests and RetriedTurnLiveTests (CmuxMobileAgentChatTests) ran inside that kit run, and none of their tests failed.

Red 3d261aa9 ("test(ios): cover retry access checks and attempt output boundaries"; the fix 23362100 follows it): compile succeeded, and the kit ran 1403 passed, 2 failed, 1 skipped. Both failures are the new attempt-boundary tests:

  • ✘ FailedTurnRetryTests/retryThoughtsStartANewItem(): FailedTurnRetryTests.swift:96 thoughts was ["Old thoughtNew thought"], expected ["Old thought", "New thought"]
  • ✘ FailedTurnRetryTests/retryOutputStartsANewMessage(withMessageIDs:) (the step's failure summary lists the test but not its assertion line)

The Rust acpmux_policy::tests red/green receipt is in my earlier comment.

Fleet steps: head ad17ff35036a21921422ee37, red 2772fe752d9fede36907535f.

Agent session: agent session

@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Merged current base 719c550 into this branch. New head: 09f4248, confirmed on the remote. The two UI conflicts preserve the landed Retry with another model controls alongside the failed-turn Retry action and eligibility check. git diff --check passes.

The earlier SHIP and fleet receipt 1236ed65 cover 2336210. This new head needs a fresh independent review and fleet evidence:

  • Simulator app compile and full kit: scripts/ci/cmux-next-ios-sim.sh test.
  • Focused Swift regressions: swift test --package-path cmux-next-ios/CmuxMobileKit --filter 'FailedTurnRetryTests|RetriedTurnLiveTests'.
  • Host checks: cargo test --manifest-path cmux-next-ios/rust/Cargo.toml -p cmux-mobile-host acpmux_policy::tests.
  • Compare any failing test by name and error against base 719c550; crashes are not base-red excuses.
  • Dark/light before/after captures: agent=-cmux-mobile-demo kbd-chat.

Dispatch is currently blocked: the message relay cannot read the destination session list, so the direct fleet requests were not delivered. No new test or review success is claimed.

@teamleaderleo
teamleaderleo marked this pull request as ready for review October 29, 2026 21:09
@teamleaderleo
teamleaderleo merged commit 793a24b into feat-cmux-next Oct 29, 2026
129 of 134 checks passed
@teamleaderleo
teamleaderleo deleted the lane/ios-failed-turn-retry branch October 29, 2026 21:09
@teamleaderleo

Copy link
Copy Markdown
Collaborator Author

Landing receipt at 09f4248b (squash merged as 793a24b)

  • Fleet iOS kit (scripts/ci/cmux-next-ios-sim.sh test, step 45333a37f3befd0861b0bbec, exact head; the CI scripts match the feat-cmux-next tip blob): build ok, launch ok, CmuxMobileKit on the iOS simulator: passed 1516, failed 0, skipped 1. That includes the new FailedTurnRetryTests (6) and RetriedTurnLiveTests (1).
  • Review (codex gpt-6-astra high, fast): SHIP at 09f4248. Diff from merge base 719c550. It found no blocking correctness problems; it checked retry suppression, refusal, reconnect, retained attachments, output separation and history replay. Its caveat: replay uses synthetic records, so it did not check the daemon side end to end. The earlier BLOCK was at an older head (391bc06).
  • GitHub checks: rust workspaces / cmux-next-ios/rust was red on the first attempt. cmux-mobile-link-ffi --test browser, browser_video_rides_the_media_session_beside_wireguard, panicked at crates/cmux-mobile-link-ffi/tests/browser.rs:43 with "timed out waiting for video back in the tunnel". This PR does not touch that crate; its only Rust change is adding _acpmux/retry to cmux-mobile-host SESSION_CONTROL_METHODS, plus a test. A rerun of the failed jobs at the same head passed, so this was a timing flake and not a crash. Every other check was green at merge time.
  • No Chief paths changed. No HOLD line and no hold label.

Agent session: agent session

@github-actions

Copy link
Copy Markdown
Contributor

cmux status (one comment, edited in place; each line is updated by the job that owns it)

Review 09f4248: HQ feed card · gallery before/after runs · checks

App of this head (app.zip, kept 1 day)

gh workflow run reload-build.yml -R manaflow-ai/cmux --ref feat-cmux-next -f tag=pr27314 -f ref=lane/ios-failed-turn-retry -f platform=macos -f backend_mode=local

@github-actions

github-actions Bot commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

teamleaderleo pushed a commit that referenced this pull request Oct 30, 2026
Base's new iOS work carried onto #27087's lists:

- Failed turn retry through acpmux (#27314): the native transcript's failure
  row retries the turn with store.retryFailedTurn (disabled while a retry is in
  flight or once the turn is not the latest failed one), as base's TurnView did.
  The coalesced chunk path keeps base's startsNewAttempt, so a retried attempt's
  first chunk starts a new message or thought.
- Top chrome (#27300): the transcript's collection view takes the native soft
  top edge and the edge inset base gave its scroll view (edgeTopInset moves onto
  ChatTranscriptCollectionView), with the top shelf and the iOS 26 gradient
  fallback for workspace chats.
- Open on iPhone (16bfeba): a native browser tab's card shows its own page
  first, then the streamed one, then a local page only if one was shown.
- Base's SwiftUI TurnView stays removed (the native rows replace it).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Agent-Session: https://cmux.com/deeplink/agent?id=aid-7aa94f16aeac121e03bf
azooz2003-bit added a commit that referenced this pull request Oct 31, 2026
…lists

13 files conflicted under cmux-next-ios/CmuxMobileKit/Sources. Both sides' behavior is
kept. Where this branch replaced the code an upstream commit changed, the upstream
change is ported into the new code:

- Transcript (TurnView/AgentChatTurns -> ChatTranscriptList rows): Find in Chat (#27318)
  marks and scrolls to the row that draws the match (ChatRow.findKey, layout frames,
  the marked row drawn as SwiftUI); Edit and Resend opens in the composer with the
  "Replace your draft?" dialog on both row paths (#27620); "Showing messages since"
  for capped history in the SwiftUI and native session line (#27374); the failure
  card retries through retryFailedTurn with Retry off while it is in flight (#27314);
  Retry keeps attachments (#28489) and Retry with another model in the SwiftUI and
  native More menu (#27322); native copy haptic follows the haptics setting (#27348).
- AgentChatStore: the branch's copy-free chunk append keeps upstream's startsNewAttempt.
- ChatKeyboardDock: upstream's header bottom, native top edge, iOS 26 fade, stack
  ceiling and find scroll (#27300, #27767, #27318) on the branch's collection view; the
  edge-inset safe area override moves to ChatTranscriptCollectionView.
- DiffFileScreen (VStacks -> DiffCodeList): Search in File, Next / Previous Hunk, match
  paint, current-match bar and scroll to line (#27322), memoized per file and query.
- WorkspacesList (SwiftUI menus -> WorkspacesListMenus): group Options, Close All in
  Group, Close Other Groups, Remove All Workspace Colors, Move to Top/Bottom, Sort
  Workspaces, Close Other in Group / All (#27322).
- WorkspaceSync: upstream's delta queue (#27299) kept; the repeated-title fix moves to
  enqueue. TerminalFeed: upstream's delta early return before the batched reads.
- FeedStore: markAllRead next to batched markRead. ArtifactPreview: cancellation check
  and off-main decode. DevicesPage: link status in the subtitle with the per-pass row
  context. WorkspaceTabsSource: Open on iPhone cards and pages, group edits and
  screens on the stored-tab model; MarkdownView: block marks; DisclosureChoice: both.
- Tests: SlashSuggestionsReachTests uses the controller's store initializer;
  TranscriptRowPortTests covers the ported transcript and diff behavior.

Per-commit table: cmux-next-ios/verify/listperf/MERGE-<this sha>.md (next commit).

Tests: macOS swift test (aws-m4pro-5) 1500 tests, the same 4 PreviewInsufficientSpace
failures as untouched upstream on that host; iOS xcodebuild test (aws-m4pro-6, iOS 26.5)
all suites pass but BoundedWaitTests under load, which passes on re-run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch was previously deployed

1 inactive deployment
ci-routing-agent — 09f4248b Deployed Oct 29, 2026 by teamleaderleo via cmux-tui tree preflight #78118
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk:normal Risk tier (docs/ci/risk-tiers.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant