fix(context): bound auto-generated project context
Refs #697 and #1827. Reported by @NASLXTO and @wuxixing. Prior context-cap and startup-diagnosis work by @linzhiqin2003 and @merchloubna70-dot shaped this fallback.
This commit is contained in:
+10
-1
@@ -71,6 +71,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Pending-input preview rows now label delivery mode explicitly as steer
|
||||
pending, rejected steer, or queued follow-up, with wrapped continuation rows
|
||||
aligned under the label so busy-turn input state is easier to read (#2054).
|
||||
- Auto-generated project instructions now reuse the bounded Project Context
|
||||
Pack data instead of running an unbounded summary/tree scan when no
|
||||
`.codewhale/instructions.md` file exists. The fallback keeps later
|
||||
top-level folders visible in noisy large workspaces while the dynamic
|
||||
`<project_context_pack>` marker remains controlled by its own setting
|
||||
(#697, #1827).
|
||||
|
||||
### Community
|
||||
|
||||
@@ -86,7 +92,10 @@ HarmonyOS/OpenHarmony port and MatePad Edge validation trail (#2634),
|
||||
**@idling11** for the PlanArtifact direction in Plan mode (#2733) and the
|
||||
dense tool-call transcript collapse direction (#2738, #2692), and
|
||||
**@h3c-hexin** for the tool-agent model inheritance and configured
|
||||
`skills_dir` fixes (#2736, #2737).
|
||||
`skills_dir` fixes (#2736, #2737). Thanks also to **@NASLXTO** and
|
||||
**@wuxixing** for the large-workspace startup reports (#697, #1827), and to
|
||||
**@linzhiqin2003** and **@merchloubna70-dot** for earlier context-cap and
|
||||
startup-diagnosis work that shaped this bounded fallback.
|
||||
|
||||
## [0.8.53] - 2026-06-03
|
||||
|
||||
|
||||
@@ -359,6 +359,22 @@ struct ReadmePack {
|
||||
/// sorted entries, bounded README text, and sorted JSON object fields. It does
|
||||
/// not include timestamps, random ids, absolute temp paths, or live git state.
|
||||
pub fn generate_project_context_pack(workspace: &Path) -> Option<String> {
|
||||
let pack = build_project_context_pack(workspace)?;
|
||||
let json = serde_json::to_string_pretty(&pack).ok()?;
|
||||
Some(format!(
|
||||
"## Project Context Pack\n\n<project_context_pack>\n{json}\n</project_context_pack>"
|
||||
))
|
||||
}
|
||||
|
||||
fn generate_bounded_project_overview(workspace: &Path) -> Option<String> {
|
||||
let pack = build_project_context_pack(workspace)?;
|
||||
let json = serde_json::to_string_pretty(&pack).ok()?;
|
||||
Some(format!(
|
||||
"## Bounded Project Overview\n\n```json\n{json}\n```"
|
||||
))
|
||||
}
|
||||
|
||||
fn build_project_context_pack(workspace: &Path) -> Option<ProjectContextPack> {
|
||||
let mut entries = Vec::new();
|
||||
collect_pack_entries(workspace, workspace, 0, &mut entries);
|
||||
sort_pack_paths(&mut entries);
|
||||
@@ -386,7 +402,7 @@ pub fn generate_project_context_pack(workspace: &Path) -> Option<String> {
|
||||
counts.insert("directory_entries".to_string(), entries.len());
|
||||
counts.insert("key_source_files".to_string(), key_source_files.len());
|
||||
|
||||
let pack = ProjectContextPack {
|
||||
Some(ProjectContextPack {
|
||||
project_name: workspace
|
||||
.file_name()
|
||||
.and_then(|name| name.to_str())
|
||||
@@ -397,12 +413,7 @@ pub fn generate_project_context_pack(workspace: &Path) -> Option<String> {
|
||||
config_files,
|
||||
key_source_files,
|
||||
counts,
|
||||
};
|
||||
|
||||
let json = serde_json::to_string_pretty(&pack).ok()?;
|
||||
Some(format!(
|
||||
"## Project Context Pack\n\n<project_context_pack>\n{json}\n</project_context_pack>"
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
fn collect_pack_entries(root: &Path, dir: &Path, depth: usize, out: &mut Vec<String>) {
|
||||
@@ -704,7 +715,7 @@ fn load_project_context_with_parents_and_home(
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-generate .deepseek/instructions.md when no context file exists anywhere.
|
||||
// Auto-generate .codewhale/instructions.md when no context file exists anywhere.
|
||||
// This avoids the per-turn filesystem scan fallback in prompts.rs that
|
||||
// breaks KV prefix cache stability.
|
||||
if !ctx.has_instructions()
|
||||
@@ -823,15 +834,13 @@ fn auto_generate_context(workspace: &Path) -> Option<String> {
|
||||
return None;
|
||||
}
|
||||
|
||||
let summary = crate::utils::summarize_project(workspace);
|
||||
let tree = crate::utils::project_tree(workspace, 2);
|
||||
let overview = generate_bounded_project_overview(workspace)?;
|
||||
|
||||
let content = format!(
|
||||
"# Project Structure (Auto-generated)\n\n\
|
||||
"# Project Context (Auto-generated)\n\n\
|
||||
> This file was automatically generated by CodeWhale.\n\
|
||||
> You can edit or delete it at any time.\n\n\
|
||||
**Summary:** {summary}\n\n\
|
||||
**Tree:**\n```\n{tree}\n```"
|
||||
{overview}"
|
||||
);
|
||||
|
||||
// Create .codewhale/ directory
|
||||
@@ -1379,6 +1388,52 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn auto_generated_context_is_bounded_for_many_file_workspace() {
|
||||
let workspace = tempdir().expect("workspace tempdir");
|
||||
let home = tempdir().expect("home tempdir");
|
||||
let noisy = workspace.path().join("aaa-many-files");
|
||||
fs::create_dir_all(&noisy).expect("mkdir noisy");
|
||||
for i in 0..1000 {
|
||||
fs::write(noisy.join(format!("file-{i:04}.rs")), "fn noisy() {}").expect("write noisy");
|
||||
}
|
||||
fs::create_dir_all(workspace.path().join("zzz-important")).expect("mkdir important");
|
||||
fs::write(
|
||||
workspace.path().join("zzz-important").join("main.rs"),
|
||||
"fn important() {}",
|
||||
)
|
||||
.expect("write important");
|
||||
|
||||
let start = std::time::Instant::now();
|
||||
let ctx = load_project_context_with_parents_and_home(workspace.path(), Some(home.path()));
|
||||
let elapsed = start.elapsed();
|
||||
assert!(
|
||||
elapsed < std::time::Duration::from_secs(2),
|
||||
"auto-generated context should stay bounded, took {elapsed:?}"
|
||||
);
|
||||
assert!(ctx.has_instructions());
|
||||
|
||||
let generated_path = workspace.path().join(".codewhale").join("instructions.md");
|
||||
assert_eq!(ctx.source_path.as_deref(), Some(generated_path.as_path()));
|
||||
let generated = fs::read_to_string(&generated_path).expect("read generated");
|
||||
assert!(generated.contains("Project Context (Auto-generated)"));
|
||||
assert!(generated.contains("Bounded Project Overview"));
|
||||
assert!(!generated.contains("<project_context_pack>"));
|
||||
assert!(
|
||||
generated.contains("\"zzz-important/\""),
|
||||
"later top-level project areas should remain visible:\n{generated}"
|
||||
);
|
||||
let noisy_count = generated.matches("aaa-many-files/file-").count();
|
||||
assert!(
|
||||
noisy_count < 300,
|
||||
"generated context should not list the whole noisy directory; saw {noisy_count}"
|
||||
);
|
||||
assert!(
|
||||
!generated.contains("file-0999.rs"),
|
||||
"bounded context should omit the tail of the noisy directory"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn project_context_pack_sort_is_cross_platform_and_priority_aware() {
|
||||
let mut unix_paths = vec![
|
||||
@@ -1657,7 +1712,7 @@ mod tests {
|
||||
ctx.instructions
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.contains("Project Structure (Auto-generated)")
|
||||
.contains("Project Context (Auto-generated)")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,9 +9,9 @@ PR is harvested, superseded, deferred, or closed.
|
||||
## Live Counts
|
||||
|
||||
- Actual open issues: 446
|
||||
- Open PRs: 56
|
||||
- Repo API open issue count: 502, because GitHub includes PRs in that total
|
||||
- Open issues labeled `v0.9.0`: 133
|
||||
- Open PRs: 57
|
||||
- Repo API open issue count: 503, because GitHub includes PRs in that total
|
||||
- Open issues labeled `v0.9.0`: 119
|
||||
- Open issues without a milestone: 102
|
||||
|
||||
## Execution Order
|
||||
@@ -51,6 +51,7 @@ harvest/stewardship commits:
|
||||
| #2737 configured `skills_dir` discovery | Locally harvested with explicit-config precedence. | The system prompt now unions workspace-discovered skills and configured `skills_dir` skills instead of treating the configured directory as a fallback. Explicit configured skills are inserted before global defaults so they are not lost behind a large global skill library. Credit @h3c-hexin; comment/close the original after the integration branch is public. |
|
||||
| #2738 dense tool-call transcript collapse | Locally harvested with expansion, cache-key, and safety fixes. | Successful read/search/list-style tool runs collapse by default once they cross the density threshold; failures, running cells, shell/exec, patch/write/edit/delete, diff preview, plan update, and review cells stay visible. Users can expand a group with Enter/Space/mouse and can set `tool_collapse = "compact" | "expanded" | "calm"`. Credit @idling11 and issue #2692; comment/close the original after the integration branch is public. |
|
||||
| #2532 pending-input delivery-mode labels | Locally re-harvested for #2054. | Pending-input preview rows now label steer-pending, rejected-steer, and queued-follow-up delivery modes, and wrapped continuation rows align under the label. `cargo test -p codewhale-tui --bin codewhale-tui --locked pending_input_preview -- --nocapture` passed. Credit @cyq1017; #2054 remains open for cancel/edit-mode affordance clarity. |
|
||||
| #697/#1827 bounded auto-generated project context | Locally implemented from the stabilization audit. | When no project instructions exist, startup now writes `.codewhale/instructions.md` from the bounded Project Context Pack data instead of an unbounded summary/tree scan. The generated file avoids the dynamic `<project_context_pack>` marker when that setting is disabled, keeps later top-level folders visible, and omits noisy directory tails. `cargo test -p codewhale-tui --bin codewhale-tui --locked auto_generated_context_is_bounded_for_many_file_workspace -- --nocapture` and `cargo test -p codewhale-tui --bin codewhale-tui --locked project_context_pack -- --nocapture` passed. Credit reporters @NASLXTO and @wuxixing, plus earlier context-cap/startup work from @linzhiqin2003 and @merchloubna70-dot; leave #697/#1827 open pending real massive-repo/manual startup verification. |
|
||||
| #2636 project-context mtime cache | Defer direct merge; harvest only after cache key/signature is widened. | Must include constitution changes, auto-generated context deletion, canonical path equivalence, and overwrite detection before landing. |
|
||||
| #2634 HarmonyOS port | Locally harvested with additional Nix-chain clearance; keep credited and do not close until the integration branch is public. | User-supplied MatePad Edge demo (`https://bilibili.com/video/av116689597368905`) confirms real-device interest. Added env-driven OpenHarmony SDK setup, OHOS platform guards/fallbacks, self-update disablement, and OHOS target gating for Starlark execpolicy parsing plus PTY support so published OHOS builds do not pull `nix` 0.28 through `rustyline` or `portable-pty`. `cargo check --workspace --all-features --locked`, focused PTY/clipboard tests, and `cargo tree --locked -p codewhale-tui --target aarch64-unknown-linux-ohos -i nix@0.28.0` passed; full OHOS target check is blocked on this host because `OHOS_NATIVE_SDK`/target CC/sysroot are not configured and `ring` cannot find `assert.h`. |
|
||||
| #2687 append-only mode/approval prompt | Defer direct merge; draft has compile failures and Plan-mode prompt correctness risks. | Any future harvest must keep stable `message[0]` genuinely mode-agnostic, preserve mode/approval suffixes after capacity replans, and distinguish external overrides from persisted generated prompts. |
|
||||
@@ -71,7 +72,7 @@ v0.9 branch so the remaining Windows/manual checks are explicit.
|
||||
| Windows IME/input recovery (#1835) | Partially fixed, still release-blocking. | Current branch has Windows IME recovery and char-routing tests, but the issue remains open with Windows/WSL reports. Needs a real Windows Terminal IME smoke for focus loss, idle, mode switch, first keystroke, and Esc recovery. |
|
||||
| Windows width/resize (#2708, #582 class) | Partially fixed on this branch. | #2708 is cherry-picked plus the fanout-card cache invalidation follow-up. `cargo test -p codewhale-tui --bin codewhale-tui --locked terminal_size -- --nocapture` passed. Still needs a real Windows Terminal resize smoke for #582 before #2721 closes. |
|
||||
| Windows shell descendant hangs (#2498, #1812 class) | Partially fixed and already harvested. | Foreground orphan-pipe regression passed locally with `cargo test -p codewhale-tui --all-features --locked foreground_shell_does_not_block_on_orphaned_subprocess_pipe -- --nocapture`. PR #2498 should close as harvested, but #1812 remains open for broader input-poll freeze modes and Windows CI/manual confirmation. |
|
||||
| Large-repo context startup (#697/#1827 class) | Partially covered. | Project-context pack ordering/budget/noise tests passed with `cargo test -p codewhale-tui --bin codewhale-tui --locked project_context_pack -- --nocapture`. Still missing a synthetic many-file startup smoke that exercises first-turn latency end to end. |
|
||||
| Large-repo context startup (#697/#1827 class) | Partially covered. | Project-context pack ordering/budget/noise tests passed, and the auto-generated fallback now has a synthetic 1000-file startup smoke with `cargo test -p codewhale-tui --bin codewhale-tui --locked auto_generated_context_is_bounded_for_many_file_workspace -- --nocapture`. Still needs a real massive-repo/manual startup benchmark before closing #697 or #1827. |
|
||||
| Sub-agent timeout and trust model (#1806, #719) | Fixed or covered in current branch. | `heartbeat_timeout_secs` clamp/default test passed, and `agent_open_description_explains_fresh_vs_forked_context_and_trust_model` asserts that sub-agent results are self-reports. |
|
||||
| Sub-agent checkpoint/resume (#2029) | Still release-blocking. | Session projection/transcript handles exist, but no checkpoint/continue status or resume contract has landed. Needs a child checkpoint/timeout/resume test that preserves policy and completes. |
|
||||
| Live shell/session liveness (#1786) | Partially fixed, still release-blocking. | Shell containment and turn-liveness tests exist, but orphaned PID/session-load reaping and long-running shell LIVE-state recovery remain open. Needs stale PID reaping and live-state regression coverage. |
|
||||
|
||||
Reference in New Issue
Block a user