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
396 changes: 391 additions & 5 deletions apps/decodex/src/radar.rs

Large diffs are not rendered by default.

97 changes: 97 additions & 0 deletions artifacts/social/x/posts/2026-06-06/openai-codex-app-26-602.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
{
"schema": "social_post/v1",
"slug": "openai-codex-app-26-602",
"channel": "x",
"target_account": "decodexspace",
"controller_account": "hackink",
"mode": "release_pulse",
"status": "published",
"audience": "Codex app users and Decodex operators",
"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": {
"social_candidates": [
"artifacts/github/social-candidates/openai-codex-app-26-602.json"
],
"urls": [
"https://developers.openai.com/codex/changelog",
"https://x.com/decodexspace/status/2063109099135574379",
"https://x.com/decodexspace/status/2063109099135574379/photo/1",
"https://x.com/decodexspace/status/2063198209271554481"
]
},
"evidence_notes": [
"The social_candidate/v1 artifact has decision.worthiness = publish and mode release_pulse.",
"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.",
"A post-publication profile timeline audit found an earlier matching @decodexspace post at https://x.com/decodexspace/status/2063109099135574379 with /photo/1 media; this later post is a duplicate publication.",
"Asia/Shanghai cap accounting initially found zero checked-in @decodexspace published records for 2026-06-06, but live profile readback proves the correct pre-publish count was at least one.",
"X duplicate detection for from:decodexspace with the exact Codex app 26.602 phrase returned no results before composing.",
"A post-publication X search repeated the exact phrase and still returned no results even while the profile timeline exposed both matching posts, so X search no-results is not reliable duplicate evidence.",
"Chrome account readback showed Decodex @decodexspace before composing.",
"The final permalink readback confirmed @decodexspace, @hackink automation attribution, the expected post text, and the official changelog link card."
],
"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"
},
{
"text": "The published X post is live on @decodexspace and includes the official changelog link card.",
"evidence": "https://x.com/decodexspace/status/2063198209271554481",
"confidence": "confirmed"
},
{
"text": "The published X post duplicates an earlier live @decodexspace Codex app 26.602 post.",
"evidence": "https://x.com/decodexspace/status/2063109099135574379",
"confidence": "confirmed"
}
],
"decision": {
"worthiness": "publish",
"priority": "high",
"idempotency_key": "x:decodexspace:codex-app-26-602:release_pulse",
"reason": "The official changelog has concrete user-visible app changes and enough reader value for a source-led release pulse.",
"daily_limit": 8,
"daily_count_before": 1,
"daily_count_after": 2,
"day": "2026-06-06",
"timezone": "Asia/Shanghai"
},
"publication": {
"posted_at": "2026-06-06T09:56:00Z",
"published_urls": [
"https://x.com/decodexspace/status/2063198209271554481"
],
"publisher": "chrome",
"account_verified": true,
"made_with_ai": false
},
"caveats": [
"This post is a duplicate of the earlier media-attached live post at https://x.com/decodexspace/status/2063109099135574379.",
"Do not treat X search no-results as sufficient duplicate-detection evidence for future publication decisions.",
"The candidate's declared generated-media path /Users/x/.codex/decodex/social-media/codex-app-26-602.png was missing at publication time.",
"The post was intentionally published text-only because the official changelog link card and compact release bullets carried the reader value without a generic replacement image.",
"Sharing for profile cards is described as available on consumer ChatGPT plans."
],
"post_lifecycle": {
"current_state": "superseded_published",
"quote_eligible": false,
"superseded_by_candidate": "https://x.com/decodexspace/status/2063109099135574379",
"reason": "Post-publication profile readback found an earlier matching live Codex app 26.602 post; this later text-only post is a duplicate and must not be reused as future publication evidence."
}
}
8 changes: 8 additions & 0 deletions dev/skills/references/social-release-publisher-gates.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,14 @@ For publishable prerelease candidates, evidence must name:
depends on AI-rendered readable text.
- Generated images live in `$CODEX_HOME/decodex/social-media/` or temporary storage by
default, not Git.
- Before composing any release, app, or prerelease post, check durable records, pending
publication PRs when available, active `social_publish_reservation/v1` records, and
the live `@decodexspace` profile/timeline for the exact lead text, release tag,
source URL, and prior status URLs. X search `No results` is not a duplicate-clear
signal by itself.
- Do not open X compose until an active `social_publish_reservation/v1` for the same
idempotency key and duplicate keys is committed and PR-visible. Repeat live
profile/timeline duplicate readback immediately before clicking Post.
- If account verification, duplicate detection, media upload, or final readback is
unreliable, fail closed. Do not downgrade to text-only unless the operator explicitly
approves that fallback for the current candidate.
Expand Down
21 changes: 21 additions & 0 deletions dev/skills/x-post-publisher/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,26 @@ Publish only when all are true:
- prerelease channel lineage and previous-post quote state were checked through
`post_lifecycle.quote_eligible`
- idempotency key has not already been published or blocked for the same source
- checked-in records, active `social_publish_reservation/v1` records, open
publication PRs when available, and live `@decodexspace` profile/timeline readback
show no matching post for the candidate's exact lead text, idempotency subject,
release tag, or source URL
- an active `social_publish_reservation/v1` with the same idempotency key and
duplicate keys has been committed and pushed so it is PR-visible before X compose
- daily cap of 8 posts for `@decodexspace` in `Asia/Shanghai` is not reached

