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
3 changes: 3 additions & 0 deletions .bazelrc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ startup --windows_enable_symlinks
common --enable_bzlmod
common --announce_rc
common --show_progress_rate_limit=15
# MODULE.bazel registers only nightly/2026-09-27. Without this, rules_rust
# resolves the stable channel and the build fails closed.
build --@rules_rust//rust/toolchain/channel=nightly
build --incompatible_enable_cc_toolchain_resolution
build --action_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1
build --workspace_status_command=scripts/bazel/workspace_status.sh
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -617,8 +617,6 @@ jobs:
# One graph walk per push. The release resolver repeats it on the tag it
# builds, since a release is dispatched without waiting on this run.
- name: Check the production feature graph
env:
RUSTUP_TOOLCHAIN: stable
run: python3 scripts/check-production-feature-profile.py

# The configuration users actually receive: the release build of
Expand Down
11 changes: 10 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -667,9 +667,18 @@ jobs:
ref: ${{ needs.validate-release.outputs.source_sha }}
persist-credentials: false

# rust-toolchain.toml is the only compiler. A floating stable channel
# would rebuild the SDK conformance daemon on a different rustc than
# the release binaries.
- name: Resolve pinned toolchain
id: rust-toolchain
shell: bash
run: |
python3 -c 'import pathlib, tomllib; print("toolchain=" + tomllib.loads(pathlib.Path("rust-toolchain.toml").read_text(encoding="utf-8"))["toolchain"]["channel"])' >> "$GITHUB_OUTPUT"

- uses: dtolnay/rust-toolchain@686976e191b89faba57d3206551f0f330d8cb249 # stable
with:
toolchain: stable
toolchain: ${{ steps.rust-toolchain.outputs.toolchain }}

- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
with:
Expand Down
39 changes: 0 additions & 39 deletions .github/workflows/upstream-compat.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,6 @@ on:
schedule:
- cron: "0 4 * * 1"
workflow_dispatch:
inputs:
nightly-date:
description: "Rust nightly snapshot to probe; empty uses yesterday UTC."
type: string
default: ""

permissions:
contents: read
Expand Down Expand Up @@ -45,37 +40,3 @@ jobs:
--incompatible_no_implicit_file_export \
--rewind_lost_inputs \
//crates/...

