Skip to content
Open
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
78 changes: 77 additions & 1 deletion apps/decodex/src/radar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,13 @@ const SOCIAL_POST_MODES: &[&str] = &[
const SOCIAL_POST_PRIORITIES: &[&str] = &["critical", "high", "low", "normal"];
const SOCIAL_POST_STATUSES: &[&str] = &["blocked", "failed", "published", "skipped"];
const SOCIAL_POST_WORTHINESS: &[&str] = &["block", "publish", "skip"];
const SOCIAL_POST_LIFECYCLE_STATES: &[&str] = &[
"deleted_by_operator",
"live",
"superseded_failed_attempt",
"superseded_published",
"superseded_text_only",
];
const SOURCE_ITEM_KINDS: &[&str] = &["commit", "pull_request"];
const UPSTREAM_IMPACT_KINDS: &[&str] =
&["browser_observation", "changelog", "commit", "pull_request", "release", "signal"];
Expand Down Expand Up @@ -5172,7 +5179,7 @@ fn validate_social_candidate(entry: &Map<String, Value>, errors: &mut Vec<String
validate_social_post_claims(entry.get("claims"), errors);
validate_social_candidate_decision(entry.get("decision"), errors);

for field in ["caveats", "next_steps"] {
for field in ["caveats", "media_refs", "next_steps"] {
validate_optional_string_list(entry.get(field), field, errors);
}
}
Expand Down Expand Up @@ -5236,6 +5243,7 @@ fn validate_social_post(entry: &Map<String, Value>, errors: &mut Vec<String>) {
validate_social_post_claims(entry.get("claims"), errors);
validate_social_post_decision(entry, errors);
validate_social_post_status_payload(entry, errors);
validate_social_post_lifecycle(entry, errors);

for field in ["caveats", "media_refs"] {
validate_optional_string_list(entry.get(field), field, errors);
Expand Down Expand Up @@ -5386,6 +5394,53 @@ fn validate_social_post_status_payload(entry: &Map<String, Value>, errors: &mut
}
}

fn validate_social_post_lifecycle(entry: &Map<String, Value>, errors: &mut Vec<String>) {
let Some(lifecycle) = entry.get("post_lifecycle") else {
return;
};
let Some(lifecycle) = lifecycle.as_object() else {
errors.push("post_lifecycle must be an object when present".into());

return;
};

if !matches_one_of(lifecycle.get("current_state"), SOCIAL_POST_LIFECYCLE_STATES) {
errors.push(format!(
"post_lifecycle.current_state must be one of {}",
choices(SOCIAL_POST_LIFECYCLE_STATES)
));
}
if lifecycle.get("quote_eligible").and_then(Value::as_bool).is_none() {
errors.push("post_lifecycle.quote_eligible must be boolean".into());
}
if !is_non_empty_string(lifecycle.get("reason")) {
errors.push("post_lifecycle.reason must be a non-empty string".into());
}
if lifecycle
.get("superseded_by_candidate")
.is_some_and(|value| !is_non_empty_string(Some(value)))
{
errors.push("post_lifecycle.superseded_by_candidate must be non-empty when present".into());
}

let current_state = string_field(lifecycle, "current_state");
let quote_eligible = lifecycle.get("quote_eligible").and_then(Value::as_bool);

if quote_eligible == Some(true)
&& (string_field(entry, "status") != Some("published") || current_state != Some("live"))
{
errors
.push("post_lifecycle.quote_eligible can be true only for live published posts".into());
}
if current_state.is_some_and(|state| state.starts_with("superseded"))
&& lifecycle.get("superseded_by_candidate").is_none()
{
errors.push(
"post_lifecycle.superseded_by_candidate is required for superseded states".into(),
);
}
}

fn validate_social_post_publication(publication: Option<&Value>, errors: &mut Vec<String>) {
let Some(publication) = publication.and_then(Value::as_object) else {
errors.push("publication is required when status is published".into());
Expand Down Expand Up @@ -5868,6 +5923,27 @@ mod tests {
assert_errors(&social_post, ["decision.daily_limit must be 8"]);
}

#[test]
fn accepts_deleted_social_post_lifecycle_and_rejects_quote_eligible_deleted_post() {
let mut social_post = valid_social_post();

social_post["post_lifecycle"] = serde_json::json!({
"current_state": "deleted_by_operator",
"quote_eligible": false,
"superseded_by_candidate": "artifacts/github/social-candidates/openai-codex-alpha4.json",
"reason": "The operator deleted this post and superseded it with a corrected candidate."
});

assert_errors(&social_post, []);

social_post["post_lifecycle"]["quote_eligible"] = serde_json::json!(true);

assert_errors(
&social_post,
["post_lifecycle.quote_eligible can be true only for live published posts"],
);
}

#[test]
fn default_github_token_falls_back_to_workflow_token() {
let _env = TestEnvVars::set(&[
Expand Down
55 changes: 55 additions & 0 deletions artifacts/github/social-candidates/openai-codex-app-26-602.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{
"schema": "social_candidate/v1",
"slug": "openai-codex-app-26-602",
"repo": "openai/codex",
"channel": "x",
"target_account": "decodexspace",
"mode": "release_pulse",
"priority": "high",
"audience": "Codex app users and Decodex operators",
"candidate_text": [
"Codex app 26.602 is out.\n\nWhat changed:\n- Profile activity insights and share cards\n- Better Computer Use startup and appshot errors\n- Browser/review UI fixes plus expanded onboarding\n\nChangelog: https://developers.openai.com/codex/changelog"
],
"source_refs": {
"urls": [
"https://developers.openai.com/codex/changelog"
]
},
"evidence_notes": [
"The official Codex changelog lists Codex app updates 26.602 on 2026-06-04.",
"The changelog says activity insights and share cards were added to the Profile section, with sharing available on consumer ChatGPT plans.",
"The changelog says Computer Use startup readiness and appshot error reporting improved.",
"The changelog says browser and review UI issues were fixed and onboarding was expanded with more role choices."
],
"claims": [
{
"text": "Codex app 26.602 includes Profile activity insights and share cards.",
"evidence": "https://developers.openai.com/codex/changelog",
"confidence": "confirmed"
},
{
"text": "Codex app 26.602 improves Computer Use startup readiness and appshot error reporting.",
"evidence": "https://developers.openai.com/codex/changelog",
"confidence": "confirmed"
},
{
"text": "Codex app 26.602 includes browser and review UI fixes plus expanded onboarding role choices.",
"evidence": "https://developers.openai.com/codex/changelog",
"confidence": "confirmed"
}
],
"decision": {
"worthiness": "publish",
"reason": "The official changelog has concrete user-visible app changes and enough reader value for a source-led release pulse.",
"idempotency_key": "x:decodexspace:codex-app-26-602:release_pulse"
},
"caveats": [
"Sharing for profile cards is described as available on consumer ChatGPT plans."
],
"media_refs": [
"/Users/x/.codex/decodex/social-media/codex-app-26-602.png"
],
"next_steps": [
"Hand this candidate to decodex-x-publisher for a source-led Codex app update post using the visually reviewed decodex_signal_card media at /Users/x/.codex/decodex/social-media/codex-app-26-602.png."
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
{
"schema": "social_candidate/v1",
"slug": "openai-codex-rust-v0-138-0-alpha-4-prerelease-delta",
"repo": "openai/codex",
"channel": "x",
"target_account": "decodexspace",
"mode": "thread",
"priority": "high",
"audience": "Codex CLI alpha testers and Decodex Radar readers",
"candidate_text": [
"Codex CLI 0.138.0-alpha.4 prerelease read\n\nIncrement: alpha.3 -> alpha.4, 8 commits.\n\nMain theme: reasoning-effort and app-server protocol cleanup.\n\nAlpha caveat: metadata read, not stable release notes.",
"Protocol/API changes:\n\n- reasoning efforts become model-defined\nhttps://github.com/openai/codex/pull/26444\n\n- model-advertised order is preserved\nhttps://github.com/openai/codex/pull/26446",
"Cleanup to watch:\n\n- response.processed websocket request removed\nhttps://github.com/openai/codex/pull/26447\n\n- experimentalFeature/enablement/set trims stale keys\nhttps://github.com/openai/codex/pull/26312",
"Why this prerelease matters:\n\nThis is not a broad feature-drop read. It is a tight alpha delta around protocol shape, model metadata, and client compatibility.\n\nCompare:\nhttps://github.com/openai/codex/compare/rust-v0.138.0-alpha.3...rust-v0.138.0-alpha.4"
],
"source_refs": {
"release_deltas": [
"site/src/content/release-deltas/openai-codex-latest.json"
],
"urls": [
"https://github.com/openai/codex/releases/tag/rust-v0.138.0-alpha.3",
"https://github.com/openai/codex/releases/tag/rust-v0.138.0-alpha.4",
"https://github.com/openai/codex/compare/rust-v0.138.0-alpha.3...rust-v0.138.0-alpha.4",
"https://github.com/openai/codex/pull/26444",
"https://github.com/openai/codex/pull/26446",
"https://github.com/openai/codex/pull/26447",
"https://github.com/openai/codex/pull/26312"
]
},
"evidence_notes": [
"GitHub release metadata shows rust-v0.138.0-alpha.4 was published as a prerelease on 2026-06-04T22:01:10Z.",
"The prerelease channel baseline is the adjacent previous prerelease rust-v0.138.0-alpha.3, not stable rust-v0.137.0.",
"GitHub compare metadata for rust-v0.138.0-alpha.3...rust-v0.138.0-alpha.4 showed 8 ahead commits.",
"This is not the first prerelease after a stable release, but local social_post/v1 records did not contain a previous alpha.3 Decodex prerelease post URL to quote.",
"The missing alpha.3 quote target is a historical Publisher coverage gap; the candidate must not invent a quote URL.",
"The alpha.3 -> alpha.4 commit titles cluster around model-defined reasoning efforts, model-advertised effort order, response.processed websocket removal, experimentalFeature/enablement/set cleanup, AGENTS.md environment filesystem routing, Windows sandbox lint, and release binary ThinLTO.",
"Public copy uses direct GitHub PR URLs on first mention instead of raw PR-number-only references.",
"A fresh candidate-specific Decodex signal card was generated and visually reviewed at /Users/x/.codex/decodex/social-media/codex-cli-0-138-alpha3-alpha4.svg.png."
],
"claims": [
{
"text": "rust-v0.138.0-alpha.4 is the latest observed openai/codex prerelease checkpoint.",
"evidence": "https://github.com/openai/codex/releases/tag/rust-v0.138.0-alpha.4",
"confidence": "confirmed"
},
{
"text": "The prerelease-channel comparison for alpha.4 is rust-v0.138.0-alpha.3 to rust-v0.138.0-alpha.4.",
"evidence": "https://github.com/openai/codex/compare/rust-v0.138.0-alpha.3...rust-v0.138.0-alpha.4",
"confidence": "confirmed"
},
{
"text": "The alpha.3 to alpha.4 compare window has 8 ahead commits.",
"evidence": "https://github.com/openai/codex/compare/rust-v0.138.0-alpha.3...rust-v0.138.0-alpha.4",
"confidence": "confirmed"
},
{
"text": "The alpha.3 to alpha.4 compare window includes model-defined reasoning effort and model-advertised effort ordering work.",
"evidence": "https://github.com/openai/codex/pull/26444 and https://github.com/openai/codex/pull/26446",
"confidence": "confirmed"
},
{
"text": "The alpha.3 to alpha.4 compare window removes response.processed websocket request handling and trims stale experimentalFeature/enablement/set keys.",
"evidence": "https://github.com/openai/codex/pull/26447 and https://github.com/openai/codex/pull/26312",
"confidence": "confirmed"
}
],
"decision": {
"worthiness": "publish",
"reason": "The adjacent alpha.3 -> alpha.4 compare supports a useful, formatted prerelease delta read with clickable PR URLs and explicit channel-lineage caveats.",
"idempotency_key": "x:decodexspace:openai-codex-rust-v0-138-0-alpha-4:alpha3-alpha4:thread"
},
"caveats": [
"Do not compare this prerelease post against stable rust-v0.137.0; that belongs to the homepage release-delta index or first-prerelease baseline only.",
"Do not quote a previous alpha.3 post unless a real previous Decodex post URL is found.",
"Upgrade or testing guidance should point readers to the exact prerelease tag until source review catches up."
],
"media_refs": [
"/Users/x/.codex/decodex/social-media/codex-cli-0-138-alpha3-alpha4.svg.png"
],
"next_steps": [
"Publish as a prerelease-channel alpha.3 -> alpha.4 delta read with /Users/x/.codex/decodex/social-media/codex-cli-0-138-alpha3-alpha4.svg.png after media upload reliability is fixed or an explicit text-only fallback is approved.",
"For the next prerelease in this train, compare alpha.4 -> alpha.5 and quote the previous alpha.4 prerelease post URL when one exists."
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
{
"schema": "social_post/v1",
"slug": "openai-codex-rust-v0-138-0-alpha-4-structured-read-text-only",
"channel": "x",
"target_account": "decodexspace",
"controller_account": "hackink",
"mode": "thread",
"status": "published",
"audience": "Codex CLI alpha testers and Decodex Radar readers",
"text": [
"Codex CLI 0.138.0-alpha.4 prerelease read\n\nWorth watching:\n- #25469 adds app-server accountSession/* protocol\n- #25638 adds /app handoff from CLI to Codex Desktop\n\nAlpha caveat: metadata read, not stable release notes.",
"Protocol/API changes in the alpha window:\n- #26444 supports model-defined reasoning efforts\n- #26446 preserves model-advertised effort order\n- #26447 removes response.processed websocket request\n- #26312 trims stale experimentalFeature keys",
"Developer/operator surface:\n- #26272 loads plugin hooks without every plugin capability\n- #26417 adds marketplaceSource to plugin list JSON\n- #25944 exposes local image paths to models\n- #25947 adds saved-image path hints for standalone imagegen",
"Why track prereleases?\n\nStable notes say what shipped. Alpha metadata shows where Codex is moving: Desktop handoff, app-server protocol, model metadata, plugins, and image flows.\n\nCompare:\nhttps://github.com/openai/codex/compare/rust-v0.137.0...rust-v0.138.0-alpha.4"
],
"source_refs": {
"urls": [
"https://github.com/openai/codex/releases/tag/rust-v0.138.0-alpha.4",
"https://github.com/openai/codex/compare/rust-v0.137.0...rust-v0.138.0-alpha.4",
"https://github.com/openai/codex/pull/25469",
"https://github.com/openai/codex/pull/25638",
"https://github.com/openai/codex/pull/26444",
"https://github.com/openai/codex/pull/26446",
"https://github.com/openai/codex/pull/26447",
"https://github.com/openai/codex/pull/26312",
"https://github.com/openai/codex/pull/26272",
"https://github.com/openai/codex/pull/26417",
"https://github.com/openai/codex/pull/25944",
"https://github.com/openai/codex/pull/25947",
"https://x.com/decodexspace/status/2062727471146152223",
"https://x.com/decodexspace/status/2062727473121616190",
"https://x.com/decodexspace/status/2062727474933547147",
"https://x.com/decodexspace/status/2062727476753895425"
]
},
"evidence_notes": [
"GitHub release metadata was rechecked immediately before posting and rust-v0.138.0-alpha.4 remained the latest observed openai/codex prerelease.",
"The thread was revised after operator feedback to focus on important PR/commit clusters, anticipated workflow changes, protocol/API changes, and scan-friendly line breaks.",
"Chrome account readback showed @decodexspace before composing.",
"The X composer accepted all four thread posts and exposed Post all before submission.",
"X returned Your posts were sent after Post all.",
"Timeline readback showed the four newly published Decodex posts with matching text and four status URLs.",
"No media was attached because the previous X file upload path stalled the Chrome extension control channel; this run intentionally avoided file upload to validate text and layout only.",
"The operator later deleted this text-only thread after pointing out missing media, channel-lineage, and raw PR-number issues.",
"Follow-up review found this thread mixed stable-to-prerelease comparison into a prerelease-channel post; it is retained as audit evidence only and must not be used as a previous prerelease quote target."
],
"claims": [
{
"text": "rust-v0.138.0-alpha.4 is the latest observed openai/codex prerelease checkpoint.",
"evidence": "https://github.com/openai/codex/releases/tag/rust-v0.138.0-alpha.4",
"confidence": "confirmed"
},
{
"text": "The rust-v0.137.0 to rust-v0.138.0-alpha.4 compare window has 45 ahead commits.",
"evidence": "https://github.com/openai/codex/compare/rust-v0.137.0...rust-v0.138.0-alpha.4",
"confidence": "confirmed"
},
{
"text": "The compare window includes app-server accountSession/* protocol work and /app Desktop handoff work.",
"evidence": "https://github.com/openai/codex/pull/25469 and https://github.com/openai/codex/pull/25638",
"confidence": "confirmed"
},
{
"text": "The compare window includes protocol/API changes around reasoning efforts, websocket request shape, and experimentalFeature keys.",
"evidence": "https://github.com/openai/codex/pull/26444, https://github.com/openai/codex/pull/26446, https://github.com/openai/codex/pull/26447, and https://github.com/openai/codex/pull/26312",
"confidence": "confirmed"
}
],
"decision": {
"worthiness": "publish",
"priority": "high",
"idempotency_key": "x:decodexspace:openai-codex-rust-v0-138-0-alpha-4:structured-thread:text-only",
"reason": "Operator deleted the rough prerelease thread and requested a fresh run to inspect the improved structure; media upload remained unreliable, so this publication intentionally validated text structure only.",
"daily_limit": 8,
"daily_count_before": 3,
"daily_count_after": 7,
"day": "2026-06-05",
"timezone": "Asia/Shanghai"
},
"publication": {
"posted_at": "2026-06-05T02:46:39Z",
"published_urls": [
"https://x.com/decodexspace/status/2062727471146152223",
"https://x.com/decodexspace/status/2062727473121616190",
"https://x.com/decodexspace/status/2062727474933547147",
"https://x.com/decodexspace/status/2062727476753895425"
],
"publisher": "chrome",
"account_verified": true,
"made_with_ai": false
},
"post_lifecycle": {
"current_state": "deleted_by_operator",
"quote_eligible": false,
"superseded_by_candidate": "artifacts/github/social-candidates/openai-codex-rust-v0-138-0-alpha-4-prerelease-delta.json",
"reason": "The text-only thread was deleted by the operator and superseded by the adjacent alpha.3 to alpha.4 prerelease candidate with media expectation and clickable PR URLs."
},
"caveats": [
"No media was attached to this published thread.",
"A candidate-specific image exists locally at /Users/x/.codex/decodex/social-media/codex-cli-0-138-alpha-4-structured.svg.png, but X upload was intentionally avoided because it previously stalled the Chrome control channel.",
"This thread is a prerelease metadata read, not stable-release guidance.",
"This record is not a live quote target for future prerelease-chain posts."
]
}
Loading