For release/prerelease/app candidates, apply
`../references/social-release-publisher-gates.md` before composing.

Do not treat X search `No results` as sufficient duplicate evidence. Use search only
as a supporting signal; if profile/timeline readback is unavailable, stale,
loading-only, or contradicts search, fail closed before composing.

Do not compose from a local-only, uncommitted, or unpushed reservation. After the
reservation is PR-visible and immediately before clicking Post, repeat live
profile/timeline duplicate readback. If a duplicate appears, cancel or expire the
reservation and do not publish.

## Chrome And Media

Use `@Chrome` only inside this low-frequency Publisher workflow. Before composing,
Expand Down Expand Up @@ -76,6 +91,12 @@ Write `artifacts/social/x/posts/<yyyy-mm-dd>/<slug>.json` with:
details as applicable
- X status/media URL or media caveat when media was used or skipped
- `post_lifecycle` when the record can affect future prerelease quote chains
- the consumed reservation path under `source_refs.reservations` when the compose gate
was reached

Update `artifacts/social/x/reservations/<yyyy-mm-dd>/<slug>.json` from `active` to
`consumed`, `canceled`, or `expired` before the run ends. Do not leave an active
reservation behind unless the thread is explicitly handed off to a human operator.

Run:

Expand Down
11 changes: 8 additions & 3 deletions dev/skills/x-post-quality-system/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,13 @@ Required shared elements:
source-agnostic placeholders

Before upload, verify the image is specific, visually consistent, readable as an X
preview, and not generic or off-brand. Record prompt/media path/quality outcome in
`social_post/v1` evidence notes or caveats when useful.
preview, and not generic or off-brand. Also verify the candidate is not already live
on the `@decodexspace` profile/timeline and does not conflict with any active
`social_publish_reservation/v1` in checked records or open publication PRs. X search
`No results` is not enough.
Record prompt/media path/quality outcome in `social_post/v1` evidence notes or caveats
when useful.