# Same lanes, nightly rustc: the probe runs one build against a recent
# upstream snapshot so next releases stop being a surprise. Never adopt
# nightly; the pinned stable 1.99.0 stays the authority lane.
nightly-rust-probe:
name: Rust nightly toolchain probe
runs-on: ubuntu-latest
timeout-minutes: 240
continue-on-error: ${{ github.event_name == 'pull_request' }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Re-pin MODULE.bazel to the nightly snapshot
env:
NIGHTLY_DATE_INPUT: ${{ inputs.nightly-date }}
run: |
set -euo pipefail
NIGHTLY_DATE="${NIGHTLY_DATE_INPUT:-$(date -u -d yesterday +%F)}"
echo "Probing Rust nightly $NIGHTLY_DATE"
if ! grep -F 'versions = ["1.99.0"],' MODULE.bazel >/dev/null; then
echo "MODULE.bazel no longer names 1.99.0; refresh this probe's patterns"
exit 2
fi
if ! grep -F 'rustfmt_version = "1.99.0"' MODULE.bazel >/dev/null; then
echo "MODULE.bazel no longer pins rustfmt to 1.99.0; refresh this probe's patterns"
exit 2
fi
sed -i "s|versions = \[\"1\.99\.0\"\]|versions = [\"nightly/$NIGHTLY_DATE\"]|" MODULE.bazel
sed -i "s|rustfmt_version = \"1\.99\.0\"|rustfmt_version = \"nightly/$NIGHTLY_DATE\"|" MODULE.bazel
grep -n "nightly" MODULE.bazel
- uses: ./.github/actions/setup-bazel
with:
cache-key: upstream-compat-nightly
- name: Build the Rust workspace on nightly
run: bazel build --config=ci //crates/... --keep_going
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,10 @@ edition = "2024"
version = "1.0.0-beta.87"

[workspace.dependencies]
asupersync = { version = "=0.5.0", default-features = false, features = ["proc-macros", "native-runtime"] }
# nightly-outcome-try is an asupersync default (Outcome's Try impls). It
# compiles only on the pinned nightly. native-runtime stays explicit because
# it is not part of that default set.
asupersync = { version = "=0.5.0", default-features = false, features = ["proc-macros", "native-runtime", "nightly-outcome-try"] }
# grafeo 0.5.43 from our fork (ScriptedAlchemy/grafeo, main). The
# published crates carry a tiered-storage
# arena whose `HotVersionRef` addresses a single 1 MiB chunk, which caps an
Expand Down
4 changes: 2 additions & 2 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ rust.toolchain(
"fastbuild": "1",
"opt": "3",
},
versions = ["1.99.0"],
rustfmt_version = "1.99.0",
versions = ["nightly/2026-09-27"],
rustfmt_version = "nightly/2026-09-27",
)
use_repo(rust, "rust_toolchains")
register_toolchains("@rust_toolchains//:all")
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<p align="center">
<a href="https://github.com/ScriptedAlchemy/tracedecay/releases/latest"><img src="https://img.shields.io/github/v/release/ScriptedAlchemy/tracedecay" alt="GitHub release"></a>
<a href="https://opensource.org/licenses/MIT"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License: MIT"></a>
<a href="https://www.rust-lang.org/"><img src="https://img.shields.io/badge/Rust-1.99.0-orange.svg" alt="Rust 1.99.0"></a>
<a href="https://www.rust-lang.org/"><img src="https://img.shields.io/badge/Rust-nightly--2026--09--27-orange.svg" alt="Rust nightly-2026-09-27"></a>
<img src="https://img.shields.io/badge/macOS-supported-blue.svg" alt="macOS">
<img src="https://img.shields.io/badge/Linux-supported-blue.svg" alt="Linux">
<img src="https://img.shields.io/badge/Windows-supported-blue.svg" alt="Windows">
Expand Down
6 changes: 2 additions & 4 deletions crates/tracedecay-agent-hosts/src/agents/chatgpt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,7 @@ impl AgentIntegration for ChatGptIntegration {
// proof that nothing was deployed.
chatgpt_staged_plugin_dir(home)
.read_dir()
.map(|mut entries| entries.next().is_some())
.unwrap_or(false)
.is_ok_and(|mut entries| entries.next().is_some())
}

