Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .config/nextest.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ windows-global-state = { max-threads = 1 }
filter = 'binary_id(tracedecay::mcp_handler_test)'
platform = { host = 'cfg(windows)' }
test-group = 'windows-global-state'
retries = 2
flaky-result = 'pass'

[[profile.ci.overrides]]
filter = 'binary_id(tracedecay::cli_non_interactive_test)'
platform = { host = 'cfg(windows)' }
test-group = 'windows-global-state'

[[profile.ci.overrides]]
filter = 'test(/^doctor::tests::/)'
Expand Down
91 changes: 59 additions & 32 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,14 +95,10 @@ jobs:
- name: Run tests
run: cargo nextest run --workspace --profile ci

windows-test-shards:
name: Test Windows ${{ matrix.partition }}/8
windows-test-build:
name: Build Windows test archive
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository || contains(fromJSON('["master","feature/holographic-memory"]'), github.event.pull_request.base.ref) }}
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
partition: [1, 2, 3, 4, 5, 6, 7, 8]
env:
RUSTC_WRAPPER: sccache
SCCACHE_GHA_ENABLED: "true"
Expand Down Expand Up @@ -135,39 +131,70 @@ jobs:
${{ runner.os }}-cargo-test-
${{ runner.os }}-cargo-

- name: Run Windows test shard
- name: Build Windows nextest archive
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$partition = [int]"${{ matrix.partition }}"
$partitions = 8
$metadata = cargo metadata --no-deps --format-version 1 | ConvertFrom-Json
$package = $metadata.packages | Where-Object { $_.name -eq "tracedecay" } | Select-Object -First 1
$targets = @($package.targets | Where-Object { $_.kind -contains "test" } | Sort-Object name)
$selected = New-Object System.Collections.Generic.List[string]
for ($i = 0; $i -lt $targets.Count; $i++) {
if (($i % $partitions) -eq ($partition - 1)) {
$selected.Add($targets[$i].name)
}
}
if ($selected.Count -eq 0) {
throw "No integration test targets selected for Windows shard $partition/$partitions"
}
Write-Host "Windows shard $partition/$partitions running $($selected.Count) integration test targets"
$nextestArgs = @("nextest", "run", "-p", "tracedecay", "--profile", "ci")
if ($partition -eq 1) {
$nextestArgs += @("--lib", "--bin", "tracedecay")
}
foreach ($target in $selected) {
$nextestArgs += @("--test", $target)
}
cargo @nextestArgs
run: cargo nextest archive -p tracedecay --profile ci --lib --bin tracedecay --tests --archive-file windows-nextest-archive.tar.zst

- name: Upload Windows nextest archive
uses: actions/upload-artifact@v4
with:
name: windows-nextest-archive
path: windows-nextest-archive.tar.zst
if-no-files-found: error
retention-days: 1

- name: Show sccache stats
if: always()
shell: pwsh
run: '& $env:SCCACHE_PATH --show-stats'

windows-test-shards:
name: Test Windows ${{ matrix.partition }}/16
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository || contains(fromJSON('["master","feature/holographic-memory"]'), github.event.pull_request.base.ref) }}
needs: [windows-test-build]
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
partition: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
steps:
- uses: actions/checkout@v7

- uses: dtolnay/rust-toolchain@stable

- name: Install ast-grep
shell: pwsh
run: |
npm install --global "@ast-grep/cli@$env:AST_GREP_VERSION"
ast-grep --version

- name: Install cargo-nextest
uses: taiki-e/install-action@nextest

- name: Download Windows nextest archive
uses: actions/download-artifact@v4
with:
name: windows-nextest-archive
path: .

- name: Run Windows test shard
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$partition = [int]"${{ matrix.partition }}"
$partitions = 16
$testThreads = 1
$partitionArg = "hash:$partition/$partitions"
$workspace = (Get-Location).Path
$archive = Join-Path $workspace "windows-nextest-archive.tar.zst"
$targetDir = Join-Path $workspace "target"
$cargoMetadata = Join-Path $targetDir "nextest/cargo-metadata.json"
$binariesMetadata = Join-Path $targetDir "nextest/binaries-metadata.json"
cargo nextest run --archive-file $archive --extract-to $workspace --extract-overwrite --profile ci --no-run
Write-Host "Windows shard $partition/$partitions running archive partition $partitionArg with $testThreads test threads"
$nextestArgs = @("nextest", "run", "--cargo-metadata", $cargoMetadata, "--binaries-metadata", $binariesMetadata, "--workspace-remap", $workspace, "--target-dir-remap", $targetDir, "--profile", "ci", "--partition", $partitionArg, "--test-threads", $testThreads)
cargo @nextestArgs