Fail closed when media is generic, reused, unavailable but required, or when duplicate
detection, account verification, upload, or final readback is unreliable.
detection, reservation visibility, account verification, upload, or final readback is
unreliable.
30 changes: 29 additions & 1 deletion docs/runbook/social-publishing-workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ Outputs:
- An optional `upstream_impact/v1` artifact under `artifacts/github/impact/`.
- A `social_candidate/v1` record under `artifacts/github/social-candidates/` when
analysis needs a durable Publisher handoff or pre-publication decision.
- A `social_publish_reservation/v1` record under
`artifacts/social/x/reservations/<yyyy-mm-dd>/` before any X compose step.
- A `social_post/v1` record under `artifacts/social/x/posts/<yyyy-mm-dd>/`.
- Optional local generated media under `$CODEX_HOME/decodex/social-media/`; generated
media files are not committed by default.
Expand Down Expand Up @@ -143,10 +145,30 @@ one exact compare URL intentionally carries the detailed PR list.
5. Check idempotency and daily cap.
- Build a stable idempotency key from account, source, mode, and release checkpoint
when applicable.
- Count already-published `@decodexspace` records for the cap day.
- Count already-published `@decodexspace` records for the cap day from checked-in
`social_post/v1` records, and inspect open PRs that add `social_post/v1` records
when the current worktree may not include every pending publication record.
- Check active `social_publish_reservation/v1` records in the current tree and open
publication PRs. If another active reservation has the same idempotency key,
source URL, exact lead text, release tag, or candidate slug, fail closed instead
of composing.
- Run live duplicate detection against the `@decodexspace` profile/timeline before
composing. Match the candidate's exact lead text, idempotency subject, release
tag, source URL, and known prior status URLs.
- X search can be an additional signal, but `No results` is not sufficient proof
that no duplicate exists. If X search and profile/timeline readback disagree, or
either surface is loading-only or unreadable, fail closed.
- The default cap day uses `Asia/Shanghai`.
- If the candidate would exceed 8 posts, do not post. Write
`status = "blocked"` with `block.reason = "daily_cap_exceeded"`.
- Before opening the X composer, create an active
`social_publish_reservation/v1` record with the idempotency key, duplicate keys,
owner/run metadata, `reserved_at`, and `expires_at`.
- Commit and push the reservation, or update the publication PR so the reservation
is PR-visible. A local-only reservation does not authorize publication.
- After the reservation is visible and immediately before clicking Post, repeat the
live profile/timeline duplicate readback. If a duplicate appears, cancel or expire
the reservation and do not publish.

6. Prepare media only when useful.
- Use the `decodex_signal_card` image template in
Expand All @@ -172,11 +194,17 @@ one exact compare URL intentionally carries the detailed PR list.
- Use `schema = "social_post/v1"`.
- Use `target_account = "decodexspace"` and `controller_account = "hackink"`.
- Set `status = "published"`, `blocked`, `failed`, or `skipped`.
- Include the consumed reservation under `source_refs.reservations` when the run
reached the compose gate.
- Preserve source refs, evidence notes, claims, decision data, and publication URLs
when available.
- For media, preserve the X status URL and any `/photo/N` readback URL. Do not add a
generated image file to Git unless the operator explicitly asks for a permanent
sample.
- Update the reservation to `consumed` with `consumed_by_social_post` after a
published, blocked, or otherwise terminal audited result. Use `canceled` or
`expired` with `release_reason` when publication stops before a durable terminal
post record is useful.

9. Validate.
- Run:
Expand Down
61 changes: 60 additions & 1 deletion docs/spec/social-publishing.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Not this document:

Defines:
- The `social_post/v1` artifact shape.
- The `social_publish_reservation/v1` pre-compose reservation shape.
- Allowed post modes for Decodex Publisher.
- The automated Chrome publishing boundary.
- The daily cap and blocked-publication ledger rule.
Expand All @@ -29,15 +30,21 @@ Defines:
The canonical schema identifier is:

- `social_post/v1`
- `social_publish_reservation/v1`

Recommended checked-in locations:

- `artifacts/social/x/posts/<yyyy-mm-dd>/<slug>.json`
- `artifacts/social/x/reservations/<yyyy-mm-dd>/<slug>.json`

`social_post/v1` is a publication record, not a review-only draft or pre-publication
candidate. Use `social_candidate/v1` for handoff decisions before Publisher evaluates
account state, idempotency, daily cap, media, and final publication.

`social_publish_reservation/v1` is a pre-compose lease. Publisher automation must
create a checked, PR-visible active reservation before opening X compose. A local-only,
uncommitted, or unpushed reservation does not authorize publication.

Generated media files are not default Git artifacts. Store successful publication
facts in Git as small JSON records. Store generated image files in a local persistent
media cache, or discard them after upload, unless an operator explicitly asks to commit
Expand All @@ -56,7 +63,7 @@ an exact sample.
| `status` | string | `published`, `blocked`, `failed`, or `skipped`. |
| `audience` | string | Primary reader group. |
| `text` | array | One or more English post bodies, one array item per thread post. |
| `source_refs` | object | Links to signal, upstream-impact, upstream-review, release, PR, or changelog evidence. |
| `source_refs` | object | Links to reservation, candidate, signal, upstream-impact, upstream-review, release, PR, or changelog evidence. |
| `evidence_notes` | array | Non-empty list of evidence-backed notes that justify the post or skip decision. |
| `claims` | array | Non-empty list of user-facing claims with evidence references. |
| `decision` | object | AI worthiness, priority, idempotency key, daily counter, and cap decision. |
Expand Down Expand Up @@ -153,6 +160,44 @@ The daily cap is hard. Automation must not publish the ninth `@decodexspace` pos
the same cap day. Instead it must write a `status = "blocked"` record with
`block.reason = "daily_cap_exceeded"`.