fn detected_host_surface(&self, home: &Path, _profile: &ProfileRoot) -> Option<PathBuf> {
Expand Down Expand Up @@ -170,8 +169,7 @@ fn doctor_check_plugin(dc: &mut DoctorCounters, home: &Path) {
// fails rather than warning as if nothing were staged.
let has_residue = staged_dir
.read_dir()
.map(|mut entries| entries.next().is_some())
.unwrap_or(false);
.is_ok_and(|mut entries| entries.next().is_some());
if has_residue {
dc.fail(&format!(
"ChatGPT staged bundle at {} is incomplete: {} missing, run `tracedecay reinstall --agent chatgpt`",
Expand Down
6 changes: 2 additions & 4 deletions crates/tracedecay-agent-hosts/src/agents/claude.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1158,8 +1158,7 @@ fn doctor_check_permissions_json(dc: &mut DoctorCounters, home: &Path) {

let installed: Vec<&str> = settings["permissions"]["allow"]
.as_array()
.map(|arr| arr.iter().filter_map(|v| v.as_str()).collect())
.unwrap_or_default();
.map_or_default(|arr| arr.iter().filter_map(|v| v.as_str()).collect());

// The plugin-namespace entries are the ones the plugin MCP server actually
// matches against; without coverage every call to a tool prompts
Expand Down Expand Up @@ -1241,8 +1240,7 @@ pub fn check_install_stale(home: &Path) {
fn warn_missing_permissions(settings: &serde_json::Value) {
let installed: Vec<&str> = settings["permissions"]["allow"]
.as_array()
.map(|arr| arr.iter().filter_map(|v| v.as_str()).collect())
.unwrap_or_default();
.map_or_default(|arr| arr.iter().filter_map(|v| v.as_str()).collect());

// Check the plugin namespace, the entries the plugin MCP server matches.
// Missing coverage of `mcp__plugin_tracedecay_graph__*` is exactly what
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -724,8 +724,7 @@ exit 0"#;
config
.get(CODEX_MCP_SERVERS_KEY)
.and_then(toml::Value::as_table)
.map(|servers| servers.keys().cloned().collect())
.unwrap_or_default()
.map_or_default(|servers| servers.keys().cloned().collect())
}

/// A fresh MCP-only install ends with exactly one standalone server that
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -827,16 +827,13 @@ where
.latest_committed(context, observed_at)
.await
.filter(|publication| publication_matches_pin(publication, pin));
let candidates = latest_publication
.as_ref()
.map(|publication| {
context_scout_candidates_from_publication(
publication,
pin.configuration.control(),
observed_at,
)
})
.unwrap_or_default();
let candidates = latest_publication.as_ref().map_or_default(|publication| {
context_scout_candidates_from_publication(
publication,
pin.configuration.control(),
observed_at,
)
});
Some(ContextScoutCanonicalInputV1 {
address,
control: pin.configuration.control(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,7 @@ pub fn lookup_registered_context_scout_owners(
owners
.get(&project_id)
.cloned()
.map(|owner| vec![owner])
.unwrap_or_default()
.map_or_default(|owner| vec![owner])
}

/// What one conditional unregister did to the project's owner slot.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -739,14 +739,11 @@ impl HostBundleWriterV1 {
)?;
let mut component_manifests = previous
.as_ref()
.map(|receipt| receipt.component_manifests.clone())
.unwrap_or_default();
.map_or_default(|receipt| receipt.component_manifests.clone());
component_manifests.retain(|previous| previous.component != manifest.component);
component_manifests.push(manifest.clone());
component_manifests.sort_by_key(|manifest| manifest.component);
let mut component_receipts = previous
.map(|receipt| receipt.component_receipts)
.unwrap_or_default();
let mut component_receipts = previous.map_or_default(|receipt| receipt.component_receipts);
component_receipts.retain(|previous| previous.component != component_receipt.component);
component_receipts.push(component_receipt.clone());
component_receipts.sort_by_key(|receipt| receipt.component);
Expand Down
10 changes: 4 additions & 6 deletions crates/tracedecay-agent-hosts/src/agents/host_bundle_registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -984,9 +984,8 @@ mod tests {
/// component-set journal.
#[test]
fn opencode_core_assets_do_not_depend_on_the_running_executable_path() {
let running = std::env::current_exe()
.map(|path| path.to_string_lossy().into_owned())
.unwrap_or_default();
let running =
std::env::current_exe().map_or_default(|path| path.to_string_lossy().into_owned());
let installed =
super::super::which_tracedecay().unwrap_or_else(|| "tracedecay".to_string());
let assets = component_assets(
Expand Down Expand Up @@ -1049,9 +1048,8 @@ mod tests {
/// `component-set-journal.hermes.v1.json` behind.
#[test]
fn hermes_core_assets_do_not_depend_on_the_running_executable_path() {
let running = std::env::current_exe()
.map(|path| path.to_string_lossy().into_owned())
.unwrap_or_default();
let running =
std::env::current_exe().map_or_default(|path| path.to_string_lossy().into_owned());
let installed =
super::super::which_tracedecay().unwrap_or_else(|| "tracedecay".to_string());
let assets = component_assets(
Expand Down
4 changes: 2 additions & 2 deletions crates/tracedecay-agent-hosts/src/agents/host_cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,8 +170,8 @@ pub(crate) fn run_host_cli(program: &Path, args: &[&str], home: &Path) -> Result
}
};

let stdout = stdout_handle.map(join_reader).unwrap_or_default();
let stderr = stderr_handle.map(join_reader).unwrap_or_default();
let stdout = stdout_handle.map_or_default(join_reader);
let stderr = stderr_handle.map_or_default(join_reader);

Ok(HostCliOutcomeV1 {
program: rendered_program,
Expand Down
6 changes: 4 additions & 2 deletions crates/tracedecay-agent-hosts/src/hooks/tool_hints/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,10 @@ fn every_category_has_compact_skill_backed_rendering() {
let skill = visible
.split_once("Skill: tracedecay:")
.and_then(|(_, rest)| rest.split_once('.'))
.map(|(skill, _)| skill)
.unwrap_or_else(|| panic!("{category:?} hint names no skill:\n{visible}"));
.map_or_else(
|| panic!("{category:?} hint names no skill:\n{visible}"),
|(skill, _)| skill,
);
let skill_path = format!("skills/{skill}/SKILL.md");
let (_, skill_body) = shipped
.iter()
Expand Down
27 changes: 27 additions & 0 deletions crates/tracedecay-agent-hosts/src/native_integration/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,33 @@ pub struct DaemonNativeIntegrationOwner {
stack_runtimes: Arc<Mutex<BTreeMap<ManifestDigest, Arc<DaemonGitHubStackRuntimeV1>>>>,
}

// The stack-runtime map's `Send` proof walks the native coordinator past the
// recursion limit (rust-lang/rust#159228). Each field is checked on its own.
unsafe impl Send for DaemonNativeIntegrationOwner {}
unsafe impl Sync for DaemonNativeIntegrationOwner {}

const _: fn(&DaemonNativeIntegrationOwner) = |value| {
fn assert_send_sync<T: Send + Sync>(_: &T) {}
let DaemonNativeIntegrationOwner {
project_id,
repository_id,
service,
snapshots,
worktrees,
store,
scope_sets,
stack_runtimes,
} = value;
assert_send_sync(project_id);
assert_send_sync(repository_id);
assert_send_sync(service);
assert_send_sync(snapshots);
assert_send_sync(worktrees);
assert_send_sync(store);
assert_send_sync(scope_sets);
assert_send_sync(stack_runtimes);
};

impl DaemonNativeIntegrationOwner {
pub fn service(&self) -> &DaemonProjectNativeIntegrationService {
&self.service
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,25 @@ async fn drain_loop(coordinator: Arc<DaemonGitHubStackCoordinatorV1>, ports: Sta
}
}

// A `BTreeMap` of these runtimes proves the native service before `Send`
// bottoms out (rust-lang/rust#159228). Each field is checked on its own.
unsafe impl Send for DaemonGitHubStackRuntimeV1 {}
unsafe impl Sync for DaemonGitHubStackRuntimeV1 {}

const _: fn(&DaemonGitHubStackRuntimeV1) = |value| {
fn assert_send_sync<T: Send + Sync>(_: &T) {}
let DaemonGitHubStackRuntimeV1 {
scope,
coordinator,
ports,
tick_task,
} = value;
assert_send_sync(scope);
assert_send_sync(coordinator);
assert_send_sync(ports);
assert_send_sync(tick_task);
};

impl DaemonGitHubStackRuntimeV1 {
#[allow(clippy::too_many_arguments)]
#[tracing::instrument(
Expand Down
4 changes: 1 addition & 3 deletions crates/tracedecay-application/src/advisory/github_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -691,9 +691,7 @@ impl GitHubReviewRefreshStateV1 {
} else {
previous.and_then(|state| state.last_complete.clone())
};
let mut attempt_receipts = previous
.map(|state| state.attempt_receipts.clone())
.unwrap_or_default();
let mut attempt_receipts = previous.map_or_default(|state| state.attempt_receipts.clone());
if let Some(receipt) = receipt {
attempt_receipts.push(receipt);
if attempt_receipts.len() > MAX_GITHUB_REFRESH_ATTEMPT_RECEIPTS_V1 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -798,8 +798,7 @@ fn safe_github_review_url(value: &str) -> bool {
};
let segments = url
.path_segments()
.map(std::iter::Iterator::collect::<Vec<_>>)
.unwrap_or_default();
.map_or_default(std::iter::Iterator::collect::<Vec<_>>);
url.scheme() == "https"
&& url.host_str() == Some("github.com")
&& url.port().is_none()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,8 +228,7 @@ impl ProximityAssembly<'_> {
let ranges = self
.session_edit_spans
.get(&((*key).clone(), path.clone()))
.map(Vec::as_slice)
.unwrap_or_default();
.map_or_default(Vec::as_slice);
let (span, symbol) = participant_graph_address(participant_nodes, ranges);
let source = ObservationSourceIdentityV1::for_provider(
observation.envelope.provider().clone(),
Expand Down
Loading
Loading