Repository navigation
fix(core): avoid recompiling on Git index changes #2080
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| # Trajectory: Avoid rebuilding native core when Git index changes | ||
|
|
||
| > **Status:** ✅ Completed | ||
| > **Confidence:** 95% | ||
| > **Started:** November 4, 2026 at 08:29 AM | ||
| > **Completed:** November 4, 2026 at 08:32 AM | ||
|
|
||
| --- | ||
|
|
||
| ## Summary | ||
|
|
||
| Removed unused Git index watch; HEAD/ref metadata preserved. Actual build-script regression passes for staging, branch commits, detached commits and explicit releases. Controlled index-only release rebuild fell from 78.693s to 0.317s (272 artifacts fresh). Local benchmark only; clean CI recompilation and version metadata remain separate work. | ||
|
|
||
| **Approach:** Standard approach | ||
|
|
||
| --- | ||
|
|
||
| ## Key Decisions | ||
|
|
||
| ### Watch only Git inputs that affect embedded revision | ||
| - **Chose:** Watch only Git inputs that affect embedded revision | ||
| - **Reasoning:** Cargo fingerprint logs show touching only the builder Git index recompiles both public and private core (65.813 seconds versus 0.441 seconds unchanged). HEAD and its active ref remain watched because revision metadata depends on commits; the index never affects build output. | ||
|
|
||
| --- | ||
|
|
||
| ## Chapters | ||
|
|
||
| ### 1. Initial work | ||
| *Agent: codex-relay-desktop-speed-up* | ||
|
|
||
| - Watch only Git inputs that affect embedded revision: Watch only Git inputs that affect embedded revision |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| { | ||
| "id": "traj_hynv0vdcl7gp", | ||
| "version": 1, | ||
| "task": { | ||
| "title": "Avoid rebuilding native core when Git index changes" | ||
| }, | ||
| "status": "completed", | ||
| "startedAt": "2026-11-04T16:29:37.671Z", | ||
| "completedAt": "2026-11-04T16:32:57.025Z", | ||
| "agents": [ | ||
| { | ||
| "name": "codex-relay-desktop-speed-up", | ||
| "role": "lead", | ||
| "joinedAt": "2026-11-04T16:29:37.707Z" | ||
| } | ||
| ], | ||
| "chapters": [ | ||
| { | ||
| "id": "chap_44p6lwqnawlw", | ||
| "title": "Initial work", | ||
| "agentName": "codex-relay-desktop-speed-up", | ||
| "startedAt": "2026-11-04T16:29:37.707Z", | ||
| "endedAt": "2026-11-04T16:32:57.025Z", | ||
| "events": [ | ||
| { | ||
| "ts": 1793809796345, | ||
| "type": "decision", | ||
| "content": "Watch only Git inputs that affect embedded revision: Watch only Git inputs that affect embedded revision", | ||
| "raw": { | ||
| "question": "Watch only Git inputs that affect embedded revision", | ||
| "chosen": "Watch only Git inputs that affect embedded revision", | ||
| "alternatives": [], | ||
| "reasoning": "Cargo fingerprint logs show touching only the builder Git index recompiles both public and private core (65.813 seconds versus 0.441 seconds unchanged). HEAD and its active ref remain watched because revision metadata depends on commits; the index never affects build output." | ||
| }, | ||
| "significance": "high" | ||
| } | ||
| ] | ||
| } | ||
| ], | ||
| "retrospective": { | ||
| "summary": "Removed unused Git index watch; HEAD/ref metadata preserved. Actual build-script regression passes for staging, branch commits, detached commits and explicit releases. Controlled index-only release rebuild fell from 78.693s to 0.317s (272 artifacts fresh). Local benchmark only; clean CI recompilation and version metadata remain separate work.", | ||
| "approach": "Standard approach", | ||
| "confidence": 0.95 | ||
| }, | ||
| "commits": [], | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: This completed trajectory omits the implementation commit and every changed path, so its audit record cannot identify the work it completed. Regenerate the completed trajectory metadata after recording the implementation commit instead of leaving (Based on your team's feedback about complete trajectory trace metadata.) [70713d7e-b5b6-4c02-88cf-a284a308fd60] Prompt for AI agents |
||
| "filesChanged": [], | ||
| "projectId": "AgentWorkforce/relay", | ||
| "tags": [], | ||
| "_trace": { | ||
| "startRef": "ad983d7278bab88b0a7d541494521146b869f78a", | ||
| "endRef": "ad983d7278bab88b0a7d541494521146b869f78a" | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,241 @@ | ||
| //! Exercise the actual build script without compiling the relay dependency graph. | ||
|
|
||
| use std::{ | ||
| fs, | ||
| path::{Path, PathBuf}, | ||
| process::{Command, Output}, | ||
| time::{SystemTime, UNIX_EPOCH}, | ||
| }; | ||
|
|
||
| struct Fixture(PathBuf); | ||
|
|
||
| impl Fixture { | ||
| fn new() -> Self { | ||
| let nonce = SystemTime::now() | ||
| .duration_since(UNIX_EPOCH) | ||
| .unwrap() | ||
| .as_nanos(); | ||
| let path = | ||
| std::env::temp_dir().join(format!("relay-build-watch-{}-{nonce}", std::process::id())); | ||
| fs::create_dir(&path).unwrap(); | ||
| Self(path.canonicalize().unwrap()) | ||
| } | ||
| } | ||
|
|
||
| impl Drop for Fixture { | ||
| fn drop(&mut self) { | ||
| let _ = fs::remove_dir_all(&self.0); | ||
| } | ||
| } | ||
|
|
||
| fn successful(command: &mut Command) -> String { | ||
| let output: Output = command.output().expect("run build fixture command"); | ||
| assert!( | ||
| output.status.success(), | ||
| "{command:?} failed:\nstdout: {}\nstderr: {}", | ||
| String::from_utf8_lossy(&output.stdout), | ||
| String::from_utf8_lossy(&output.stderr), | ||
| ); | ||
| String::from_utf8(output.stdout).unwrap() | ||
| } | ||
|
|
||
| fn git(repo: &Path, args: &[&str]) -> String { | ||
| successful( | ||
| Command::new("git") | ||
| .arg("-C") | ||
| .arg(repo) | ||
| .args([ | ||
| "-c", | ||
| "user.name=Build test", | ||
| "-c", | ||
| "user.email=build-test@example.invalid", | ||
| ]) | ||
| .args(["-c", "commit.gpgsign=false"]) | ||
| .arg("-c") | ||
| .arg(format!( | ||
| "core.hooksPath={}", | ||
| repo.join(".git/no-hooks").display() | ||
| )) | ||
| .env_remove("GIT_DIR") | ||
| .env_remove("GIT_WORK_TREE") | ||
| .env_remove("GIT_INDEX_FILE") | ||
| .args(args), | ||
| ) | ||
| .trim() | ||
| .to_owned() | ||
| } | ||
|
|
||
| fn build_script(executable: &Path, runner: &Path, repo: &Path, release: Option<&str>) -> String { | ||
| let mut command = Command::new(executable); | ||
| command | ||
| .current_dir(runner) | ||
| .env_remove("AGENT_RELAY_PROBE_VERSION") | ||
| .env_remove("AGENT_RELAY_PROBE_BASE_VERSION") | ||
| .env_remove("AGENT_RELAY_SITE_URL") | ||
| .env_remove("AGENT_RELAY_URL_SCHEME") | ||
| .env_remove("AGENT_RELAY_WINDOWS_ICON") | ||
| .env_remove("GIT_DIR") | ||
| .env_remove("GIT_WORK_TREE") | ||
| .env_remove("GIT_INDEX_FILE") | ||
| .env("CARGO_PKG_VERSION", "0.1.0") | ||
| .env("CARGO_CFG_TARGET_OS", std::env::consts::OS) | ||
| .env("AGENT_RELAY_PROBE_GIT_DIR", repo) | ||
| .env("AGENT_RELAY_PROBE_CHANGELOG", repo.join("CHANGELOG.md")); | ||
| if let Some(version) = release { | ||
| // A release override must not read or watch the development changelog. | ||
| command.env("AGENT_RELAY_PROBE_VERSION", version).env( | ||
| "AGENT_RELAY_PROBE_CHANGELOG", | ||
| repo.join("missing-changelog.md"), | ||
| ); | ||
| } | ||
| successful(&mut command) | ||
| } | ||
|
|
||
| fn stamped<'a>(output: &'a str, name: &str) -> &'a str { | ||
| let prefix = format!("cargo:rustc-env={name}="); | ||
| output | ||
| .lines() | ||
| .find_map(|line| line.strip_prefix(&prefix)) | ||
| .unwrap() | ||
| } | ||
|
|
||
| fn assert_git_watches(output: &str, repo: &Path, runner: &Path, attached: bool) { | ||
| let paths: Vec<_> = output | ||
| .lines() | ||
| .filter_map(|line| line.strip_prefix("cargo:rerun-if-changed=")) | ||
| .map(PathBuf::from) | ||
| .map(|path| { | ||
| if path.is_absolute() { | ||
| path | ||
| } else { | ||
| runner.join(path) | ||
| } | ||
| }) | ||
| .filter_map(|path| path.canonicalize().ok()) | ||
| .collect(); | ||
| let git_path = |name: &str| { | ||
| let path = PathBuf::from(git(repo, &["rev-parse", "--git-path", name])); | ||
| let path = if path.is_absolute() { | ||
| path | ||
| } else { | ||
| repo.join(path) | ||
| }; | ||
| path.canonicalize().expect("Git fixture metadata exists") | ||
| }; | ||
| assert!( | ||
| paths.contains(&git_path("HEAD")), | ||
| "HEAD must still invalidate build identity" | ||
| ); | ||
| if attached { | ||
| let reference = git(repo, &["symbolic-ref", "HEAD"]); | ||
| assert!( | ||
| paths.contains(&git_path(&reference)), | ||
| "commits must still invalidate build identity" | ||
| ); | ||
| } else { | ||
| assert!( | ||
| !paths.contains(&git_path("refs/heads/main")), | ||
| "a detached build must not watch the previous branch" | ||
| ); | ||
| } | ||
| assert!( | ||
| !paths.contains(&git_path("index")), | ||
| "staging does not change embedded identity" | ||
| ); | ||
| } | ||
|
|
||
| #[test] | ||
| fn index_changes_preserve_identity_and_commits_update_it() { | ||
| let fixture = Fixture::new(); | ||
| let repo = fixture.0.join("builder"); | ||
| let runner = fixture.0.join("runner"); | ||
| fs::create_dir(&repo).unwrap(); | ||
| fs::create_dir(&runner).unwrap(); | ||
| git(&repo, &["init", "--quiet"]); | ||
| git(&repo, &["symbolic-ref", "HEAD", "refs/heads/main"]); | ||
| fs::write(repo.join("CHANGELOG.md"), "## v2026.11.4\n\n- A release.\n").unwrap(); | ||
| git(&repo, &["add", "CHANGELOG.md"]); | ||
| git(&repo, &["commit", "--quiet", "-m", "initial"]); | ||
| let original_revision = git(&repo, &["rev-parse", "--short=12", "HEAD"]); | ||
|
|
||
| let manifest = PathBuf::from(env!("CARGO_MANIFEST_DIR")); | ||
| let executable = fixture | ||
| .0 | ||
| .join(format!("build-script{}", std::env::consts::EXE_SUFFIX)); | ||
| successful( | ||
| Command::new(std::env::var_os("RUSTC").unwrap_or_else(|| "rustc".into())) | ||
| .arg("--edition=2021") | ||
| .arg(manifest.join("build.rs")) | ||
| .arg("-o") | ||
| .arg(&executable), | ||
| ); | ||
| let development = build_script(&executable, &runner, &repo, None); | ||
| assert_git_watches(&development, &repo, &runner, true); | ||
| assert_eq!( | ||
| stamped(&development, "RELAY_DESKTOP_VERSION"), | ||
| format!("2026.11.4-dev+{original_revision}") | ||
| ); | ||
| assert_eq!( | ||
| stamped(&development, "RELAY_DESKTOP_REVISION"), | ||
| original_revision | ||
| ); | ||
|
|
||
| let release_version = "2026.11.5-beta.2"; | ||
| let release = build_script(&executable, &runner, &repo, Some(release_version)); | ||
| assert_git_watches(&release, &repo, &runner, true); | ||
| assert_eq!(stamped(&release, "RELAY_DESKTOP_VERSION"), release_version); | ||
| assert_eq!( | ||
| stamped(&release, "RELAY_DESKTOP_REVISION"), | ||
| original_revision | ||
| ); | ||
| assert!(!release | ||
| .lines() | ||
| .any(|line| line.starts_with("cargo:rerun-if-changed=") && line.contains("changelog"))); | ||
|
|
||
| fs::write(repo.join("staged.txt"), "only the index changes\n").unwrap(); | ||
| git(&repo, &["add", "staged.txt"]); | ||
| assert_eq!(build_script(&executable, &runner, &repo, None), development); | ||
| assert_eq!( | ||
| build_script(&executable, &runner, &repo, Some(release_version)), | ||
| release | ||
| ); | ||
|
|
||
| git(&repo, &["commit", "--quiet", "-m", "next source revision"]); | ||
| let revision = git(&repo, &["rev-parse", "--short=12", "HEAD"]); | ||
| assert_ne!(revision, original_revision); | ||
| let development = build_script(&executable, &runner, &repo, None); | ||
| assert_eq!( | ||
| stamped(&development, "RELAY_DESKTOP_VERSION"), | ||
| format!("2026.11.4-dev+{revision}") | ||
| ); | ||
| let release = build_script(&executable, &runner, &repo, Some(release_version)); | ||
| assert_eq!(stamped(&release, "RELAY_DESKTOP_VERSION"), release_version); | ||
| assert_eq!(stamped(&release, "RELAY_DESKTOP_REVISION"), revision); | ||
| assert_git_watches(&release, &repo, &runner, true); | ||
|
|
||
| git(&repo, &["checkout", "--quiet", "--detach", "HEAD"]); | ||
| let detached = build_script(&executable, &runner, &repo, Some(release_version)); | ||
| assert_git_watches(&detached, &repo, &runner, false); | ||
| assert_eq!(stamped(&detached, "RELAY_DESKTOP_VERSION"), release_version); | ||
| assert_eq!(stamped(&detached, "RELAY_DESKTOP_REVISION"), revision); | ||
|
|
||
| fs::write(repo.join("detached.txt"), "commit without a branch ref\n").unwrap(); | ||
| git(&repo, &["add", "detached.txt"]); | ||
| git( | ||
| &repo, | ||
| &["commit", "--quiet", "-m", "detached source revision"], | ||
| ); | ||
| let detached_revision = git(&repo, &["rev-parse", "--short=12", "HEAD"]); | ||
| assert_ne!(detached_revision, revision); | ||
| let detached = build_script(&executable, &runner, &repo, Some(release_version)); | ||
| assert_git_watches(&detached, &repo, &runner, false); | ||
| assert_eq!( | ||
| stamped(&detached, "RELAY_DESKTOP_REVISION"), | ||
| detached_revision | ||
| ); | ||
| let detached_development = build_script(&executable, &runner, &repo, None); | ||
| assert_eq!( | ||
| stamped(&detached_development, "RELAY_DESKTOP_VERSION"), | ||
| format!("2026.11.4-dev+{detached_revision}") | ||
| ); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: This completed trajectory is missing its generated
.trace.jsoncompanion. Run the completion/finalization flow that emits the raw trace rather than committing only the summary and trajectory snapshot.Prompt for AI agents