You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maka.apache.org by wy...@apache.org on 2026/10/20 13:55:51 UTC

(maka) branch rust-preview updated: fix(git): return complete repository status

This is an automated email from the ASF dual-hosted git repository.

M4n5ter pushed a commit to branch rust-preview
in repository https://gitbox.apache.org/repos/asf/maka.git


The following commit(s) were added to refs/heads/rust-preview by this push:
     new e21fe5b537 fix(git): return complete repository status
e21fe5b537 is described below

commit e21fe5b537bdb84bea34afd462189bd0f41206c1
Author: Wang <m4...@gmail.com>
AuthorDate: Tue Oct 20 21:55:39 2026 +0800

    fix(git): return complete repository status
    
    Remove observer count and byte refusals and return every status entry
    through the public Host service. Migrate the SDK and Desktop contract
    and retain repository authority, read-only checks and worker custody.
    
    Generated-by: Codex
---
 packages/plugin-sdk/src/contracts.ts               |   5 +-
 packages/plugin-sdk/src/generated/runtime.ts       |   1 -
 plugins/agent/api/README.md                        |   2 +-
 plugins/agent/api/README.zh-CN.md                  |   2 +-
 plugins/agent/native/src/owner/git.rs              |   1 -
 plugins/agent/native/src/owner/tests/git.rs        | 162 ++++++-
 plugins/agent/native/src/service.rs                |   7 +-
 plugins/workspace/workspace-files/engine/README.md |   2 +-
 .../workspace-files/engine/README.zh-CN.md         |   2 +-
 .../workspace/workspace-files/engine/src/git.rs    |   6 -
 .../workspace-files/engine/src/git/repository.rs   |  72 +--
 .../workspace-files/engine/src/git/scan.rs         |  71 +--
 .../workspace-files/engine/src/git/tests.rs        | 524 +++++++++++++++++++--
 products/maka/cli/notices.json                     |   8 +-
 products/maka/cli/src/acp/tests/public_client.rs   |   1 -
 products/maka/desktop-shell/app/changes.js         |  18 +-
 products/maka/desktop-shell/app/messages.js        |   5 -
 17 files changed, 674 insertions(+), 215 deletions(-)

diff --git a/packages/plugin-sdk/src/contracts.ts b/packages/plugin-sdk/src/contracts.ts
index 22c479442e..8bad0d6f40 100644
--- a/packages/plugin-sdk/src/contracts.ts
+++ b/packages/plugin-sdk/src/contracts.ts
@@ -12426,12 +12426,9 @@ export const contracts = {
                       $ref: '#/$defs/GitStatusEntry',
                     },
                   },
-                  truncated: {
-                    type: 'boolean',
-                  },
                 },
                 additionalProperties: false,
