Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
9958d00
Fail clippy only on diagnostics overlapping a changed hunk
thedancingdeveloper Oct 9, 2026
052e1b1
Use awk regex literals so mawk accepts the diff parse
thedancingdeveloper Oct 9, 2026
a1a55b6
Add configurable extra skill roots under [skills]
thedancingdeveloper Oct 9, 2026
eaccf56
Fill the new skill root fields in the config test
thedancingdeveloper Oct 9, 2026
2eb434e
Regenerate the config schema for the extra skill roots
thedancingdeveloper Oct 9, 2026
a0a515b
Fix hermeticity of repo ancestry test without project markers
thedancingdeveloper Oct 9, 2026
fdc424e
Add a config-gated Claude Code global-instructions provider
thedancingdeveloper Oct 9, 2026
0a6f97d
Confine @import expansion to the caller's allowed roots
thedancingdeveloper Oct 9, 2026
88b994e
Fill claude_compat_instructions in the thread-manager sample
thedancingdeveloper Oct 9, 2026
ed8017d
Format the Claude-compat instructions code
thedancingdeveloper Oct 9, 2026
f86cffa
Return the app-server time provider directly
thedancingdeveloper Oct 9, 2026
f55f282
Add a project_doc_mode that loads every project doc and its imports
thedancingdeveloper Oct 9, 2026
cd2e973
Format the project-doc compat loader
thedancingdeveloper Oct 9, 2026
f16557d
Regenerate the config schema for claude_compat_instructions
thedancingdeveloper Oct 10, 2026
c24a92c
hooks: add per-handler failClosed and review write_stdin input
thedancingdeveloper Oct 9, 2026
0f45beb
hooks: close the failClosed gaps the review found
thedancingdeveloper Oct 9, 2026
674c841
hooks: look up the exec start cwd without blocking
thedancingdeveloper Oct 9, 2026
5beae3a
hooks: format the write_stdin hook changes
thedancingdeveloper Oct 9, 2026
487b162
hooks: format the failClosed changes
thedancingdeveloper Oct 9, 2026
385bef2
Regenerate the hook handler schema fixtures for failClosed
thedancingdeveloper Oct 9, 2026
cc1d100
Regenerate the experimental app-server export for failClosed
thedancingdeveloper Oct 9, 2026
bffff76
Regenerate the config schema fixture for failClosed
thedancingdeveloper Oct 9, 2026
28a3aee
Let spawn_agent carry an output schema for the child's first turn
thedancingdeveloper Oct 9, 2026
bcbcf14
Drop the fail_closed literal from the session test
thedancingdeveloper Oct 9, 2026
52badca
Fill fail_closed in the session hook fixture
thedancingdeveloper Oct 10, 2026
477f7d1
fork-ci: read the base lock unlocked when HEAD's manifests add edges
thedancingdeveloper Oct 10, 2026
7d861cd
Accept scenario snapshots for the spawn_agent schema
thedancingdeveloper Oct 10, 2026
2b9e28d
Refuse instruction imports that cannot be shown to be inside a root
thedancingdeveloper Oct 10, 2026
f8bd418
fork-ci: ignore the daemon and shell-snapshot failures that fork/main…
thedancingdeveloper Oct 10, 2026
222490f
fork-ci: stop ignoring flaky and hook tests without a baseline failure
thedancingdeveloper Oct 12, 2026
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
25 changes: 21 additions & 4 deletions .github/fork-ci-known-failures.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
# Tests that fail on unmodified fork/main. One nextest filterset per line,
# with a `#` reason that cites the baseline run showing it fail. A line is
# only added after that evidence exists; the file starts empty.
test(pending_input_persistence::background_user_input) # timing flake: fails on PR #6/#12 in files the PR does not touch; green on the fork/main baseline
test(pending_input_persistence::synchronous_store) # timing flake: fails on PR #6/#12 in files the PR does not touch; green on the fork/main baseline
test(remote_env::future_pending_environment) # timing flake: fails on PR #6/#12 in files the PR does not touch; green on the fork/main baseline
test(hooks::permission_request_hook_allows_exec_command_without_user_approval) # timing flake: fails on PR #6/#12 in files the PR does not touch; green on the fork/main baseline
# Fail on unmodified fork/main on gha-spot-1 (fork-ci run 38089490933, workflow_dispatch
# with these packages forced). Environmental, not caused by a fork branch.
test(auto_daemon_start_attaches_to_shared_server) # fails on unmodified fork/main, fork-ci run 38089490933
test(incompatible_daemon_can_cancel) # fails on unmodified fork/main, fork-ci run 38089490933
test(incompatible_daemon_can_restart_with_confirmed_settings) # fails on unmodified fork/main, fork-ci run 38089490933
test(managed_daemon_restarts_from_deleted_working_directory) # fails on unmodified fork/main, fork-ci run 38089490933
test(managed_identity_survives_locale_and_timezone_changes) # fails on unmodified fork/main, fork-ci run 38089490933
test(managed_start_keeps_updater_on_marker_mismatch_but_stops_it_for_pin) # fails on unmodified fork/main, fork-ci run 38089490933
test(managed_start_succeeds_when_updater_record_is_invalid) # fails on unmodified fork/main, fork-ci run 38089490933
test(managed_starts_ensure_one_updater_and_recover_a_missing_one) # fails on unmodified fork/main, fork-ci run 38089490933
test(packaged_daemon_bootstrap_seeds_local_package) # fails on unmodified fork/main, fork-ci run 38089490933
test(packaged_daemon_explicit_replacement_migrates_running_legacy) # fails on unmodified fork/main, fork-ci run 38089490933
test(packaged_daemon_restart_seeds_local_package) # fails on unmodified fork/main, fork-ci run 38089490933
test(packaged_daemon_start_and_explicit_replacement) # fails on unmodified fork/main, fork-ci run 38089490933
test(restart_applies_saved_updater_preference) # fails on unmodified fork/main, fork-ci run 38089490933
test(wall_clock_shift_keeps_server_and_updater_managed) # fails on unmodified fork/main, fork-ci run 38089490933
test(shell_snapshot::shell_snapshot_concurrent_replays_keep_independent_readers::blocked_descriptor_path) # fails on unmodified fork/main, fork-ci run 38089490933
test(shell_snapshot_v2_capture_failure_falls_back_and_retries::remote_pipe_recovery) # fails on unmodified fork/main, fork-ci run 38089490933
test(shell_snapshot_v2_capture_failure_falls_back_and_retries::remote_tty_recovery) # fails on unmodified fork/main, fork-ci run 38089490933
test(shell_snapshot_v2_filters_profile_exports_and_stays_in_memory::remote_sandbox) # fails on unmodified fork/main, fork-ci run 38089490933

