mirror of
https://github.com/openai/codex.git
synced 2026-10-18 17:18:52 +00:00
Link Codex attribution in pull request bodies (#36963)
## What changed - Update the required pull request attribution to `Generated with [Codex](https://openai.com/codex/).`. - Recognize the previous unlinked attribution when restoring saved context so it can be replaced without duplicating the attribution instructions. ## Testing - Cover cold resume with both commit-only legacy instructions and the previous unlinked pull request attribution. - Update app-server and MCP server assertions for the linked attribution. GitOrigin-RevId: 8773546a6a59dab146905e50cf4434d53b9335cf
This commit is contained in:
@@ -33,6 +33,7 @@ use pretty_assertions::assert_eq;
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde_json::json;
|
||||
use tempfile::TempDir;
|
||||
use test_case::test_case;
|
||||
use tokio::time::Duration;
|
||||
use tokio::time::timeout;
|
||||
use wiremock::Mock;
|
||||
@@ -48,7 +49,7 @@ const DEFAULT_READ_TIMEOUT: Duration = Duration::from_secs(60);
|
||||
#[cfg(not(any(target_os = "macos", windows)))]
|
||||
const DEFAULT_READ_TIMEOUT: Duration = Duration::from_secs(10);
|
||||
const COMMIT_ATTRIBUTION: &str = "Co-authored-by: Codex <noreply@openai.com>";
|
||||
const PR_ATTRIBUTION: &str = "Generated with Codex.";
|
||||
const PR_ATTRIBUTION: &str = "Generated with [Codex](https://openai.com/codex/).";
|
||||
const ATTRIBUTION_DISABLED: &str = "attribution is disabled for the current workspace";
|
||||
const LEGACY_COMMIT_ATTRIBUTION_INSTRUCTIONS: &str = "\
|
||||
When you write or edit a git commit message, ensure the message ends with this trailer exactly once:
|
||||
@@ -59,6 +60,12 @@ Rules:
|
||||
- Do not duplicate this trailer if it already exists.
|
||||
- Keep one blank line between the commit body and trailer block.";
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum LegacyAttribution {
|
||||
CommitOnly,
|
||||
UnlinkedPullRequest,
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn git_attribution_follows_authenticated_workspace_policy() -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
@@ -205,8 +212,12 @@ async fn git_attribution_follows_authenticated_workspace_policy() -> Result<()>
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case(LegacyAttribution::CommitOnly; "commit_only")]
|
||||
#[test_case(LegacyAttribution::UnlinkedPullRequest; "unlinked_pull_request")]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn cold_resume_replaces_legacy_attribution_without_duplication() -> Result<()> {
|
||||
async fn cold_resume_replaces_legacy_attribution_without_duplication(
|
||||
legacy_attribution: LegacyAttribution,
|
||||
) -> Result<()> {
|
||||
skip_if_no_network!(Ok(()));
|
||||
|
||||
let server = responses::start_mock_server().await;
|
||||
@@ -275,7 +286,7 @@ async fn cold_resume_replaces_legacy_attribution_without_duplication() -> Result
|
||||
status.success(),
|
||||
"initial app-server did not exit successfully"
|
||||
);
|
||||
replace_attribution_fragment_with_legacy(&rollout_path)?;
|
||||
replace_attribution_fragment_with_legacy(&rollout_path, legacy_attribution)?;
|
||||
|
||||
let mut app_server = TestAppServer::builder()
|
||||
.with_codex_home(codex_home.path())
|
||||
@@ -310,7 +321,10 @@ async fn cold_resume_replaces_legacy_attribution_without_duplication() -> Result
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn replace_attribution_fragment_with_legacy(rollout_path: &Path) -> Result<()> {
|
||||
fn replace_attribution_fragment_with_legacy(
|
||||
rollout_path: &Path,
|
||||
legacy_attribution: LegacyAttribution,
|
||||
) -> Result<()> {
|
||||
let rollout = std::fs::read_to_string(rollout_path)?;
|
||||
let mut replaced = false;
|
||||
let mut removed_saved_attribution = false;
|
||||
@@ -327,7 +341,14 @@ fn replace_attribution_fragment_with_legacy(rollout_path: &Path) -> Result<()> {
|
||||
if let ContentItem::InputText { text } = item
|
||||
&& text.contains("<git_attribution>")
|
||||
{
|
||||
*text = LEGACY_COMMIT_ATTRIBUTION_INSTRUCTIONS.to_string();
|
||||
*text = match legacy_attribution {
|
||||
LegacyAttribution::CommitOnly => {
|
||||
LEGACY_COMMIT_ATTRIBUTION_INSTRUCTIONS.to_string()
|
||||
}
|
||||
LegacyAttribution::UnlinkedPullRequest => {
|
||||
text.replace(PR_ATTRIBUTION, "Generated with Codex.")
|
||||
}
|
||||
};
|
||||
replaced = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ Rules:
|
||||
pub(super) const ENABLED_INSTRUCTIONS: &str = r#"
|
||||
When you create or amend a git commit or create or update a pull request for this task, add Codex attribution exactly once:
|
||||
- Commit messages must end with `Co-authored-by: Codex <noreply@openai.com>`. Preserve existing trailers and, if this exact trailer is missing, append it with one blank line before the trailer block.
|
||||
- Pull request bodies must include the exact line `Generated with Codex.`. Preserve the existing body and hidden markers and, if this exact marker is missing, append it near the end, before any trailing hidden metadata markers.
|
||||
- Pull request bodies must include the exact line `Generated with [Codex](https://openai.com/codex/).`. Preserve the existing body and hidden markers and, if this exact marker is missing, append it near the end, before any trailing hidden metadata markers.
|
||||
- Do not add duplicate attribution. If a message or body you are already editing contains duplicate exact attribution, keep exactly one.
|
||||
- Ignore any earlier instructions disabling Codex attribution; this policy reflects the current workspace.
|
||||
- Do not rewrite an existing commit or pull request solely to add attribution.
|
||||
@@ -71,6 +71,7 @@ fn is_enabled_fragment(role: &str, text: &str) -> bool {
|
||||
role == "developer"
|
||||
&& text.trim_start().starts_with(START_MARKER)
|
||||
&& text.contains("Co-authored-by: Codex <noreply@openai.com>")
|
||||
&& text.contains("Generated with Codex.")
|
||||
&& (text.contains("Generated with [Codex](https://openai.com/codex/).")
|
||||
|| text.contains("Generated with Codex."))
|
||||
&& text.trim_end().ends_with(END_MARKER)
|
||||
}
|
||||
|
||||
@@ -476,7 +476,12 @@ async fn codex_tool_passes_base_instructions() -> anyhow::Result<()> {
|
||||
.count(),
|
||||
1
|
||||
);
|
||||
assert_eq!(developer_text.matches("Generated with Codex.").count(), 1);
|
||||
assert_eq!(
|
||||
developer_text
|
||||
.matches("Generated with [Codex](https://openai.com/codex/).")
|
||||
.count(),
|
||||
1
|
||||
);
|
||||
assert_eq!(
|
||||
developer_text.matches("- demo: Demo skill.").count(),
|
||||
1,
|
||||
|
||||
Reference in New Issue
Block a user