-                required: ['head', 'entries', 'truncated'],
+                required: ['head', 'entries'],
               },
               GitStatusEntry: {
                 type: 'object',
diff --git a/packages/plugin-sdk/src/generated/runtime.ts b/packages/plugin-sdk/src/generated/runtime.ts
index 7c2d56fa2f..fd199e748e 100644
--- a/packages/plugin-sdk/src/generated/runtime.ts
+++ b/packages/plugin-sdk/src/generated/runtime.ts
@@ -9895,7 +9895,6 @@ export interface GitStatusEntry {
 export interface GitStatus {
   head: string | null;
   entries: GitStatusEntry[];
-  truncated: boolean;
 }
 export interface AgentGitDiffRead {
   sessionId: string;
diff --git a/plugins/agent/api/README.md b/plugins/agent/api/README.md
index 4b2d3bcb4f..be5cded32e 100644
--- a/plugins/agent/api/README.md
+++ b/plugins/agent/api/README.md
@@ -41,7 +41,7 @@ Executor `execute` receives immutable files through one optional binary resource
 
 `gitStatus` and `gitDiff` read the selected Session's repository through the same Agent service, including WorkHub coordinator sessions. Status separates staged, worktree and untracked changes; a path can appear in both staged and worktree areas. Diff compares HEAD to the index, the index to the working file, or an empty file to an untracked file, respectively. An unborn repository has a null `head`; a workspace without a repository returns null. Paths use exact Git pathname bytes encode [...]
 
-These are fresh, read-only observations, not repository snapshots. Status fits within 512 KiB and marks an incomplete result with `truncated`. Per-file diff reads complete inputs and returns text, binary, unchanged or unsupported; text patches are returned in full. Inputs and patches are buffered in memory. Repository metadata, control-file and entry-count checks still apply. The workspace must be the repository root. Unsupported repository features fail explicitly, including external fi [...]
+These are fresh, read-only observations, not repository snapshots. Status returns the complete list of staged, worktree and untracked entries. Per-file diff reads complete inputs and returns text, binary, unchanged or unsupported; text patches are returned in full. Repository metadata, control files, status entries, diff inputs and patches are buffered in memory. Cancellation is cooperative; these queries do not promise bounded memory or a fixed execution time. The workspace must be the  [...]
 
 `listFiles` lists direct children of the selected Session workspace; `watchFiles` reports when that directory needs rereading. Both also work for WorkHub coordinator sessions. Use null `path` for the workspace root, or a `WorkspacePath` carrying exact relative pathname bytes as `bytesBase64`. Symlinks are leaf entries; `.git`, the workspace marker and private Host state/control directories are excluded, including in unrestricted model workspaces. Only the Session’s validated managed work [...]
 
diff --git a/plugins/agent/api/README.zh-CN.md b/plugins/agent/api/README.zh-CN.md
index 48feeb4498..76e7919b88 100644
--- a/plugins/agent/api/README.zh-CN.md
+++ b/plugins/agent/api/README.zh-CN.md
@@ -41,7 +41,7 @@ Executor 的 `execute` 通过名为 `attachments` 的单个可选二进制资源
 
 `gitStatus` 和 `gitDiff` 通过同一个 Agent 服务读取所选 Session 的仓库,也适用于 WorkHub 协调者会话。状态区分已暂存、工作区和未跟踪的变化;同一路径可以同时出现在已暂存和工作区部分。diff 分别比较 HEAD 与索引、索引与工作文件,或空文件与未跟踪文件。尚无提交的仓库返回 null `head`;没有仓库的工作区返回 null。路径以 `bytesBase64` 保留 Git 文件名的原始字节,重命名呈现为删除加新增。
 
-这些查询提供当前的只读观察,不是仓库快照。状态响应不超过 512 KiB,结果不完整时明确标记 `truncated`。单文件 diff 读取完整输入,区分文本、二进制、未变化和不支持的情况;文本补丁完整返回。输入和补丁在内存中缓冲,仓库元数据、control 文件和条目数量检查仍然适用。所选工作区必须是仓库根目录;外部过滤器、稀疏检出、子模块等不支持的仓库特性会明确失败,不执行配置中的命令。工作区或比较基准变化后应重新查询。协议类型见 [Git 声明](src/contract_git.rs);已完成工作的不可变补丁仍使用 `snapshotPatch` 和 `readPatch`,遵循各自的捕获限制。
+这些查询提供当前的只读观察,不是仓库快照。状态完整返回已暂存、工作区和未跟踪的条目列表。单文件 diff 读取完整输入,区分文本、二进制、未变化和不支持的情况;文本补丁完整返回。仓库元数据、control 文件、状态条目、diff 输入和补丁在内存中缓冲。取消采用协作方式;这些查询不承诺有界内存或固定执行时限。所选工作区必须是仓库根目录;外部过滤器、稀疏检出、子模块等不支持的仓库特性会明确失败,不执行配置中的命令。工作区或比较基准变化后应重新查询。协议类型见 [Git 声明](src/contract_git.rs);已完成工作的不可变补丁仍使用 `snapshotPatch` 和 `readPatch`,遵循各自的捕获限制。
 
 `listFiles` 列出所选 Session 工作区中一个目录的直接子项,`watchFiles` 通知该目录需要重新读取;也适用于 WorkHub 协调者会话。`path` 为 null 表示工作区根目录,其他路径使用 `WorkspacePath`,以 `bytesBase64` 保留相对文件名的原始字节。符号链接作为叶子展示,不进入其目标;排除 `.git`、工作区标记及 Host 私有状态/control 目录,模型配置为无限制访问时也不例外。私有状态内仅允许读取 Session 已验证的受管 worktree。每页最多 128 项、512 KiB,最多扫描 100,000 个直接子项;路径最多 4,096 字节、64 层。平台无法表示的文件名明确报错,不做有损转换。
 
diff --git a/plugins/agent/native/src/owner/git.rs b/plugins/agent/native/src/owner/git.rs
index 2711a130d8..8950df6b65 100644
--- a/plugins/agent/native/src/owner/git.rs
+++ b/plugins/agent/native/src/owner/git.rs
@@ -150,7 +150,6 @@ fn git_failure(error: &io::Error) -> &'static str {
         "git_invalid_path" => "agent_git_invalid_path",
         "git_unsupported" => "agent_git_unsupported",
         "git_private" => "agent_git_private",
-        "git_too_large" => "agent_git_too_large",
         "git_cancelled" => "agent_git_cancelled",
         "git_changed" => "agent_git_changed",
         "git_unavailable" => "agent_git_unavailable",
diff --git a/plugins/agent/native/src/owner/tests/git.rs b/plugins/agent/native/src/owner/tests/git.rs
index 7b29a8993f..7d02f45092 100644
--- a/plugins/agent/native/src/owner/tests/git.rs
+++ b/plugins/agent/native/src/owner/tests/git.rs
@@ -179,37 +179,169 @@ async fn git_status_and_diff_read_current_workspace_without_mutating_index() {
             f.config.shutdown().await.unwrap();
         }
         let status = observed.expect("Agent gitStatus must read the selected Session repository");
-        assert_eq!(status["truncated"], false);
         let mut entries = status["entries"].as_array().unwrap().clone();
-        entries.sort_by_key(|entry| (entry["area"].as_str().unwrap().to_owned(), entry["path"].to_string()));
-        assert_eq!(entries, vec![
-            json!({"area":"staged","path":{"bytesBase64":"dHJhY2tlZC50eHQ="},"kind":"modified"}),
-            json!({"area":"untracked","path":{"bytesBase64":"bmV3LnR4dA=="},"kind":"added"}),
-            json!({"area":"worktree","path":{"bytesBase64":"dHJhY2tlZC50eHQ="},"kind":"modified"}),
-        ]);
+        entries.sort_by_key(|entry| {
+            (
+                entry["area"].as_str().unwrap().to_owned(),
+                entry["path"].to_string(),
+            )
+        });
+        let mut status = status;
+        status["entries"] = json!(entries);
+        let head = std::fs::read_to_string(f.workspace.join(".git/refs/heads/main")).unwrap();
+        assert_eq!(
+            status,
+            json!({"head":head.trim(),"entries":[
+                {"area":"staged","path":{"bytesBase64":"dHJhY2tlZC50eHQ="},"kind":"modified"},
+                {"area":"untracked","path":{"bytesBase64":"bmV3LnR4dA=="},"kind":"added"},
+                {"area":"worktree","path":{"bytesBase64":"dHJhY2tlZC50eHQ="},"kind":"modified"},
+            ]})
+        );
         for (area, name, expected) in [
             ("staged", "tracked.txt", vec!["-base", "+staged"]),
             ("worktree", "tracked.txt", vec!["-staged", "+worktree"]),
             ("untracked", "new.txt", vec!["+new file"]),
         ] {
-            let diff = f.query("gitDiff", json!({"sessionId":session,"area":area,"path":path(name)})).await.unwrap();
+            let diff = f
+                .query(
+                    "gitDiff",
+                    json!({"sessionId":session,"area":area,"path":path(name)}),
+                )
+                .await
+                .unwrap();
             assert_eq!(changed_lines(&diff), expected, "{area}");
         }
         std::fs::write(f.workspace.join("tracked.txt"), "later edit\n").unwrap();
-        let fresh = f.query("gitDiff", json!({"sessionId":session,"area":"worktree","path":path("tracked.txt")})).await.unwrap();
+        let fresh = f
+            .query(
+                "gitDiff",
+                json!({"sessionId":session,"area":"worktree","path":path("tracked.txt")}),
+            )
+            .await
+            .unwrap();
         assert_eq!(changed_lines(&fresh), vec!["-staged", "+later edit"]);
         std::fs::remove_file(f.workspace.join("new.txt")).unwrap();
-        let fresh_status = f.query("gitStatus", json!({"sessionId":session})).await.unwrap();
-        let mut areas: Vec<_> = fresh_status["entries"].as_array().unwrap().iter().map(|entry| entry["area"].as_str().unwrap()).collect();
+        let fresh_status = f
+            .query("gitStatus", json!({"sessionId":session}))
+            .await
+            .unwrap();
+        let mut areas: Vec<_> = fresh_status["entries"]
+            .as_array()
+            .unwrap()
+            .iter()
+            .map(|entry| entry["area"].as_str().unwrap())
+            .collect();
         areas.sort();
         assert_eq!(areas, ["staged", "worktree"]);
         assert_eq!(std::fs::read(&index).unwrap(), index_bytes);
-        assert_eq!(std::fs::metadata(&index).unwrap().modified().unwrap(), index_modified);
+        assert_eq!(
+            std::fs::metadata(&index).unwrap().modified().unwrap(),
+            index_modified
+        );
         assert!(!f.workspace.join(".git/index.lock").exists());
-        assert_eq!(std::fs::read_to_string(f.workspace.join("tracked.txt")).unwrap(), "later edit\n");
+        assert_eq!(
+            std::fs::read_to_string(f.workspace.join("tracked.txt")).unwrap(),
+            "later edit\n"
+        );
         f.app.shutdown().await.unwrap();
         f.config.shutdown().await.unwrap();
-    }).await.unwrap();
+    })
+    .await
+    .unwrap();
+}
+
+#[tokio::test]
+async fn git_status_returns_every_literal_entry_through_public_host_without_mutating_repository() {
+    let f = Fixture::new("http://127.0.0.1:1/v1", None).await;
+    let session = f.create("Complete Git status").await;
+    repository(&f.workspace);
+    let staged = "staged-change.txt";
+    let worktree = "worktree-change.txt";
+    std::fs::write(f.workspace.join(staged), "staged base\n").unwrap();
+    std::fs::write(f.workspace.join(worktree), "worktree base\n").unwrap();
+    git(&f.workspace, &["add", staged, worktree]);
+    git(&f.workspace, &["commit", "--quiet", "-m", "status base"]);
+    std::fs::write(f.workspace.join(staged), "staged current\n").unwrap();
+    git(&f.workspace, &["add", staged]);
+    std::fs::write(f.workspace.join(worktree), "worktree current\n").unwrap();
+    let names = (0..1_800)
+        .map(|number| format!("literal-{number:04}-{}-中文.txt", "x".repeat(190)))
+        .collect::<Vec<_>>();
+    for name in &names {
+        std::fs::write(f.workspace.join(name), "literal untracked contents\n").unwrap();
+    }
+    let head = std::fs::read_to_string(f.workspace.join(".git/refs/heads/main")).unwrap();
+    let mut expected_entries = vec![
+        json!({"area":"staged","path":path(staged),"kind":"modified"}),
+        json!({"area":"worktree","path":path(worktree),"kind":"modified"}),
+    ];
+    expected_entries.extend(
+        names
+            .iter()
+            .map(|name| json!({"area":"untracked","path":path(name),"kind":"added"})),
+    );
+    let expected = json!({"head":head.trim(),"entries":expected_entries});
+    let preserved = [
+        ".git/HEAD",
+        ".git/refs/heads/main",
+        ".git/index",
+        staged,
+        worktree,
+    ]
+    .into_iter()
+    .chain(names.iter().map(String::as_str))
+    .map(|name| {
+        let file = f.workspace.join(name);
+        let bytes = std::fs::read(&file).unwrap();
+        let modified = std::fs::metadata(&file).unwrap().modified().unwrap();
+        (file, bytes, modified)
+    })
+    .collect::<Vec<_>>();
+    let observed = f.query("gitStatus", json!({"sessionId":session})).await;
+    // Retain the query outcome and close both real Host owners before any RED assertion.
+    let app_shutdown = f.app.shutdown().await;
+    let config_shutdown = f.config.shutdown().await;
+    app_shutdown.unwrap();
+    config_shutdown.unwrap();
+    for (file, bytes, modified) in preserved {
+        assert_eq!(
+            std::fs::read(&file).unwrap(),
+            bytes,
+            "query changed {}",
+            file.display()
+        );
+        assert_eq!(
+            std::fs::metadata(&file).unwrap().modified().unwrap(),
+            modified,
+            "query touched {}",
+            file.display()
+        );
+    }
+    assert!(!f.workspace.join(".git/index.lock").exists());
+    assert!(serde_json::to_vec(&expected).unwrap().len() > 512 * 1024);
+    let status = observed.expect("public Host gitStatus must deliver every repository entry");
+    assert_eq!(status["head"], head.trim());
+    assert_eq!(
+        status["entries"].as_array().unwrap().len(),
+        1_802,
+        "public Host gitStatus must not return a bounded prefix"
+    );
+    for (number, (actual, expected)) in status["entries"]
+        .as_array()
+        .unwrap()
+        .iter()
+        .zip(expected["entries"].as_array().unwrap())
+        .enumerate()
+    {
+        assert_eq!(
+            actual, expected,
+            "complete public Host status entry {number}"
+        );
+    }
+    assert!(
+        status == expected,
+        "complete public Host gitStatus must have exactly the expected head/entries shape"
+    );
 }
 
 #[tokio::test]
@@ -344,7 +476,7 @@ async fn git_queries_keep_unborn_binary_results_within_session_and_method_grants
         repository(&f.workspace);
         std::fs::write(f.workspace.join("binary.dat"), b"before\0after").unwrap();
         git(&f.workspace, &["add", "binary.dat"]);