33 changes: 24 additions & 9 deletions .github/workflows/fork-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,12 @@ jobs:
# every later cargo invocation would compile against it.
trap 'mv /tmp/Cargo.lock.head Cargo.lock' EXIT
git show "$base:codex-rs/Cargo.lock" > Cargo.lock
cargo metadata --format-version 1 --locked > "$base_meta"
# Not --locked. The base lock describes the base manifests, and
# reading it against HEAD's manifests (a new edge on an existing
# package) makes cargo 1.95 call the lock stale even though the
# resolve is identical. --offline keeps it from moving a version;
# the copy above is discarded either way.
cargo metadata --format-version 1 --offline > "$base_meta"
mv /tmp/Cargo.lock.head Cargo.lock
trap - EXIT
mapfile -t locked < <(python3 "${GITHUB_WORKSPACE}/.github/scripts/fork-lock-affected.py" "$base_meta" "$head_meta")
Expand Down Expand Up @@ -210,16 +215,22 @@ jobs:
for crate in "${crates[@]}"; do args+=(-p "$crate"); done
cargo fmt "${args[@]}" -- --check

# -D warnings would fail the PR on upstream lint debt in files it never
# -D warnings would fail the PR on upstream lint debt in lines it never
# touched, and a fork patch must not have to fix that. Clippy runs without
# it, and only a warning or error whose primary span is in a file this PR
# changed fails the build. Compile errors are fatal regardless, which
# it, and only a warning or error whose primary span overlaps a hunk this
# PR changed fails the build. Compile errors are fatal regardless, which
# `cargo check` above already enforces.
- name: cargo clippy
shell: bash
run: |
set -euo pipefail
printf '%s\n' "${{ steps.crates.outputs.files }}" | sed 's|^codex-rs/||' > /tmp/changed-files
base="$(git merge-base HEAD origin/fork/main 2>/dev/null || git merge-base HEAD fork/main)"
# One changed line range per hunk, as "file start count" with the
# codex-rs/ prefix stripped. A count of 0 is a pure deletion.
git diff -U0 "$base"...HEAD -- codex-rs \
| awk '/^diff --git/{file=$4; sub(/^b\/codex-rs\//, "", file)}
/^@@/{split($3,a,","); sub(/^\+/, "", a[1]); print file, a[1], (a[2] == "" ? 1 : a[2])}' \
> /tmp/changed-lines
mapfile -t crates < <(printf '%s\n' "${{ steps.crates.outputs.list }}" | sed '/^$/d')
(( ${#crates[@]} )) || exit 0
args=()
Expand All @@ -233,18 +244,22 @@ jobs:
cargo clippy "${args[@]}" --all-targets --timings --message-format=json -- -W clippy::all \
> /tmp/clippy.json || true
[[ -f cargo-timing.html ]] && cp cargo-timing.html /tmp/cargo-timings/clippy-timing.html
# The gate is by changed file path, so it reads the whole stream once.
# The gate is by changed hunk, so it reads the whole stream once.
# Splitting per crate first drops every lint from a crate whose
# package id omits the name (cargo does that when the name equals the
# directory's last segment: codex-home, codex-api, ...).
rm -f /tmp/jq-err
offenders="$(jq -r --slurpfile changed <(jq -R . /tmp/changed-files) '
offenders="$(jq -r --slurpfile lines <(jq -R 'split(" ") | {file:.[0], start:.[1]|tonumber, count:.[2]|tonumber}' /tmp/changed-lines) '
select(.reason == "compiler-message")
| .message
| select(.level == "warning" or .level == "error")
| . as $m
| ($m.spans[] | select(.is_primary)) as $s
| select(any($changed[]; . == $s.file_name))
| select(any($lines[];
.file == $s.file_name
and .count > 0
and $s.line_start <= (.start + .count - 1)
and $s.line_end >= .start))
| "\($s.file_name):\($s.line_start):\($s.column_start): \($m.level): \($m.message)"
' /tmp/clippy.json 2>/tmp/jq-err)"
if [[ -s /tmp/jq-err ]]; then
Expand All @@ -254,7 +269,7 @@ jobs:
fi
if [[ -n "$offenders" ]]; then
echo "$offenders"
echo "clippy findings in files this PR changes" >&2
echo "clippy findings in lines this PR changes" >&2
exit 1
fi

Expand Down
2 changes: 2 additions & 0 deletions codex-rs/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file not shown.
Binary file not shown.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions codex-rs/app-server-protocol/src/protocol/v2/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -685,6 +685,10 @@ pub enum ConfiguredHookHandler {
#[serde(rename = "additionalContextLimit")]
#[ts(rename = "additionalContextLimit")]
additional_context_limit: Option<usize>,
/// When true, a failure of this hook blocks the tool instead of failing open.
#[serde(default, rename = "failClosed")]
#[ts(rename = "failClosed")]
fail_closed: bool,
},
#[serde(rename = "mcp_tool")]
#[ts(rename = "mcp_tool")]
Expand Down
8 changes: 5 additions & 3 deletions codex-rs/app-server/src/mcp_refresh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ mod tests {
use codex_core::thread_store_from_config;
use codex_exec_server::EnvironmentManager;
use codex_extension_api::NoopExtensionEventSink;
use codex_home::CodexHomeUserInstructionsProvider;
use codex_home::user_instructions_provider;
use codex_login::AuthManager;
use codex_login::CodexAuth;
use codex_protocol::protocol::SessionSource;
Expand Down Expand Up @@ -490,9 +490,11 @@ enabled = false
queue_service: None,
turn_start_admission: None,
}),
Arc::new(CodexHomeUserInstructionsProvider::new(
user_instructions_provider(
good_config.codex_home.clone(),
)),
good_config.claude_compat_instructions,
good_config.project_doc_max_bytes,
),
/*analytics_events_client*/ None,
codex_core::passthrough_image_store(),
Arc::clone(&thread_store),
Expand Down
17 changes: 9 additions & 8 deletions codex-rs/app-server/src/message_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ use codex_exec_server::EnvironmentManager;
use codex_extension_api::TurnStartAdmission;
use codex_feedback::CodexFeedback;
use codex_goal_extension::GoalService;
use codex_home::CodexHomeUserInstructionsProvider;
use codex_home::user_instructions_provider;
use codex_login::AuthManager;
use codex_otel::auth_storage::AuthStorageOriginator;
use codex_protocol::ThreadId;
Expand Down Expand Up @@ -354,9 +354,11 @@ impl MessageProcessor {
queue_service: queue_service.clone(),
turn_start_admission: Some(Arc::clone(&turn_start_admission)),
}),
Arc::new(CodexHomeUserInstructionsProvider::new(
user_instructions_provider(
config.codex_home.clone(),
)),
config.claude_compat_instructions,
config.project_doc_max_bytes,
),
Some(analytics_events_client.clone()),
codex_core::passthrough_image_store(),
Arc::clone(&thread_store),
Expand All @@ -366,11 +368,10 @@ impl MessageProcessor {
outgoing.clone(),
thread_state_manager.clone(),
)),
Some({
let time_provider =
app_server_time_provider(outgoing.clone(), thread_state_manager.clone());
time_provider
}),
Some(app_server_time_provider(
outgoing.clone(),
thread_state_manager.clone(),
)),
);
match code_mode_session_provider {
Some(provider) => manager.with_code_mode_session_provider(provider),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -766,13 +766,15 @@ fn map_hook_handler_to_api(handler: CoreHookHandlerConfig) -> ConfiguredHookHand
r#async,
status_message,
additional_context_limit,
fail_closed,
} => ConfiguredHookHandler::Command {
command,
command_windows,
timeout_sec,
r#async,
status_message,
additional_context_limit,
fail_closed,
},
CoreHookHandlerConfig::McpTool {
server,
Expand Down
1 change: 1 addition & 0 deletions codex-rs/app-server/tests/suite/v2/config_rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ statusMessage = "Scanning file"
r#async: false,
status_message: None,
additional_context_limit: Some(4_096),
fail_closed: false,
},
ConfiguredHookHandler::McpTool {
server: "security".to_string(),
Expand Down
1 change: 1 addition & 0 deletions codex-rs/app-server/tests/suite/v2/hooks_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ fn command_hook_hash(
r#async,
status_message: status_message.map(ToOwned::to_owned),
additional_context_limit,
fail_closed: false,
}],
},
};
Expand Down
8 changes: 5 additions & 3 deletions codex-rs/cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ use codex_core::config::resolve_profile_v2_config_path;
use codex_features::FEATURES;
use codex_features::Stage;
use codex_features::is_known_feature_key;
use codex_home::CodexHomeUserInstructionsProvider;
use codex_home::user_instructions_provider;
use codex_login::AuthManager;
use codex_login::is_workload_identity_selected;
use codex_memories_write::clear_memory_roots_contents;
Expand Down Expand Up @@ -2054,9 +2054,11 @@ async fn run_debug_prompt_input_command(
});
}

let user_instructions_provider = Arc::new(CodexHomeUserInstructionsProvider::new(
let user_instructions_provider = user_instructions_provider(
config.codex_home.clone(),
));
config.claude_compat_instructions,
config.project_doc_max_bytes,
);
let auth_manager =
AuthManager::shared_from_config(&config, /*enable_codex_api_key_env*/ false).await?;
let mut extensions = codex_extension_api::ExtensionRegistryBuilder::new();
Expand Down
4 changes: 3 additions & 1 deletion codex-rs/codex-home/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ workspace = true
[dependencies]
codex-extension-api = { workspace = true }
codex-utils-absolute-path = { workspace = true }
tokio = { workspace = true, features = ["fs"] }
dirs = { workspace = true }
sha2 = { workspace = true }
tokio = { workspace = true, features = ["fs", "macros", "rt"] }
tracing = { workspace = true }

[dev-dependencies]
Expand Down
Loading
Loading