From 54edd6746a9e4f757e8f27c84b97b668c8b0672e Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 25 Sep 2026 19:12:22 +0000 Subject: [PATCH 1/3] fix: let CI run when DEPLOY_KEY is unset and match stable rustfmt webfactory/ssh-agent aborted Quality, Build & Test, and the Windows cross-check whenever the deploy key secret was empty, so those jobs never compiled. Git dependencies here are public HTTPS, so start the agent only when a key is configured. Reformat the six files current stable rustfmt rejects, and do not require a linked issue on Dependabot lockfile bumps. Co-authored-by: OnlineChef --- .github/workflows/ci.yml | 20 ++++++++++++++++ .github/workflows/release.yml | 3 +++ .github/workflows/require-issue.yml | 7 ++++++ .../src/agent/prompting_tests.rs | 7 ++---- crates/jcode-base/src/prompt.rs | 4 +++- crates/jcode-desktop2/src/scene.rs | 23 ++++--------------- crates/jcode-desktop2/src/tests/actions.rs | 5 +--- crates/jcode-tui/src/tui/app/turn_memory.rs | 3 ++- 8 files changed, 43 insertions(+), 29 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 016f1b22d6..aac24fdbe4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,12 @@ jobs: ssh-key: ${{ secrets.DEPLOY_KEY }} submodules: recursive + # Git dependencies in this repo are public HTTPS. The deploy key is only + # needed when a private git source is configured. webfactory/ssh-agent + # fails the job when ssh-private-key is empty, which skipped every build + # and test on a repo that has no DEPLOY_KEY secret. - name: Configure SSH for cargo git dependencies + if: ${{ secrets.DEPLOY_KEY != '' }} uses: webfactory/ssh-agent@dc588b651fe13675774614f8e6a936a468676387 with: ssh-private-key: ${{ secrets.DEPLOY_KEY }} @@ -175,7 +180,12 @@ jobs: ssh-key: ${{ secrets.DEPLOY_KEY }} submodules: recursive + # Git dependencies in this repo are public HTTPS. The deploy key is only + # needed when a private git source is configured. webfactory/ssh-agent + # fails the job when ssh-private-key is empty, which skipped every build + # and test on a repo that has no DEPLOY_KEY secret. - name: Configure SSH for cargo git dependencies + if: ${{ secrets.DEPLOY_KEY != '' }} uses: webfactory/ssh-agent@dc588b651fe13675774614f8e6a936a468676387 with: ssh-private-key: ${{ secrets.DEPLOY_KEY }} @@ -414,7 +424,12 @@ jobs: ssh-key: ${{ secrets.DEPLOY_KEY }} submodules: recursive + # Git dependencies in this repo are public HTTPS. The deploy key is only + # needed when a private git source is configured. webfactory/ssh-agent + # fails the job when ssh-private-key is empty, which skipped every build + # and test on a repo that has no DEPLOY_KEY secret. - name: Configure SSH for cargo git dependencies + if: ${{ secrets.DEPLOY_KEY != '' }} uses: webfactory/ssh-agent@dc588b651fe13675774614f8e6a936a468676387 with: ssh-private-key: ${{ secrets.DEPLOY_KEY }} @@ -669,7 +684,12 @@ jobs: ssh-key: ${{ secrets.DEPLOY_KEY }} submodules: recursive + # Git dependencies in this repo are public HTTPS. The deploy key is only + # needed when a private git source is configured. webfactory/ssh-agent + # fails the job when ssh-private-key is empty, which skipped every build + # and test on a repo that has no DEPLOY_KEY secret. - name: Configure SSH for cargo git dependencies + if: ${{ secrets.DEPLOY_KEY != '' }} uses: webfactory/ssh-agent@dc588b651fe13675774614f8e6a936a468676387 with: ssh-private-key: ${{ secrets.DEPLOY_KEY }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 18b0472c15..4ace56bc91 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -90,7 +90,10 @@ jobs: submodules: recursive fetch-depth: 0 + # Same as CI: skip the agent when DEPLOY_KEY is unset. An empty key + # makes webfactory/ssh-agent fail before the release build starts. - name: Configure SSH for cargo git dependencies + if: ${{ secrets.DEPLOY_KEY != '' }} uses: webfactory/ssh-agent@dc588b651fe13675774614f8e6a936a468676387 with: ssh-private-key: ${{ secrets.DEPLOY_KEY }} diff --git a/.github/workflows/require-issue.yml b/.github/workflows/require-issue.yml index e2a02fd0ce..e4b13cbf9c 100644 --- a/.github/workflows/require-issue.yml +++ b/.github/workflows/require-issue.yml @@ -39,6 +39,13 @@ jobs: const owner = context.repo.owner; const repo = context.repo.repo; + const login = pr.user?.login || ''; + // Dependabot cannot open a tracking issue for a lockfile bump. + // Human PRs still have to link a real issue in this repository. + if (login === 'dependabot[bot]') { + core.info('Dependabot pull request; linked issue is not required.'); + return; + } const text = `${pr.title || ''}\n\n${pr.body || ''}`; // Collect candidate issue numbers that belong to THIS repo. diff --git a/crates/jcode-app-core/src/agent/prompting_tests.rs b/crates/jcode-app-core/src/agent/prompting_tests.rs index 538607bd20..a0099b707c 100644 --- a/crates/jcode-app-core/src/agent/prompting_tests.rs +++ b/crates/jcode-app-core/src/agent/prompting_tests.rs @@ -39,11 +39,8 @@ impl Provider for PromptLockProvider { } fn unique_temp_dir(tag: &str) -> std::path::PathBuf { - let dir = std::env::temp_dir().join(format!( - "jcode-prompt-lock-{}-{}", - std::process::id(), - tag - )); + let dir = + std::env::temp_dir().join(format!("jcode-prompt-lock-{}-{}", std::process::id(), tag)); let _ = std::fs::remove_dir_all(&dir); std::fs::create_dir_all(&dir).expect("create temp prompt dir"); dir diff --git a/crates/jcode-base/src/prompt.rs b/crates/jcode-base/src/prompt.rs index 48b47a1b87..88d2aec131 100644 --- a/crates/jcode-base/src/prompt.rs +++ b/crates/jcode-base/src/prompt.rs @@ -225,7 +225,9 @@ pub fn append_dynamic_prompt_parts( if !split.dynamic_part.is_empty() { split.dynamic_part.push_str("\n\n"); } - split.dynamic_part.push_str(&format!("# Active Skill\n\n{}", skill)); + split + .dynamic_part + .push_str(&format!("# Active Skill\n\n{}", skill)); } } const SELFDEV_MODE_PROMPT: &str = include_str!("prompt/selfdev_mode.txt"); diff --git a/crates/jcode-desktop2/src/scene.rs b/crates/jcode-desktop2/src/scene.rs index e3e89ef302..0461a4bf7d 100644 --- a/crates/jcode-desktop2/src/scene.rs +++ b/crates/jcode-desktop2/src/scene.rs @@ -1311,12 +1311,7 @@ fn draw_transcript( Affine::scale(scale), color, None, - &Rect::new( - x0, - y0, - x1, - y1, - ), + &Rect::new(x0, y0, x1, y1), ); } if let Some(selection) = model.selection.as_ref() @@ -1941,18 +1936,10 @@ mod tests { for scale in [1.0, 1.25, 1.5, 1.75, 2.0, 2.5] { let hairline = 1.0 / scale; let origin = 13.37; - let (_, first_bottom) = diff_band_y( - Rect::new(0.0, 0.0, 100.0, 19.2), - origin, - hairline, - false, - ); - let (second_top, _) = diff_band_y( - Rect::new(0.0, 19.2, 100.0, 38.4), - origin, - hairline, - false, - ); + let (_, first_bottom) = + diff_band_y(Rect::new(0.0, 0.0, 100.0, 19.2), origin, hairline, false); + let (second_top, _) = + diff_band_y(Rect::new(0.0, 19.2, 100.0, 38.4), origin, hairline, false); let overlap_px = (first_bottom - second_top) * scale; assert!( (overlap_px - 1.0).abs() < 1e-9, diff --git a/crates/jcode-desktop2/src/tests/actions.rs b/crates/jcode-desktop2/src/tests/actions.rs index f1399296e3..e706918562 100644 --- a/crates/jcode-desktop2/src/tests/actions.rs +++ b/crates/jcode-desktop2/src/tests/actions.rs @@ -1590,10 +1590,7 @@ fn a_new_session_preserves_the_old_panel_until_the_new_one_attaches() { let (updates_tx, updates_rx) = channel(); let (commands_tx, _commands_rx) = channel(); let commands = crate::harness::CommandSender::for_test(commands_tx); - app.harness = Some(( - updates_rx, - commands.clone(), - )); + app.harness = Some((updates_rx, commands.clone())); app.new_session(); diff --git a/crates/jcode-tui/src/tui/app/turn_memory.rs b/crates/jcode-tui/src/tui/app/turn_memory.rs index 16df9d35c8..12a4928dce 100644 --- a/crates/jcode-tui/src/tui/app/turn_memory.rs +++ b/crates/jcode-tui/src/tui/app/turn_memory.rs @@ -38,7 +38,8 @@ impl App { Some(lock) if lock.skills_fingerprint == fingerprint && lock.is_canary == is_canary - && lock.config_generation == config_generation => { + && lock.config_generation == config_generation => + { (lock.static_part.clone(), lock.context_info.clone()) } _ => { From c94fceac99337ba0d40df31a213ee330724fed93 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 25 Sep 2026 19:14:42 +0000 Subject: [PATCH 2/3] fix: detect an empty deploy key without using secrets in if GitHub rejects workflows that reference secrets from an if condition, so the previous guard never started a job. Record whether DEPLOY_KEY is set as a step output, and skip the linked-issue requirement while Issues are disabled on the repository. Co-authored-by: OnlineChef --- .github/workflows/ci.yml | 68 +++++++++++++++++++++++++---- .github/workflows/release.yml | 15 ++++++- .github/workflows/require-issue.yml | 15 +++++++ 3 files changed, 89 insertions(+), 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aac24fdbe4..858cbd1f8a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,9 +28,22 @@ jobs: # Git dependencies in this repo are public HTTPS. The deploy key is only # needed when a private git source is configured. webfactory/ssh-agent # fails the job when ssh-private-key is empty, which skipped every build - # and test on a repo that has no DEPLOY_KEY secret. + # and test on a repo that has no DEPLOY_KEY secret. `secrets` is not a + # valid `if` context, so the presence check is a step output. + - name: Detect deploy key + id: deploy_key + shell: bash + env: + DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }} + run: | + if [ -n "$DEPLOY_KEY" ]; then + echo "present=true" >> "$GITHUB_OUTPUT" + else + echo "present=false" >> "$GITHUB_OUTPUT" + fi + - name: Configure SSH for cargo git dependencies - if: ${{ secrets.DEPLOY_KEY != '' }} + if: steps.deploy_key.outputs.present == 'true' uses: webfactory/ssh-agent@dc588b651fe13675774614f8e6a936a468676387 with: ssh-private-key: ${{ secrets.DEPLOY_KEY }} @@ -183,9 +196,22 @@ jobs: # Git dependencies in this repo are public HTTPS. The deploy key is only # needed when a private git source is configured. webfactory/ssh-agent # fails the job when ssh-private-key is empty, which skipped every build - # and test on a repo that has no DEPLOY_KEY secret. + # and test on a repo that has no DEPLOY_KEY secret. `secrets` is not a + # valid `if` context, so the presence check is a step output. + - name: Detect deploy key + id: deploy_key + shell: bash + env: + DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }} + run: | + if [ -n "$DEPLOY_KEY" ]; then + echo "present=true" >> "$GITHUB_OUTPUT" + else + echo "present=false" >> "$GITHUB_OUTPUT" + fi + - name: Configure SSH for cargo git dependencies - if: ${{ secrets.DEPLOY_KEY != '' }} + if: steps.deploy_key.outputs.present == 'true' uses: webfactory/ssh-agent@dc588b651fe13675774614f8e6a936a468676387 with: ssh-private-key: ${{ secrets.DEPLOY_KEY }} @@ -427,9 +453,22 @@ jobs: # Git dependencies in this repo are public HTTPS. The deploy key is only # needed when a private git source is configured. webfactory/ssh-agent # fails the job when ssh-private-key is empty, which skipped every build - # and test on a repo that has no DEPLOY_KEY secret. + # and test on a repo that has no DEPLOY_KEY secret. `secrets` is not a + # valid `if` context, so the presence check is a step output. + - name: Detect deploy key + id: deploy_key + shell: bash + env: + DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }} + run: | + if [ -n "$DEPLOY_KEY" ]; then + echo "present=true" >> "$GITHUB_OUTPUT" + else + echo "present=false" >> "$GITHUB_OUTPUT" + fi + - name: Configure SSH for cargo git dependencies - if: ${{ secrets.DEPLOY_KEY != '' }} + if: steps.deploy_key.outputs.present == 'true' uses: webfactory/ssh-agent@dc588b651fe13675774614f8e6a936a468676387 with: ssh-private-key: ${{ secrets.DEPLOY_KEY }} @@ -687,9 +726,22 @@ jobs: # Git dependencies in this repo are public HTTPS. The deploy key is only # needed when a private git source is configured. webfactory/ssh-agent # fails the job when ssh-private-key is empty, which skipped every build - # and test on a repo that has no DEPLOY_KEY secret. + # and test on a repo that has no DEPLOY_KEY secret. `secrets` is not a + # valid `if` context, so the presence check is a step output. + - name: Detect deploy key + id: deploy_key + shell: bash + env: + DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }} + run: | + if [ -n "$DEPLOY_KEY" ]; then + echo "present=true" >> "$GITHUB_OUTPUT" + else + echo "present=false" >> "$GITHUB_OUTPUT" + fi + - name: Configure SSH for cargo git dependencies - if: ${{ secrets.DEPLOY_KEY != '' }} + if: steps.deploy_key.outputs.present == 'true' uses: webfactory/ssh-agent@dc588b651fe13675774614f8e6a936a468676387 with: ssh-private-key: ${{ secrets.DEPLOY_KEY }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4ace56bc91..1505863cff 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -92,8 +92,21 @@ jobs: # Same as CI: skip the agent when DEPLOY_KEY is unset. An empty key # makes webfactory/ssh-agent fail before the release build starts. + # `secrets` is not a valid `if` context, so the presence check is a step output. + - name: Detect deploy key + id: deploy_key + shell: bash + env: + DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }} + run: | + if [ -n "$DEPLOY_KEY" ]; then + echo "present=true" >> "$GITHUB_OUTPUT" + else + echo "present=false" >> "$GITHUB_OUTPUT" + fi + - name: Configure SSH for cargo git dependencies - if: ${{ secrets.DEPLOY_KEY != '' }} + if: steps.deploy_key.outputs.present == 'true' uses: webfactory/ssh-agent@dc588b651fe13675774614f8e6a936a468676387 with: ssh-private-key: ${{ secrets.DEPLOY_KEY }} diff --git a/.github/workflows/require-issue.yml b/.github/workflows/require-issue.yml index e4b13cbf9c..2e9f625278 100644 --- a/.github/workflows/require-issue.yml +++ b/.github/workflows/require-issue.yml @@ -39,6 +39,21 @@ jobs: const owner = context.repo.owner; const repo = context.repo.repo; + + // This repository currently has Issues turned off, and the token + // that runs CI cannot turn them back on. A linked-issue requirement + // then fails every pull request, including Dependabot, with no way + // to open the issue the check demands. Enforce the link only while + // issues exist. + const { data: repoData } = await github.rest.repos.get({ owner, repo }); + if (!repoData.has_issues) { + core.info( + 'Issues are disabled on this repository, so a linked issue cannot exist. ' + + 'The link requirement applies again when issues are enabled.' + ); + return; + } + const login = pr.user?.login || ''; // Dependabot cannot open a tracking issue for a lockfile bump. // Human PRs still have to link a real issue in this repository. From da9a15fb367f684e62518f1cfe95e8de7dce69b8 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Fri, 25 Sep 2026 19:45:40 +0000 Subject: [PATCH 3/3] fix: satisfy rustc 1.98 clippy deny-warnings Stable clippy now rejects needless late initialization and chunks_exact with a constant size. Initialize those bindings in the branch expression and decode bloom words with as_chunks. Co-authored-by: OnlineChef --- crates/jcode-app-core/src/tool/apply_patch.rs | 11 ++++----- .../src/tool/session_search_index.rs | 11 ++++----- .../src/markdown_render_full.rs | 10 +++----- .../src/markdown_render_lazy.rs | 10 +++----- crates/jcode-tui-render/src/swarm_gallery.rs | 9 ++++--- src/cli/commands.rs | 24 +++++++------------ 6 files changed, 28 insertions(+), 47 deletions(-) diff --git a/crates/jcode-app-core/src/tool/apply_patch.rs b/crates/jcode-app-core/src/tool/apply_patch.rs index f5d4618f50..716afcd609 100644 --- a/crates/jcode-app-core/src/tool/apply_patch.rs +++ b/crates/jcode-app-core/src/tool/apply_patch.rs @@ -581,18 +581,17 @@ fn parse_apply_patch(input: &str) -> Result> { continue; } - let change_context; - if current == "@@" { - change_context = None; + let change_context = if current == "@@" { i += 1; + None } else if let Some(ctx) = current.strip_prefix("@@ ") { - change_context = Some(ctx.to_string()); i += 1; + Some(ctx.to_string()) } else if is_first_chunk { - change_context = None; + None } else { break; - } + }; let mut old_lines = Vec::new(); let mut new_lines = Vec::new(); diff --git a/crates/jcode-app-core/src/tool/session_search_index.rs b/crates/jcode-app-core/src/tool/session_search_index.rs index 430d5314a1..959a92d053 100644 --- a/crates/jcode-app-core/src/tool/session_search_index.rs +++ b/crates/jcode-app-core/src/tool/session_search_index.rs @@ -298,13 +298,10 @@ impl TokenHashIndex { for (key, mtime_ms, size, overflow, token_count, word_count) in metas { let bytes = cursor.take(word_count * 8)?; let bits: Vec = bytes - .chunks_exact(8) - .map(|chunk| { - u64::from_le_bytes([ - chunk[0], chunk[1], chunk[2], chunk[3], chunk[4], chunk[5], chunk[6], - chunk[7], - ]) - }) + .as_chunks::<8>() + .0 + .iter() + .map(|chunk| u64::from_le_bytes(*chunk)) .collect(); entries.push(IndexEntry { key, diff --git a/crates/jcode-tui-markdown/src/markdown_render_full.rs b/crates/jcode-tui-markdown/src/markdown_render_full.rs index 1ffe706999..0ce457de19 100644 --- a/crates/jcode-tui-markdown/src/markdown_render_full.rs +++ b/crates/jcode-tui-markdown/src/markdown_render_full.rs @@ -640,13 +640,9 @@ pub fn render_markdown_with_width(text: &str, max_width: Option) -> Vec> = lead; let mut task_used = 0usize; - let used: usize; - if shown == 0 && !chips.is_empty() { + let used: usize = if shown == 0 && !chips.is_empty() { // Degenerate width: show the first chip truncated. let budget = width.saturating_sub(lead_w + if show_tally { tail_w + gap } else { 0 }); let c = &chips[0]; @@ -721,7 +720,7 @@ pub fn render_swarm_strip( let style = Style::default().fg(c.color); spans.push(Span::styled(format!("{} ", c.glyph), style)); spans.push(Span::styled(name.clone(), style)); - used = disp_w(&c.glyph) + 1 + disp_w(&name); + disp_w(&c.glyph) + 1 + disp_w(&name) } else { for (i, chip) in chips.iter().take(shown).enumerate() { if i > 0 { @@ -758,8 +757,8 @@ pub fn render_swarm_strip( Style::default().fg(rgb(140, 140, 150)), )); } - used = chips_used + task_used; - } + chips_used + task_used + }; // ---- Right-align the tail (tally [+ hint]) ---- if show_tally { diff --git a/src/cli/commands.rs b/src/cli/commands.rs index 8fc2314291..3c1520432b 100644 --- a/src/cli/commands.rs +++ b/src/cli/commands.rs @@ -2253,9 +2253,7 @@ Re-run with `--force` if you really want to stop the server."; let mut signaled_pid: Option = None; let mut stopped = false; - let detail: String; - - if let Some(info) = server_info.as_ref() { + let detail: String = if let Some(info) = server_info.as_ref() { let pid = info.pid; if crate::platform::is_process_running(pid) { #[cfg(unix)] @@ -2265,11 +2263,9 @@ Re-run with `--force` if you really want to stop the server."; match crate::platform::signal_detached_process_group(pid, libc::SIGTERM) { Ok(()) => { signaled_pid = Some(pid); - detail = format!("Sent SIGTERM to jcode server (pid {pid})."); - } - Err(e) => { - detail = format!("Failed to signal jcode server (pid {pid}): {e}"); + format!("Sent SIGTERM to jcode server (pid {pid}).") } + Err(e) => format!("Failed to signal jcode server (pid {pid}): {e}"), } } #[cfg(not(unix))] @@ -2277,25 +2273,23 @@ Re-run with `--force` if you really want to stop the server."; match crate::platform::signal_detached_process_group(pid, 0) { Ok(()) => { signaled_pid = Some(pid); - detail = format!("Terminated jcode server (pid {pid})."); - } - Err(e) => { - detail = format!("Failed to terminate jcode server (pid {pid}): {e}"); + format!("Terminated jcode server (pid {pid}).") } + Err(e) => format!("Failed to terminate jcode server (pid {pid}): {e}"), } } } else { - detail = format!("Registered jcode server (pid {pid}) is not running."); + format!("Registered jcode server (pid {pid}) is not running.") } } else if had_listener { // A listener answers but no registry entry maps to it. We deliberately // do not guess a pid; just reap the socket below once the listener is // gone. (This is rare: a daemon that bound the socket but never wrote a // registry entry.) - detail = "Found a live server socket with no registry entry.".to_string(); + "Found a live server socket with no registry entry.".to_string() } else { - detail = "No running jcode server found.".to_string(); - } + "No running jcode server found.".to_string() + }; // Wait for the listener to disappear after signalling. Escalate to SIGKILL // once if the daemon does not exit within the graceful window.