windows-test:
name: Test Windows
if: ${{ always() && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository || contains(fromJSON('["master","feature/holographic-memory"]'), github.event.pull_request.base.ref)) }}
Expand Down
8 changes: 5 additions & 3 deletions src/hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2326,11 +2326,13 @@ const CURSOR_SESSION_INGEST_BUDGET: Duration = Duration::from_secs(4);
/// Budget for the end-of-turn `stop` catch-up ingest (registered with a 30s timeout).
const CURSOR_STOP_INGEST_BUDGET: Duration = Duration::from_secs(25);
/// Budget for the transcript catch-up portion of the `preCompact` hook.
const CURSOR_PRE_COMPACT_INGEST_BUDGET: Duration = Duration::from_secs(20);
const CURSOR_PRE_COMPACT_INGEST_BUDGET: Duration = Duration::from_secs(30);
Comment thread
ScriptedAlchemy marked this conversation as resolved.
/// Budget for the auxiliary `cursor-agent` summary call inside the hook. Kept
/// below the registered Cursor hook timeout so the child can be killed/reaped
/// by `TraceDecay` rather than by Cursor killing the hook process.
const CURSOR_PRE_COMPACT_SUMMARY_BUDGET: Duration = Duration::from_secs(85);
/// by `TraceDecay` rather than by Cursor killing the hook process. Sized so
/// the ingest budget plus this cap stay below the overall preCompact budget,
/// leaving slack for LCM prepare/persist and process overhead.
const CURSOR_PRE_COMPACT_SUMMARY_BUDGET: Duration = Duration::from_secs(75);
/// Overall budget for the `preCompact` hook (registered with a 120s timeout).
const CURSOR_PRE_COMPACT_BUDGET: Duration = Duration::from_secs(115);
const COMPACTION_CONTEXT_RECOVERY_HINT: &str = "Context was just compacted. If important prior-session context seems missing, query TraceDecay session context before assuming the compacted summary is complete. Start with `tracedecay_message_search` or `tracedecay_lcm_expand_query`; use `tracedecay_lcm_describe` and `tracedecay_lcm_expand` when you need the summary DAG sources.";
Expand Down
35 changes: 16 additions & 19 deletions tests/dashboard_savings_api_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

mod common;

use std::path::{Path, PathBuf};
use std::path::Path;
use std::sync::Mutex;

use common::{
Expand All @@ -33,13 +33,16 @@ struct Fixture {
_env_guards: Vec<EnvVarGuard>,
base_url: String,
server: tokio::task::JoinHandle<()>,
global_db_path: PathBuf,
session_db_path: PathBuf,
project_root: PathBuf,
/// Start of the current UTC day; seeded timestamps hang off this.
day_start: i64,
}

#[derive(Clone, Copy, PartialEq, Eq)]
enum FixtureSeed {
Base,
DailyLimitRegression,
}

impl Drop for Fixture {
fn drop(&mut self) {
self.server.abort();
Expand Down Expand Up @@ -399,7 +402,7 @@ async fn seed_daily_limit_regression(
}
}

async fn start_fixture() -> Fixture {
async fn start_fixture(seed: FixtureSeed) -> Fixture {
let tmp = TempDir::new().expect("temp dir");
let project_root = tmp.path().join("project");
std::fs::create_dir_all(&project_root).expect("project dir");
Expand Down Expand Up @@ -433,6 +436,10 @@ async fn start_fixture() -> Fixture {
.expect("tracedecay init");
let session_db_path = project_session_db_path(&project_root);
seed_global_db(&session_db_path, &project_root, day_start).await;
if seed == FixtureSeed::DailyLimitRegression {
seed_daily_limit_regression(&session_db_path, &global_db_path, &project_root, day_start)
.await;
}
let port = pick_free_port();
let base_url = format!("http://127.0.0.1:{port}");
let server = tokio::spawn(async move {
Expand All @@ -446,9 +453,6 @@ async fn start_fixture() -> Fixture {
_env_guards: env_guards,
base_url,
server,
global_db_path,
session_db_path,
project_root,
day_start,
}
}
Expand Down Expand Up @@ -477,7 +481,7 @@ fn savings_ledger_endpoints_reflect_seeded_ledger() {
.unwrap_or_else(std::sync::PoisonError::into_inner);
let runtime = create_runtime();
runtime.block_on(async {
let fixture = start_fixture().await;
let fixture = start_fixture(FixtureSeed::Base).await;
let agent = http_agent();

// Capability flag + tab registration.
Expand Down Expand Up @@ -570,14 +574,7 @@ fn daily_model_series_limits_days_not_model_rows() {
.unwrap_or_else(std::sync::PoisonError::into_inner);
let runtime = create_runtime();
runtime.block_on(async {
let fixture = start_fixture().await;
seed_daily_limit_regression(
&fixture.session_db_path,
&fixture.global_db_path,
&fixture.project_root,
fixture.day_start,
)
.await;
let fixture = start_fixture(FixtureSeed::DailyLimitRegression).await;

let (_, models) = get_json(
&http_agent(),
Expand Down Expand Up @@ -633,7 +630,7 @@ fn session_costs_label_actual_vs_tokenized_vs_estimated() {
.unwrap_or_else(std::sync::PoisonError::into_inner);
let runtime = create_runtime();
runtime.block_on(async {
let fixture = start_fixture().await;
let fixture = start_fixture(FixtureSeed::Base).await;
let agent = http_agent();

// Whether this build carries the BPE tokenizer (the `token-counting`
Expand Down Expand Up @@ -824,7 +821,7 @@ fn pricing_serves_bundled_fallback_when_offline() {
.unwrap_or_else(std::sync::PoisonError::into_inner);
let runtime = create_runtime();
runtime.block_on(async {
let fixture = start_fixture().await;
let fixture = start_fixture(FixtureSeed::Base).await;
let agent = http_agent();

let (status, pricing) = get_json(
Expand Down
Loading