-        let expected = json!({"head":null,"entries":[{"area":"staged","path":{"bytesBase64":"YmluYXJ5LmRhdA=="},"kind":"added"}],"truncated":false});
+        let expected = json!({"head":null,"entries":[{"area":"staged","path":{"bytesBase64":"YmluYXJ5LmRhdA=="},"kind":"added"}]});
         assert_eq!(f.query("gitStatus", json!({"sessionId":session})).await.unwrap(), expected);
         let input = json!({"sessionId":session,"area":"staged","path":path("binary.dat")});
         assert_eq!(f.query("gitDiff", input.clone()).await.unwrap(), json!({"kind":"binary"}));
diff --git a/plugins/agent/native/src/service.rs b/plugins/agent/native/src/service.rs
index 16f6ce2048..73ff7d4be6 100644
--- a/plugins/agent/native/src/service.rs
+++ b/plugins/agent/native/src/service.rs
@@ -280,12 +280,7 @@ impl Handler for Agent {
                 }
                 "forkReceipt" => encode(owner.fork_receipt(decode(input)?, context.scope()).await?),
                 "context" => encode(owner.context(decode(input)?, context.scope()).await?),
-                "gitStatus" => {
-                    let result = encode(owner.git_status(decode(input)?, context.scope()).await?)?;
-                    maka_plugin_core::json::check_delivery(&result, 512 * 1024)
-                        .map_err(|_| "agent_git_output_unavailable")?;
-                    Ok(result)
-                }
+                "gitStatus" => encode(owner.git_status(decode(input)?, context.scope()).await?),
                 "listFiles" => {
                     let result = encode(owner.list_files(decode(input)?, context.scope()).await?)?;
                     maka_plugin_core::json::check_delivery(&result, 512 * 1024)
diff --git a/plugins/workspace/workspace-files/engine/README.md b/plugins/workspace/workspace-files/engine/README.md
index b97468c7b5..11521b73a8 100644
--- a/plugins/workspace/workspace-files/engine/README.md
+++ b/plugins/workspace/workspace-files/engine/README.md
@@ -29,7 +29,7 @@ Text reads scan the original authorized file descriptor once per page, retaining
 
 `workspace` prepares intrinsic identity through the original captured directory. For a usable Git repository, it appends `.maka-workspace.json` to the common `info/exclude` only when absent, preserving existing bytes. This fixed preparation covers selected subdirectories and linked worktrees; it requires the real private Root/control boundaries and an inspected binding for managed worktrees. An unusable nearest `.git` entry does not redirect preparation to an ancestor repository; failure [...]
 
-`git` provides native, read-only repository status and per-file comparisons. Callers supply the original directory identity, private State Root and any validated managed-worktree binding, and retain the blocking worker until it settles. Git metadata and ignore/attribute control files are checked before library traversal; external commands and alternate object stores are not used. Cancellation is cooperative, and response limits do not promise a fixed execution time or an atomic repositor [...]
+`git` provides native, read-only repository status and per-file comparisons. Callers supply the original directory identity, private State Root and any validated managed-worktree binding, and retain the blocking worker until it settles. Git metadata and ignore/attribute control files are checked before library traversal; external commands and alternate object stores are not used. Status entries and text patches are returned in full. Repository metadata, control files and comparison resul [...]
 
 `files` lists direct children through held directory capabilities and reports native invalidation hints. The caller supplies the original workspace identity, private Root/control paths and validated managed-worktree binding; caller paths cannot create that exception. `WorkspacePath` is shared with Git and preserves exact relative pathname bytes. Listing is live and bounded, with no snapshot cache. A watch registers the workspace-to-target directory chain before readiness, ignores read/ac [...]
 
diff --git a/plugins/workspace/workspace-files/engine/README.zh-CN.md b/plugins/workspace/workspace-files/engine/README.zh-CN.md
index 498c0564f6..14cba957cd 100644
--- a/plugins/workspace/workspace-files/engine/README.zh-CN.md
+++ b/plugins/workspace/workspace-files/engine/README.zh-CN.md
@@ -29,7 +29,7 @@
 
 `workspace` 通过原始已捕获目录准备内在身份。存在可用 Git 仓库时,仅在缺少规则时向共用的 `info/exclude` 追加 `.maka-workspace.json`,保留已有字节。该固定初始化操作覆盖选中的子目录和 linked worktree,并要求真实私有 Root/control 边界及受检验的托管 worktree binding。最近的 `.git` 入口不可用时不会转而修改祖先仓库;向有效排除文件写入失败仍会报错。已跟踪文件和用户忽略规则保留 Git 原有的优先级。
 
-`git` 提供原生的只读仓库状态和单文件比较。调用方传入原目录身份、私有 State Root 及经过验证的受管 worktree 绑定,并持有阻塞 worker 直到工作结束。库遍历前先检查 Git 元数据及 ignore/attributes 控制文件,不执行外部命令或使用替代对象库。取消采用协作方式;响应大小限制不代表固定执行时限或原子仓库快照。
+`git` 提供原生的只读仓库状态和单文件比较。调用方传入原目录身份、私有 State Root 及经过验证的受管 worktree 绑定,并持有阻塞 worker 直到工作结束。库遍历前先检查 Git 元数据及 ignore/attributes 控制文件,不执行外部命令或使用替代对象库。状态条目和文本补丁完整返回。仓库元数据、控制文件和比较结果在内存中缓冲。取消采用协作方式;查询不承诺有界内存、固定执行时限或原子仓库快照。
 
 `files` 通过持有的目录能力列出直接子项,并提供原生变更失效提示。调用方传入原工作区身份、私有 Root/control 路径及经过验证的受管 worktree 绑定;调用方路径不能自行创建这个例外。与 Git 共用的 `WorkspacePath` 保留准确的相对路径字节。列表采用有界实时分页,不保存快照缓存。监听在就绪前注册工作区到目标目录的祖先链,忽略读取/access 通知、合并变更,并在交付前重新校验目录名称。即使客户端取消,原 worker 也必须等待 `Watch::close` 完成正常的原生监听及回调清理。原生文件系统通知不是持久事件历史。
 
diff --git a/plugins/workspace/workspace-files/engine/src/git.rs b/plugins/workspace/workspace-files/engine/src/git.rs
index 427cc796b8..e2c19db792 100644
--- a/plugins/workspace/workspace-files/engine/src/git.rs
+++ b/plugins/workspace/workspace-files/engine/src/git.rs
@@ -40,11 +40,6 @@ mod scan;
 #[cfg(test)]
 mod tests;
 
-const MAX_ENTRIES: usize = 100_000;
-const MAX_METADATA: usize = 50 * 1024 * 1024;
-const MAX_CONTROL: usize = 256 * 1024;
-const MAX_OUTPUT: usize = 512 * 1024;
-
 #[derive(
     Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, JsonSchema,
 )]
@@ -78,7 +73,6 @@ pub struct GitStatus {
     #[schemars(with = "maka_plugin_core::wire_schema::RequiredNullable<String>")]
     pub head: Option<String>,
     pub entries: Vec<GitStatusEntry>,
-    pub truncated: bool,
 }
 #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
 #[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