## Publication Reservation Object

`social_publish_reservation/v1` prevents two Publisher runs from composing the same
post when durable `social_post/v1` records have not been merged yet.

Required fields:

| Field | Type | Notes |
| --- | --- | --- |
| `schema` | string | Must be `social_publish_reservation/v1`. |
| `slug` | string | Stable URL-safe identifier for the candidate. |
| `channel` | string | Must be `x`. |
| `target_account` | string | Must be `decodexspace`. |
| `controller_account` | string | Must be `hackink`. |
| `mode` | string | One value from the post-mode table. |
| `status` | string | `active`, `consumed`, `canceled`, or `expired`. |
| `idempotency_key` | string | Same stable key that the terminal `social_post/v1` record will use. |
| `reserved_at` | string | UTC RFC3339 timestamp. |
| `expires_at` | string | UTC RFC3339 timestamp for stale-reservation cleanup. |
| `day` | string | Calendar day used for cap accounting, formatted `YYYY-MM-DD`. |
| `timezone` | string | Default is `Asia/Shanghai`. |
| `candidate_refs` | object | Links to `social_candidate/v1` artifacts or source URLs that authorize the reservation. |
| `duplicate_keys` | array | Non-empty strings to check in durable records, open PRs, and live profile readback. |

Optional fields:

- `owner`: automation id, run id, branch, and PR URL that own the active reservation.
- `evidence_notes`: notes about duplicate checks and account/profile readback at
reservation time.
- `consumed_by_social_post`: required when `status = "consumed"`.
- `release_reason`: required when `status = "canceled"` or `status = "expired"`.

Validation rule: `decodex radar validate` rejects duplicate active reservation
idempotency keys and rejects an active reservation whose key already has a terminal
`published` or `blocked` `social_post/v1` record. Failed or skipped publication
attempts do not reserve the key permanently; a retry must create a fresh active
reservation and consume, cancel, or expire it at the end of the run.

## Blocked Cap Records

When a candidate is blocked by the daily cap, the record must preserve the review
Expand Down Expand Up @@ -187,6 +232,20 @@ described here. It must use the logged-in `@decodexspace` account, verify the ac
before composing, and fail closed when Chrome, login state, X page structure, duplicate
detection, or media upload is unreliable.

Duplicate detection must not rely on X search alone. Before composing, Publisher
automation must check durable `social_post/v1` records, active
`social_publish_reservation/v1` records in the checked tree and open publication PRs
when available, and a live `@decodexspace` profile/timeline readback for the
candidate's exact lead text, idempotency subject, release tag, or source URL. Treat an
X search `No results` state as weak evidence only; if profile or permalink readback is
unavailable, stale, loading-only, or contradicts search, fail closed instead of
publishing.

After the active reservation is PR-visible and immediately before clicking Post,
Publisher automation must repeat live profile/timeline readback. If a duplicate appears
at that final gate, cancel or expire the reservation and write a non-published
`social_post/v1` record only when it adds audit or idempotency value.

Chrome tabs are temporary execution resources. Publisher automation must close or
release research, compose, upload, and readback tabs after the `social_post/v1` record
captures the result. A tab may stay open only as an explicit human handoff, such as
Expand Down
2 changes: 2 additions & 0 deletions scripts/github/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ Current checked contracts:
- `release_delta/v1` artifacts are validated by `decodex radar validate`.
- `upstream_impact/v1` artifacts are validated by `decodex radar validate`.
- `social_candidate/v1` artifacts are validated by `decodex radar validate`.
- `social_publish_reservation/v1` artifacts are validated by
`decodex radar validate`.
- `social_post/v1` artifacts are validated by `decodex radar validate`.

Contract ownership:
Expand Down
Loading