diff --git a/plugins/workspace/workspace-files/engine/src/git/repository.rs b/plugins/workspace/workspace-files/engine/src/git/repository.rs
index efb96cec1f..da14528d01 100644
--- a/plugins/workspace/workspace-files/engine/src/git/repository.rs
+++ b/plugins/workspace/workspace-files/engine/src/git/repository.rs
@@ -79,7 +79,7 @@ impl Repository {
         let git_dir = if metadata.is_dir() {
             entry.clone()
         } else if metadata.is_file() {
-            let bytes = bounded(&entry, MAX_CONTROL)?;
+            let bytes = read_file(&entry)?;
             let path = bytes
                 .strip_prefix(b"gitdir: ")
                 .ok_or_else(|| failure("git_unsupported"))?;
@@ -99,7 +99,7 @@ impl Repository {
             .map(dunce::canonicalize)
             .transpose()?;
         guard_metadata(&git_dir, &private_root, private_admin.as_deref())?;
-        let common_dir = match optional(&git_dir.join("commondir"), MAX_CONTROL)? {
+        let common_dir = match optional(&git_dir.join("commondir"))? {
             Some(bytes) => {
                 let path = bytes.strip_suffix(b"\n").unwrap_or(&bytes);
                 let path = path.strip_suffix(b"\r").unwrap_or(path);
@@ -152,11 +152,8 @@ impl Repository {
         {
             return Err(failure("git_unsupported"));
         }
-        let index_before = optional(&repo.index_path(), MAX_METADATA)?;
+        let index_before = optional(&repo.index_path())?;
         let index = repo.index_or_empty().map_err(io::Error::other)?;
-        if index.entries().len() > MAX_ENTRIES {
-            return Err(failure("git_too_large"));
-        }
         for entry in index.entries() {
             interrupted(&cancel)?;
             if entry.mode == Mode::COMMIT
@@ -179,9 +176,6 @@ impl Repository {
             .map_err(io::Error::other)?
             .detach();
         let baseline = repo.index_from_tree(&tree).map_err(io::Error::other)?;
-        if baseline.entries().len() > MAX_ENTRIES {
-            return Err(failure("git_too_large"));
-        }
         if baseline
             .entries()
             .iter()
@@ -224,9 +218,7 @@ impl Repository {
             .map_err(io::Error::other)?
             .id()
             .map(|id| id.detach().to_string());
-        if self.head != head
-            || self.index_before != optional(&self.repo.index_path(), MAX_METADATA)?
-        {
+        if self.head != head || self.index_before != optional(&self.repo.index_path())? {
             return Err(failure("git_changed"));
         }
         Ok(())
@@ -314,33 +306,19 @@ impl Repository {
         }
         entries.sort();
         entries.dedup();
-        let mut result = GitStatus {
+        Ok(GitStatus {
             head: self.head.clone(),
-            entries: Vec::new(),
-            truncated: true,
-        };
-        let mut size = serde_json::to_vec(&result).map_err(io::Error::other)?.len();
-        for (area, name, kind) in entries {
-            let entry = GitStatusEntry {
-                area,
-                path: WorkspacePath::from_bytes(&name)?,
-                kind,
-            };
-            let bytes = serde_json::to_vec(&entry).map_err(io::Error::other)?.len()
-                + usize::from(!result.entries.is_empty());
-            if size + bytes > MAX_OUTPUT {
-                return Ok(result);
-            }
-            size += bytes;
-            result.entries.push(entry);
-        }
-        result.truncated = false;
-        // false is one byte longer than true.
-        if size + 1 > MAX_OUTPUT {
-            result.entries.pop();
-            result.truncated = true;
-        }
-        Ok(result)
+            entries: entries
+                .into_iter()
+                .map(|(area, name, kind)| {
+                    Ok(GitStatusEntry {
+                        area,
+                        path: WorkspacePath::from_bytes(&name)?,
+                        kind,
+                    })
+                })
+                .collect::<io::Result<_>>()?,
+        })
     }
 }
 
@@ -355,32 +333,24 @@ pub(super) fn guard_metadata(
     }
     Ok(())
 }
-pub(super) fn optional(path: &Path, limit: usize) -> io::Result<Option<Vec<u8>>> {
+pub(super) fn optional(path: &Path) -> io::Result<Option<Vec<u8>>> {
     match fs::symlink_metadata(path) {
-        Ok(meta) if meta.is_file() && !meta.file_type().is_symlink() => {
-            bounded(path, limit).map(Some)
-        }
+        Ok(meta) if meta.is_file() && !meta.file_type().is_symlink() => read_file(path).map(Some),
         Ok(_) => Err(failure("git_unsupported")),
         Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(None),
         Err(e) => Err(e),
     }
 }
-pub(super) fn bounded(path: &Path, limit: usize) -> io::Result<Vec<u8>> {
+pub(super) fn read_file(path: &Path) -> io::Result<Vec<u8>> {
     let parent = Dir::open_ambient_dir(
         path.parent().ok_or_else(|| failure("git_unsupported"))?,
         ambient_authority(),
     )?;
     let mut bytes = Vec::new();
-    let file = parent.open_with(
+    let mut file = parent.open_with(
         path.file_name().ok_or_else(|| failure("git_unsupported"))?,
         crate::workspace::options().read(true),
     )?;
-    if file.metadata()?.len() > limit as u64 {
-        return Err(failure("git_too_large"));
-    }
-    file.take(limit as u64 + 1).read_to_end(&mut bytes)?;
-    if bytes.len() > limit {
-        return Err(failure("git_too_large"));
-    }
+    file.read_to_end(&mut bytes)?;
     Ok(bytes)
 }
diff --git a/plugins/workspace/workspace-files/engine/src/git/scan.rs b/plugins/workspace/workspace-files/engine/src/git/scan.rs
index 54d7a28033..2bab584819 100644
--- a/plugins/workspace/workspace-files/engine/src/git/scan.rs
+++ b/plugins/workspace/workspace-files/engine/src/git/scan.rs
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-use super::repository::{Repository, bounded, guard_metadata, optional};
+use super::repository::{Repository, guard_metadata, optional, read_file};
 use super::*;
 use cap_fs_ext::DirExt;
 use cap_std::fs::Dir;
@@ -36,7 +36,6 @@ pub(super) fn metadata(
     admin: Option<&Path>,
     cancel: &AtomicBool,
 ) -> io::Result<()> {
-    let mut count = 0;
     let mut roots = vec![git];
     if common != git {
         roots.push(common);
@@ -53,22 +52,11 @@ pub(super) fn metadata(
             "objects",
             "info",
         ] {
-            metadata_entry(
-                &root.join(name),
-                private,
-                admin,
-                cancel,
-                &mut count,
-                name == "objects",
-            )?;
+            metadata_entry(&root.join(name), private, admin, cancel, name == "objects")?;
         }
         // gix expands split-index links through these immutable sibling files.
         for entry in fs::read_dir(root)? {
             interrupted(cancel)?;
-            count += 1;
-            if count > MAX_ENTRIES {
-                return Err(failure("git_too_large"));
-            }
             let entry = entry?;
             if entry
                 .file_name()
@@ -76,7 +64,7 @@ pub(super) fn metadata(
                 .get(..12)
                 .is_some_and(|prefix| prefix.eq_ignore_ascii_case(b"sharedindex."))
             {
-                metadata_entry(&entry.path(), private, admin, cancel, &mut count, false)?;
+                metadata_entry(&entry.path(), private, admin, cancel, false)?;
             }
         }
     }
@@ -87,7 +75,6 @@ fn metadata_entry(
     private: &Path,
     admin: Option<&Path>,
     cancel: &AtomicBool,
-    count: &mut usize,
     object_store: bool,
 ) -> io::Result<()> {
     interrupted(cancel)?;
@@ -96,24 +83,19 @@ fn metadata_entry(
         Err(e) if e.kind() == io::ErrorKind::NotFound => return Ok(()),
         Err(e) => return Err(e),
     };
-    *count += 1;
-    if *count > MAX_ENTRIES {
-        return Err(failure("git_too_large"));
-    }
     if meta.file_type().is_symlink() || (!meta.is_dir() && !meta.is_file()) {
         return Err(failure("git_unsupported"));
     }
     guard_metadata(path, private, admin)?;
     if meta.is_dir() {
         for child in fs::read_dir(path)? {
-            metadata_entry(&child?.path(), private, admin, cancel, count, object_store)?;
+            metadata_entry(&child?.path(), private, admin, cancel, object_store)?;
         }
-    } else if object_store && path.file_name().is_some_and(|name| name == "alternates") {
-        if !bounded(path, MAX_CONTROL)?.is_empty() {
-            return Err(failure("git_unsupported"));
-        }
-    } else if !object_store && meta.len() > MAX_METADATA as u64 {
-        return Err(failure("git_too_large"));
+    } else if object_store
+        && path.file_name().is_some_and(|name| name == "alternates")
+        && !read_file(path)?.is_empty()
+    {
+        return Err(failure("git_unsupported"));
     }
     Ok(())
 }
@@ -121,7 +103,7 @@ fn metadata_entry(
 pub(super) fn workspace(repo: &Repository) -> io::Result<()> {
     let mut ignores = Search::default();
     let exclude = repo.repo.common_dir().join("info/exclude");
-    if let Some(bytes) = optional(&exclude, MAX_CONTROL)? {
+    if let Some(bytes) = optional(&exclude)? {
         ignores.add_patterns_buffer(&bytes, &exclude, None, Default::default());
     }
     let fs = repo.repo.filesystem_options().map_err(io::Error::other)?;
@@ -141,8 +123,6 @@ pub(super) fn workspace(repo: &Repository) -> io::Result<()> {
         } else {
             Case::Sensitive
         },
-        visited: 0,
-        control_bytes: 0,
     };
     walker.visit(&repo.directory, Path::new(""), false)
 }
@@ -153,8 +133,6 @@ struct Walker<'a> {
     precompose_unicode: bool,
     pattern_root: PathBuf,
     case: Case,
-    visited: usize,
-    control_bytes: usize,
 }
 impl Walker<'_> {
     fn visit(
@@ -179,10 +157,6 @@ impl Walker<'_> {
         self.control(directory, relative, ".gitattributes")?;
         for child in directory.entries()? {
             interrupted(&self.repo.cancel)?;
-            self.visited += 1;
-            if self.visited > MAX_ENTRIES {
-                return Err(failure("git_too_large"));
-            }
             let child = child?;
             let name = child.file_name();
             if name.as_encoded_bytes().eq_ignore_ascii_case(b".git") {
@@ -255,25 +229,16 @@ impl Walker<'_> {
         }
         .into_owned())
     }
-    fn control(
-        &mut self,
-        directory: &Dir,
-        relative: &Path,
-        name: &str,
-    ) -> io::Result<Option<Vec<u8>>> {
+    fn control(&self, directory: &Dir, relative: &Path, name: &str) -> io::Result<Option<Vec<u8>>> {
         let bytes = match directory.symlink_metadata(name) {
             Ok(meta) => {
                 if !meta.is_file() || meta.file_type().is_symlink() {
                     return Err(failure("git_unsupported"));
                 }
-                if meta.len() > MAX_CONTROL as u64 {
-                    return Err(failure("git_too_large"));
-                }
                 self.repo.check(&self.repo.root.join(relative).join(name))?;
                 let mut bytes = Vec::new();
                 directory
                     .open_with(name, crate::workspace::options().read(true))?
-                    .take(MAX_CONTROL as u64 + 1)
                     .read_to_end(&mut bytes)?;
                 bytes
             }
@@ -299,16 +264,6 @@ impl Walker<'_> {
                 {
                     return Err(failure("git_unsupported"));
                 }
-                if self
-                    .repo
-                    .repo
-                    .find_header(entry.id)
-                    .map_err(io::Error::other)?
-                    .size()
-                    > MAX_CONTROL as u64
-                {
-                    return Err(failure("git_too_large"));
-                }
                 self.repo
                     .repo
                     .find_object(entry.id)
@@ -318,10 +273,6 @@ impl Walker<'_> {
             }
             Err(e) => return Err(e),
         };
-        self.control_bytes += bytes.len();
-        if bytes.len() > MAX_CONTROL || self.control_bytes > MAX_METADATA {
-            return Err(failure("git_too_large"));
-        }
         Ok(Some(bytes))
     }
 }
diff --git a/plugins/workspace/workspace-files/engine/src/git/tests.rs b/plugins/workspace/workspace-files/engine/src/git/tests.rs
index 22e8cf89ba..e3586d2433 100644
--- a/plugins/workspace/workspace-files/engine/src/git/tests.rs
+++ b/plugins/workspace/workspace-files/engine/src/git/tests.rs
@@ -18,7 +18,13 @@
  */
 
 use super::*;
-use std::{fs, path::PathBuf, process::Command, time::SystemTime};
+use std::{
+    fs,
+    io::Write,
+    path::PathBuf,
+    process::{Command, Stdio},
+    time::SystemTime,
+};
 
 struct Fixture {
     _temp: tempfile::TempDir,
@@ -233,7 +239,10 @@ impl ReadOnlySnapshot {
     }
     fn assert_unchanged(&self, f: &Fixture) {
         let index = f.root.join(".git/index");
-        assert_eq!(fs::read(&index).unwrap(), self.index);
+        assert!(
+            fs::read(&index).unwrap() == self.index,
+            "index bytes changed"
+        );
         assert_eq!(
             fs::metadata(&index).unwrap().modified().unwrap(),
             self.index_modified
@@ -241,9 +250,8 @@ impl ReadOnlySnapshot {
         assert_eq!(git_stdout(&f.root, &["rev-parse", "HEAD"]), self.head);
         for (name, bytes, modified) in &self.files {
             let path = f.root.join(name);
-            assert_eq!(
-                fs::read(&path).unwrap(),
-                *bytes,
+            assert!(
+                fs::read(&path).unwrap() == *bytes,
                 "worktree bytes changed: {name}"
             );
             assert_eq!(
@@ -411,24 +419,23 @@ fn large_unselected_tracked_file_does_not_block_small_comparison() {
 }
 
 #[test]
-fn status_marks_the_bounded_serialized_prefix_and_preserves_actual_paths() {
+fn status_returns_every_entry_beyond_the_former_serialized_prefix() {
     let f = Fixture::new();
-    for i in 0..1800 {
-        fs::write(f.root.join(format!("{i:04}-{}", "x".repeat(190))), "").unwrap();
+    let names: Vec<_> = (0..1800)
+        .map(|i| format!("{i:04}-{}", "x".repeat(190)))
+        .collect();
+    for name in &names {
+        fs::write(f.root.join(name), "").unwrap();
     }
     let result = f.status();
-    assert!(result.truncated);
-    assert!(serde_json::to_vec(&result).unwrap().len() <= MAX_OUTPUT);
-    assert_eq!(
-        result.entries[0].path.bytes().unwrap(),
-        format!("0000-{}", "x".repeat(190)).as_bytes()
-    );
-    let last = result.entries.last().unwrap().path.bytes().unwrap();
-    assert!(
-        f.root
-            .join(gix::path::from_bstr(last.as_bstr()).as_ref())
-            .is_file()
-    );
+    assert!(serde_json::to_vec(&result).unwrap().len() > 512 * 1024);
+    let actual = entries(result);
+    assert_eq!(actual.len(), 1800);
+    for ((area, path, kind), name) in actual.iter().zip(&names) {
+        assert_eq!(*area, GitArea::Untracked);
+        assert_eq!(path, name.as_bytes());
+        assert_eq!(*kind, GitChangeKind::Added);
+    }
 }
 
 #[test]
@@ -661,7 +668,7 @@ fn case_renamed_tracked_directory_cannot_hide_an_ignored_control_symlink() {
         fs::write(f.root.join(original).join("tracked"), "tracked\n").unwrap();
         fs::write(
             f.root.join(original).join(".gitattributes"),
-            vec![b'#'; MAX_CONTROL + 1],
+            vec![b'#'; 256 * 1024 + 1],
         )
         .unwrap();
         f.commit();
@@ -675,13 +682,9 @@ fn case_renamed_tracked_directory_cannot_hide_an_ignored_control_symlink() {
         symlink(&private_ignore, f.root.join(renamed).join(".gitignore")).unwrap();
         let result = status(&f.root, &f.identity, &f.private, None, cancel());
         fs::remove_file(f.root.join(renamed).join(".gitignore")).unwrap();
-        let indexed_attributes = status(&f.root, &f.identity, &f.private, None, cancel());
         drop(f);
-        assert_eq!(
-            indexed_attributes.unwrap_err().to_string(),
-            "git_too_large",
-            "{original:?} -> {renamed:?}"
-        );
+        // Complete indexed-control fallback has a separate, spelling-stable
+        // fixture below; this regression proves tracked-directory authority.
         assert_eq!(
             result.unwrap_err().to_string(),
             "git_unsupported",
@@ -691,8 +694,7 @@ fn case_renamed_tracked_directory_cannot_hide_an_ignored_control_symlink() {
 }
 
 #[test]
-fn metadata_budget_is_not_bypassed_by_an_objects_named_workspace_ancestor() {
-    use std::io::Write;
+fn complete_large_configuration_is_accepted_and_late_filters_are_rejected() {
     let mut f = Fixture::new();
     fs::write(f.root.join("tracked"), "safe\n").unwrap();
     f.commit();
@@ -701,25 +703,58 @@ fn metadata_budget_is_not_bypassed_by_an_objects_named_workspace_ancestor() {
     let renamed = parent.join("repository");
     fs::rename(&f.root, &renamed).unwrap();
     f.root = renamed;
-    // A valid comment keeps this a real readable Git configuration, not a parser failure.
+    // A complete valid comment exceeds the former metadata ceiling.
     let mut config = fs::OpenOptions::new()
         .append(true)
         .open(f.root.join(".git/config"))
         .unwrap();
     config.write_all(b"\n#").unwrap();
     let chunk = [b'x'; 8192];
-    for _ in 0..MAX_METADATA / chunk.len() {
+    for _ in 0..6401 {
         config.write_all(&chunk).unwrap();
     }
+    config.write_all(b"\n").unwrap();
     drop(config);
-    let result = status(&f.root, &f.identity, &f.private, None, cancel());
-    drop(f);
-    assert_eq!(result.unwrap_err().to_string(), "git_too_large");
+    fs::write(f.root.join("tracked"), "modified\n").unwrap();
+    let snapshot = ReadOnlySnapshot::capture(&f, &["tracked", ".git/config"]);
+    let accepted = status(&f.root, &f.identity, &f.private, None, cancel());
+    snapshot.assert_unchanged(&f);
+    drop(snapshot);
+    assert_eq!(
+        git_stdout(&f.root, &["status", "--porcelain=v1"]),
+        b" M tracked\n"
+    );
+    let sentinel = f._temp.path().join("executed");
+    let mut config = fs::OpenOptions::new()
+        .append(true)
+        .open(f.root.join(".git/config"))
+        .unwrap();
+    writeln!(
+        config,
+        "[filter \"malicious\"]\n clean = touch {}",
+        sentinel.display()
+    )
+    .unwrap();
+    drop(config);
+    fs::write(f.root.join(".gitattributes"), "tracked filter=malicious\n").unwrap();
+    let snapshot = ReadOnlySnapshot::capture(&f, &["tracked", ".git/config", ".gitattributes"]);
+    let rejected = status(&f.root, &f.identity, &f.private, None, cancel());
+    snapshot.assert_unchanged(&f);
+    assert!(!sentinel.exists(), "external filter executed");
+    assert_eq!(
+        entries(accepted.unwrap().unwrap()),
+        vec![(
+            GitArea::Worktree,
+            b"tracked".to_vec(),
+            GitChangeKind::Modified
+        )]
+    );
+    assert_eq!(rejected.unwrap_err().to_string(), "git_unsupported");
 }
 
 #[cfg(unix)]
 #[test]
-fn split_index_reads_only_bounded_regular_shared_indexes() {
+fn split_index_reads_regular_shared_indexes_and_rejects_links_and_corruption() {
     use std::os::unix::fs::symlink;
     let f = Fixture::new();
     fs::write(f.root.join("tracked"), "safe\n").unwrap();
@@ -763,16 +798,421 @@ fn split_index_reads_only_bounded_regular_shared_indexes() {
         fs::write(&shared, &bytes).unwrap();
         result
     };
-    fs::OpenOptions::new()
-        .write(true)
-        .open(&shared)
-        .unwrap()
-        .set_len(MAX_METADATA as u64 + 1)
-        .unwrap();
+    // Padding does not make a valid large index. Keep corruption rejection
+    // explicit: the signature is invalid independently of the retired size gate.
+    let mut corrupt = fs::OpenOptions::new().write(true).open(&shared).unwrap();
+    corrupt.write_all(b"BAD!").unwrap();
+    corrupt.set_len(50 * 1024 * 1024 + 1).unwrap();
+    drop(corrupt);
     let oversized = status(&f.root, &f.identity, &f.private, None, cancel());
     drop(f);
     assert_eq!(result.unwrap_err().to_string(), "git_unsupported");
-    assert_eq!(oversized.unwrap_err().to_string(), "git_too_large");
+    assert_eq!(oversized.unwrap_err().to_string(), "git_unavailable");
     #[cfg(target_os = "macos")]
     assert_eq!(uppercase_result.unwrap_err().to_string(), "git_unsupported");
 }
+
+fn git_input(root: &Path, args: &[&str], input: &[u8]) -> Vec<u8> {
+    let mut child = Command::new("git")
+        .current_dir(root)
+        .env_remove("GIT_DIR")
+        .env_remove("GIT_WORK_TREE")
+        .env_remove("GIT_INDEX_FILE")
+        .env("GIT_CONFIG_NOSYSTEM", "1")
+        .env(
+            "GIT_CONFIG_GLOBAL",
+            if cfg!(windows) { "NUL" } else { "/dev/null" },
+        )
+        .args(args)
+        .stdin(Stdio::piped())
+        .stdout(Stdio::piped())
+        .stderr(Stdio::piped())
+        .spawn()
+        .unwrap();
+    child.stdin.take().unwrap().write_all(input).unwrap();
+    let output = child.wait_with_output().unwrap();
+    assert!(
+        output.status.success(),
+        "{}",
+        String::from_utf8_lossy(&output.stderr)
+    );
+    output.stdout
+}
+
+fn observe_diff(f: &Fixture, area: GitArea, name: &[u8]) -> io::Result<Option<GitDiff>> {
+    diff(
+        &f.root,
+        &f.identity,
+        &f.private,
+        None,
+        area,
+        &WorkspacePath::from_bytes(name).unwrap(),
+        cancel(),
+    )
+}
+
+fn large_comment_with_tail(tail: &[u8]) -> Vec<u8> {
+    let mut bytes = vec![b'#'; 256 * 1024 + 1];
+    bytes.push(b'\n');
+    bytes.extend_from_slice(tail);
+    bytes
+}
+
+#[test]
+fn complete_control_files_preserve_late_ignore_rules_and_indexed_attributes() {
+    let f = Fixture::new();
+    fs::write(f.root.join("text.txt"), "same\r\nsame\n").unwrap();
+    fs::write(
+        f.root.join(".gitattributes"),
+        large_comment_with_tail(b"text.txt text eol=lf\n"),
+    )
+    .unwrap();
+    f.commit();
+    // Different raw bytes with equal size require normalization without Git's
+    // size-mismatch status shortcut; an old mtime forces a content comparison.
+    fs::write(f.root.join("text.txt"), "same\nsame\r\n").unwrap();
+    fs::OpenOptions::new()
+        .write(true)
+        .open(f.root.join("text.txt"))
+        .unwrap()
+        .set_modified(SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(1))
+        .unwrap();
+    fs::write(
+        f.root.join(".gitignore"),
+        large_comment_with_tail(b"ignored-by-worktree/\n"),
+    )
+    .unwrap();
+    fs::write(
+        f.root.join(".git/info/exclude"),
+        large_comment_with_tail(b"ignored-by-exclude/\n"),
+    )
+    .unwrap();
+    for name in ["ignored-by-worktree", "ignored-by-exclude"] {
+        // Traversing either ignored directory would reject its non-file control.
+        fs::create_dir_all(f.root.join(name).join(".gitignore")).unwrap();
+        fs::write(f.root.join(name).join("hidden"), "hidden\n").unwrap();
+    }
+    let snapshot = ReadOnlySnapshot::capture(
+        &f,
+        &[
+            "text.txt",
+            ".gitattributes",
+            ".gitignore",
+            ".git/info/exclude",
+        ],
+    );
+    let disk_status = status(&f.root, &f.identity, &f.private, None, cancel());
+    let disk_diff = observe_diff(&f, GitArea::Worktree, b"text.txt");
+    snapshot.assert_unchanged(&f);
+    assert_eq!(
+        git_stdout(&f.root, &["status", "--porcelain=v1"]),
+        b"?? .gitignore\n"
+    );
+    fs::remove_file(f.root.join(".gitattributes")).unwrap();
+    fs::OpenOptions::new()
+        .write(true)
+        .open(f.root.join("text.txt"))
+        .unwrap()
+        .set_modified(SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(2))
+        .unwrap();
+    let snapshot = ReadOnlySnapshot::capture(&f, &["text.txt", ".gitignore", ".git/info/exclude"]);
+    let indexed_status = status(&f.root, &f.identity, &f.private, None, cancel());
+    let indexed_diff = observe_diff(&f, GitArea::Worktree, b"text.txt");
+    snapshot.assert_unchanged(&f);
+    assert_eq!(
+        git_stdout(&f.root, &["status", "--porcelain=v1"]),
+        b" D .gitattributes\n?? .gitignore\n"
+    );
+    assert_eq!(
+        entries(disk_status.unwrap().unwrap()),
+        vec![(
+            GitArea::Untracked,
+            b".gitignore".to_vec(),
+            GitChangeKind::Added
+        )]
+    );
+    assert_eq!(disk_diff.unwrap().unwrap(), GitDiff::Unchanged);
+    assert_eq!(
+        entries(indexed_status.unwrap().unwrap()),
+        vec![
+            (
+                GitArea::Worktree,
+                b".gitattributes".to_vec(),
+                GitChangeKind::Deleted
+            ),
+            (
+                GitArea::Untracked,
+                b".gitignore".to_vec(),
+                GitChangeKind::Added
+            ),
+        ]
+    );
+    assert_eq!(indexed_diff.unwrap().unwrap(), GitDiff::Unchanged);
+}
+
+#[cfg(unix)]
+#[test]
+fn large_ignore_prefix_does_not_hide_a_tracked_directory_control_symlink() {
+    use std::os::unix::fs::symlink;
+    let f = Fixture::new();
+    fs::create_dir(f.root.join("tracked-dir")).unwrap();
+    fs::write(f.root.join("tracked-dir/file"), "tracked\n").unwrap();
+    f.commit();
+    fs::write(
+        f.root.join(".gitignore"),
+        large_comment_with_tail(b"tracked-dir/\n"),
+    )
+    .unwrap();
+    let secret = f.private.join("ignore");
+    fs::write(&secret, "private sentinel\n").unwrap();
+    symlink(&secret, f.root.join("tracked-dir/.gitignore")).unwrap();
+    let snapshot = ReadOnlySnapshot::capture(&f, &["tracked-dir/file", ".gitignore"]);
+    let result = status(&f.root, &f.identity, &f.private, None, cancel());
+    snapshot.assert_unchanged(&f);
+    assert_eq!(fs::read(&secret).unwrap(), b"private sentinel\n");
+    assert_eq!(result.unwrap_err().to_string(), "git_unsupported");
+}
+
+#[test]
+fn nonempty_object_alternates_remain_unsupported_beyond_the_control_prefix() {
+    let f = Fixture::new();
+    fs::write(f.root.join("tracked"), "safe\n").unwrap();
+    f.commit();
+    fs::create_dir_all(f.root.join(".git/objects/info")).unwrap();
+    let alternates = f.root.join(".git/objects/info/alternates");
+    fs::write(&alternates, vec![b'\n'; 256 * 1024 + 1]).unwrap();
+    let snapshot = ReadOnlySnapshot::capture(&f, &["tracked", ".git/objects/info/alternates"]);
+    let result = status(&f.root, &f.identity, &f.private, None, cancel());
+    snapshot.assert_unchanged(&f);
+    assert_eq!(result.unwrap_err().to_string(), "git_unsupported");
+}
+
+#[test]
+fn aggregate_control_bytes_do_not_block_a_small_selected_comparison() {
+    let f = Fixture::new();
+    fs::write(f.root.join("small"), "before\n").unwrap();
+    f.commit();
+    fs::write(f.root.join("small"), "after\n").unwrap();
+    let source = f._temp.path().join("attributes");
+    let mut comment = vec![b'#'; 255 * 1024 - 1];
+    comment.push(b'\n');
+    fs::write(&source, &comment).unwrap();
+    // Each file is below 256KiB; 205 complete files exceed 50MiB. Hardlinks
+    // keep physical payload storage to one file without changing read behavior.
+    for i in 0..205 {
+        let directory = f.root.join(format!("controls-{i:03}"));
+        fs::create_dir(&directory).unwrap();
+        fs::hard_link(&source, directory.join(".gitattributes")).unwrap();
+    }
+    let snapshot = ReadOnlySnapshot::capture(
+        &f,
+        &[
+            "small",
+            "controls-000/.gitattributes",
+            "controls-204/.gitattributes",
+        ],
+    );
+    let result = observe_diff(&f, GitArea::Worktree, b"small");
+    snapshot.assert_unchanged(&f);
+    assert_patch_reconstructs(
+        result.unwrap().unwrap(),
+        "small",
+        Some(b"before\n"),
+        b"after\n",
+    );
+}
+
+#[test]
+fn large_index_and_head_path_sets_do_not_block_a_small_selected_comparison() {
+    let f = Fixture::new();
+    fs::write(f.root.join("anchor"), "anchor\n").unwrap();
+    f.commit();
+    let blob =
+        String::from_utf8(git_input(&f.root, &["hash-object", "-w", "--stdin"], b"")).unwrap();
+    let mut index_info = Vec::new();
+    for i in 0..100_001 {
+        writeln!(index_info, "100644 {}\tabsent-{i:06}", blob.trim()).unwrap();
+    }
+    git(&f.root, &["read-tree", "--empty"]);
+    git_input(&f.root, &["update-index", "--index-info"], &index_info);
+    assert_eq!(
+        git_stdout(&f.root, &["ls-files", "-z"])
+            .iter()
+            .filter(|&&byte| byte == 0)
+            .count(),
+        100_001
+    );
+    fs::write(f.root.join("selected"), "selected\n").unwrap();
+    let snapshot = ReadOnlySnapshot::capture(&f, &["selected"]);
+    let index_result = observe_diff(&f, GitArea::Untracked, b"selected");
+    snapshot.assert_unchanged(&f);
+    let tree = String::from_utf8(git_stdout(&f.root, &["write-tree"])).unwrap();
+    let commit = String::from_utf8(git_input(
+        &f.root,
+        &["commit-tree", tree.trim()],
+        b"many paths\n",
+    ))
+    .unwrap();
+    git(&f.root, &["update-ref", "HEAD", commit.trim()]);
+    git(&f.root, &["read-tree", "--empty"]);
+    assert_eq!(
+        git_stdout(&f.root, &["ls-tree", "-rz", "HEAD"])
+            .iter()
+            .filter(|&&byte| byte == 0)
+            .count(),
+        100_001
+    );
+    let snapshot = ReadOnlySnapshot::capture(&f, &["selected"]);
+    let head_result = observe_diff(&f, GitArea::Untracked, b"selected");
+    snapshot.assert_unchanged(&f);
+    for (name, result) in [("index", index_result), ("HEAD", head_result)] {
+        assert_patch_reconstructs(
+            result
+                .unwrap_or_else(|error| panic!("{name} count: {error}"))
+                .unwrap(),
+            "selected",
+            None,
+            b"selected\n",
+        );
+    }
+}
+
+#[test]
+fn large_workspace_and_metadata_entry_sets_do_not_block_a_small_comparison() {
+    let f = Fixture::new();
+    fs::write(f.root.join("small"), "before\n").unwrap();
+    f.commit();
+    fs::write(f.root.join("small"), "after\n").unwrap();
+    let many = f.root.join("many");
+    fs::create_dir(&many).unwrap();
+    // Group hardlinks to stay below filesystem per-inode link counts. Seeds are
+    // outside the workspace, and only one set of 100001 entries is constructed.
+    let mut seed = PathBuf::new();
+    for i in 0..100_001 {
+        if i % 1000 == 0 {
+            seed = f._temp.path().join(format!("empty-{}", i / 1000));
+            fs::write(&seed, "").unwrap();
+        }
+        fs::hard_link(&seed, many.join(format!("entry-{i:06}"))).unwrap();
+    }
+    let snapshot = ReadOnlySnapshot::capture(&f, &["small"]);
+    let workspace_result = observe_diff(&f, GitArea::Worktree, b"small");
+    snapshot.assert_unchanged(&f);
+    fs::rename(&many, f.root.join(".git/info/many")).unwrap();
+    let metadata_result = observe_diff(&f, GitArea::Worktree, b"small");
+    snapshot.assert_unchanged(&f);
+    // The shared-index discovery pass enumerates immediate Git-root entries,
+    // independently of the recursive metadata walk above. Reuse every entry.
+    let metadata_many = f.root.join(".git/info/many");
+    for entry in fs::read_dir(&metadata_many).unwrap() {
+        let entry = entry.unwrap();
+        fs::rename(entry.path(), f.root.join(".git").join(entry.file_name())).unwrap();
+    }
+    fs::remove_dir(&metadata_many).unwrap();
+    let git_root_result = observe_diff(&f, GitArea::Worktree, b"small");
+    snapshot.assert_unchanged(&f);
+    for (name, result) in [
+        ("workspace", workspace_result),
+        ("recursive metadata", metadata_result),
+        ("Git-root enumeration", git_root_result),
+    ] {
+        assert_patch_reconstructs(
+            result
+                .unwrap_or_else(|error| panic!("{name} count: {error}"))
+                .unwrap(),
+            "small",
+            Some(b"before\n"),
+            b"after\n",
+        );
+    }
+}
+
+// Git's index format permits unknown optional extensions with uppercase names.
+// Preserve real entries and encode a complete TEST extension plus the raw index
+// checksum; padding or a blob hash would not make a valid large index.
+fn add_large_optional_index_extension(path: &Path) -> (Vec<u8>, gix::hash::ObjectId) {
+    let mut bytes = fs::read(path).unwrap();
+    let old_checksum = bytes.split_off(bytes.len() - 20);
+    bytes.extend_from_slice(b"TEST");
+    let payload_size = 50 * 1024 * 1024 + 1;
+    bytes.extend_from_slice(&(payload_size as u32).to_be_bytes());
+    bytes.resize(bytes.len() + payload_size, 0);
+    let mut hasher = gix::hash::hasher(gix::hash::Kind::Sha1);
+    hasher.update(&bytes);
+    let checksum = hasher.try_finalize().unwrap();
+    bytes.extend_from_slice(checksum.as_bytes());
+    fs::write(path, &bytes).unwrap();
+    (old_checksum, checksum)
+}
+
+#[test]
+fn valid_large_indexes_and_shared_indexes_preserve_complete_selected_comparisons() {
+    let f = Fixture::new();
+    fs::write(f.root.join("small"), "before\n").unwrap();
+    f.commit();
+    fs::write(f.root.join("small"), "after\n").unwrap();
+    let original_index = fs::read(f.root.join(".git/index")).unwrap();
+    let expected_files = git_stdout(&f.root, &["ls-files", "--stage", "-z"]);
+    add_large_optional_index_extension(&f.root.join(".git/index"));
+    assert_eq!(
+        git_stdout(&f.root, &["ls-files", "--stage", "-z"]),
+        expected_files
+    );
+    let snapshot = ReadOnlySnapshot::capture(&f, &["small"]);
+    let index_result = observe_diff(&f, GitArea::Worktree, b"small");
+    snapshot.assert_unchanged(&f);
+    drop(snapshot);
+    fs::write(f.root.join(".git/index"), &original_index).unwrap();
+    git(&f.root, &["update-index", "--split-index"]);
+    let shared = fs::read_dir(f.root.join(".git"))
+        .unwrap()
+        .map(|entry| entry.unwrap().path())
+        .find(|path| {
+            path.file_name()
+                .unwrap()
+                .as_encoded_bytes()
+                .starts_with(b"sharedindex.")
+        })
+        .unwrap();
+    let (old_checksum, checksum) = add_large_optional_index_extension(&shared);
+    let renamed = shared.with_file_name(format!("sharedindex.{checksum}"));
+    fs::rename(&shared, &renamed).unwrap();
+    let mut index = fs::read(f.root.join(".git/index")).unwrap();
+    index.truncate(index.len() - 20);
+    let links: Vec<_> = index
+        .windows(28)
+        .enumerate()
+        .filter_map(|(offset, bytes)| {
+            (bytes.starts_with(b"link") && bytes[8..28] == old_checksum).then_some(offset + 8)
+        })
+        .collect();
+    assert_eq!(links.len(), 1, "expected one split-index link extension");
+    index[links[0]..links[0] + 20].copy_from_slice(checksum.as_bytes());
+    let mut hasher = gix::hash::hasher(gix::hash::Kind::Sha1);
+    hasher.update(&index);
+    index.extend_from_slice(hasher.try_finalize().unwrap().as_bytes());
+    fs::write(f.root.join(".git/index"), &index).unwrap();
+    assert_eq!(
+        git_stdout(&f.root, &["ls-files", "--stage", "-z"]),
+        expected_files
+    );
+    let snapshot = ReadOnlySnapshot::capture(
+        &f,
+        &[
+            "small",
+            renamed.strip_prefix(&f.root).unwrap().to_str().unwrap(),
+        ],
+    );
+    let shared_result = observe_diff(&f, GitArea::Worktree, b"small");
+    snapshot.assert_unchanged(&f);
+    for (name, result) in [("index", index_result), ("shared index", shared_result)] {
+        assert_patch_reconstructs(
+            result
+                .unwrap_or_else(|error| panic!("{name} bytes: {error}"))
+                .unwrap(),
+            "small",
+            Some(b"before\n"),
+            b"after\n",
+        );
+    }
+}
diff --git a/products/maka/cli/notices.json b/products/maka/cli/notices.json
index 5edcd2985d..dae4b0f097 100644
--- a/products/maka/cli/notices.json
+++ b/products/maka/cli/notices.json
@@ -5,8 +5,8 @@
     "package-lock.json": "a5af15aca21fabe0e1bdd48784167df4dc81cee17777d54861dcbd641168a4eb",
     "package.json": "38baf3beeb5876849ca16e31868d59bfaee769f260c7555b4a8a1847b92e28a1",
     "packages/plugin-sdk/src/configurations.ts": "e7bb3e7245adc43f27a3834c2522b2645540ae5a6a1300a706842abd4a4da852",
-    "packages/plugin-sdk/src/contracts.ts": "e2d9fd3d17c1a7a08a450e0d6c38026252482678c4a75cd08c02d61a03a581ee",
-    "packages/plugin-sdk/src/generated/runtime.ts": "877abeba50cc5620b885458148d0f84d68cf1da8263d728f2785666a8372109f",
+    "packages/plugin-sdk/src/contracts.ts": "bed2e4a0331650bf3641b12236f5e3560d1e506162237f234bf9194844adebc4",
+    "packages/plugin-sdk/src/generated/runtime.ts": "826265d786565bf0136543f5525514b29d68ce6786d28bdab3e0c4f7f44954c4",
     "packages/plugin-sdk/src/index.ts": "e33226c48bc54fbf377a604ead3b28ea448f50c287582804dab1e6e1059a6f71",
     "packages/plugin-sdk/src/native.ts": "6a826e012f5b7887c88ea295d5826b29939ae2ff0ff722db7b4124e37fc49749",
     "packages/plugin-sdk/src/ui.ts": "d669d2c5c70fc61aae3c0582f586950bf9cea863ee72b48af673e57c30c1b0b9",
@@ -400,7 +400,7 @@
     "products/maka/desktop-shell/Cargo.toml": "5e17a2121442e4d7d619b48aaff5e189ac141896122779d3c703bcd0217e4d6b",
     "products/maka/desktop-shell/app/.gitignore": "2062b06ca0c0837bc413b1be256047d54b7abbf44c6d1c356cb2e75219dbf155",
     "products/maka/desktop-shell/app/archived.js": "0dc3ae95a85df754679cbf4eccdfd6b3701100f6ea2f971f542e1e39ad9e4588",
-    "products/maka/desktop-shell/app/changes.js": "4a1c5e241eeef15a68c6fdf0e340e72c66fb31dd47b06a3a4ac5cde43e9098e3",
+    "products/maka/desktop-shell/app/changes.js": "cd94537df8609d25d91b95c5d9aa56919eabd0e8d80a369068b79fb0b8443b9e",
     "products/maka/desktop-shell/app/composer.js": "03f35e65e61bd52b123669ef905fdccf0eb0f5091f8ca496ea7750e51aa8b52a",
     "products/maka/desktop-shell/app/context.js": "be7c84743da7623db0881bea1e61ea162cfc796142430dc83ee36c57b35dac0a",
     "products/maka/desktop-shell/app/directories.js": "584765021111c993072d4533bd270fee87fdd217274d382f6108f293090e3a06",
@@ -455,7 +455,7 @@
     "products/maka/desktop-shell/app/jsconfig.json": "76f43dd8eb9005eee9ed713cc437052788bd3900bfb81f9b71d6f0a609e47f5c",
     "products/maka/desktop-shell/app/locale.js": "b1bb5c17c658a271a512b4a5b8be991e4fb18865ae6795c4a54fa8eefe3d0fcc",
     "products/maka/desktop-shell/app/main.js": "fa0c9f22c8399aca940b63d34f8402fcc31aa6d57172f813ebc0c31493c82086",
-    "products/maka/desktop-shell/app/messages.js": "8ef83a420351a82e319fdbb9f363d01cf62b8c81013a604a8595de8a4e5dd724",
+    "products/maka/desktop-shell/app/messages.js": "9875bb6ec902b42abf5ec2f89e08e3f05718f3e835074ea5a1826a4d891bd597",
     "products/maka/desktop-shell/app/patch.js": "590c7b57894888439dc5c196b50c7971e9c05ce8089784fd5af61c61aa60e6f8",
     "products/maka/desktop-shell/app/search.js": "8f959ffc154d28ad425e7a6e5d76a1d175b909937e056c914337066e56219863",
     "products/maka/desktop-shell/app/sessions.js": "e9143f98d5203a1583962d101690e1b1ce64389c3d2727a948dd522e0b3b1113",
diff --git a/products/maka/cli/src/acp/tests/public_client.rs b/products/maka/cli/src/acp/tests/public_client.rs
index 0bb77ed106..cf2f9fea2d 100644
--- a/products/maka/cli/src/acp/tests/public_client.rs
+++ b/products/maka/cli/src/acp/tests/public_client.rs
@@ -223,7 +223,6 @@ async fn independent_public_agent_client_reads_git_changes_after_reconnect() {
         assert_eq!(status, json!({
             "head":null,
             "entries":[{"area":"staged","path":{"bytesBase64":"dHJhY2tlZC50eHQ="},"kind":"added"}],
-            "truncated":false,
         }));
 
         fixture.native.close().await.unwrap();
diff --git a/products/maka/desktop-shell/app/changes.js b/products/maka/desktop-shell/app/changes.js
index 2dc9100828..1554d4b7a1 100644
--- a/products/maka/desktop-shell/app/changes.js
+++ b/products/maka/desktop-shell/app/changes.js
@@ -129,7 +129,7 @@ export class Changes {
     /**
      * The status as last read: undefined before the first read lands, null
      * when the folder is not a Git repository, else `{ head, entries,
-     * truncated, groups, order, paths, kinds }` with the entries as the list
+     * groups, order, paths, kinds }` with the entries as the list
      * draws them (entryOf).
      */
     this.status = undefined;
@@ -264,7 +264,6 @@ export class Changes {
       }
       this.status = {
         head: status.head,
-        truncated: status.truncated,
         entries,
         groups,
         order: groups.flat(),
@@ -386,9 +385,8 @@ export class Changes {
   }
 
   /**
-   * The list's items: a heading for each area with changes, its entries, and
-   * the note when the status was cut short. Rebuilt only when the status or
-   * the marked entry changed.
+   * The list's items: a heading for each area with changes and its entries.
+   * Rebuilt only when the status or the marked entry changed.
    */
   items() {
     const status = this.status;
@@ -403,9 +401,6 @@ export class Changes {
         items.push({ kind: 'entry', key: entry.key, entry, marked: entry.key === this.lastOpened });
       }
     }
-    if (status?.truncated) {
-      items.push({ kind: 'truncated', key: 'truncated', count: status.entries.length });
-    }
     this.view = { status, marked: this.lastOpened, items };
     return items;
   }
@@ -526,13 +521,6 @@ function changesItem(item, cx) {
         .child(text(String(item.count), 'small', m.inkMuted)),
     );
   }
-  if (item.kind === 'truncated') {
-    return row.child(
-      text(t('changesTruncated', { count: item.count }), 'small', m.inkMuted)
-        .px(8)
-        .truncate(),
-    );
-  }
   const entry = item.entry;
   const [letter, role] = entry.area === 'untracked' ? ['U', 'success'] : KINDS[entry.kind];
   return row.child(
diff --git a/products/maka/desktop-shell/app/messages.js b/products/maka/desktop-shell/app/messages.js
index 6f7add9a73..6ec8375a83 100644
--- a/products/maka/desktop-shell/app/messages.js
+++ b/products/maka/desktop-shell/app/messages.js
@@ -1489,11 +1489,6 @@ export const MESSAGES = {
   changesNoCommits: ['No commits yet', '还没有提交', '還沒有提交'],
   changesFilesOne: ['{count} file changed', '{count} 个文件有改动', '{count} 個檔案有變更'],
   changesFilesOther: ['{count} files changed', '{count} 个文件有改动', '{count} 個檔案有變更'],
-  changesTruncated: [
-    'Only the first {count} changes are listed',
-    '仅列出前 {count} 项改动',
-    '僅列出前 {count} 項變更',
-  ],
   diffUnstaged: ['Unstaged', '未暂存', '未暫存'],
   diffBack: ['Back to the list (Esc)', '返回列表(Esc)', '返回列表(Esc)'],
   diffPrevious: ['Previous file ([)', '上一个文件([)', '上一個檔案([)'],


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@maka.apache.org
For additional commands, e-mail: commits-help@maka.apache.org