Skip to content

feat(codex): show the commit-attribution prompt layer - #2692

Merged
lidge-jun merged 4 commits into
devfrom
codex/git-attribution-layer
Aug 26, 2026
Merged

feat(codex): show the commit-attribution prompt layer#2692
lidge-jun merged 4 commits into
devfrom
codex/git-attribution-layer

Conversation

@lidge-jun

Copy link
Copy Markdown
Owner

Summary

Stacked on #2691. The prompt panel was under-reporting the prompt by a whole
developer section.

ext/git-attribution contributes one — the instruction to add a
Co-authored-by: Codex trailer to commits the model writes and a
Generated with Codex. line to pull requests it opens — and LAYER_INVENTORY
had no entry for it. This is the layer the request called "annotation": it lives
in the 120_codex-cli checkout, not in 121_openai-codex, which is why a search
of the latter turns up only MCP tool annotations and url_citation annotations.

It is runtime-conditional, not a config toggle. lib.rs:33-80 resolves
enablement from the auth server and caches it per thread;
features/src/lib.rs:277 records the old config flag as removed. So there is no
key for this GUI to write and nothing in [features] to link to, and the row
renders with no switch at all — the existing rule for a layer Codex cannot
suppress, since a disabled control would claim a capability that does not exist.

Two bugs the work surfaced.

Its order is null, because it registers through
extensions.context_contributors() rather than appearing in world_state.rs. The
panel collapsed a null order to 0, which put such a layer at the very TOP of the
stack and claimed it is assembled first. Null now sorts last.

Rendering the real page in a browser caught the second one, which no unit test
had: the row said "Always on" while its dialog described a condition. So did
plugins, which has been shipping that contradiction since the panel landed. The
condition map existed and only the dialog read it. The row reads it now.

Codex Set prompt panel showing the Commit attribution row

The row sits last in the state list, carries the neutral position marker rather
than an invented number, has no switch, and states the real condition.

Verification

bun test tests/codex-prompt-layers.test.ts + route          89 pass 0 fail
bun test ./gui/tests/codex-set-prompt-layers.test.tsx       15 pass 0 fail
bun test ./gui/tests/                                     1075 pass 0 fail
bun test ./gui/tests/locale-parity.test.ts                   3 pass 0 fail
cd gui && bun x tsc -b --force                              exit 0
bun x tsc --noEmit                                          exit 0
bun run lint:gui                                            exit 0, no new findings
bun run build:gui                                           exit 0
bun run privacy:scan                                        passed
git diff --check                                            clean

Live verification against the running proxy, not only the test doubles:

GET /api/codex-prompt -> inventory=16
  {"id":"git-attribution","class":"runtime-conditional","key":null,"default":null,"order":null}

--dump-dom of #codex-set/prompt, state list order:
  base-instructions ... multi-agent-mode, git-attribution
  then the transition list: model-switch, realtime
  git-attribution note: "Set by your account's attribution policy."
  plugins note:          "Emitted when a plugin is selected ..."

That DOM dump is what found the always-on bug; the API check is what confirms the
descriptor reaches a real client rather than a fixture.

The probe reports this layer as not-exposed rather than guessing a tag. The Rust
source names a <git_attribution> marker pair, but a world-state section is
diff-rendered and emits nothing on a turn where its state has not changed — live
codex debug prompt-input at 0.145.0 returned 32978 bytes with no such block. The
probe's own header records that inferring tags from Rust constants produced a wrong
mapping once, so this id goes in the unmapped list instead.

Nine locales carry the three new strings; locale parity stays at zero gaps.

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed.
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults.

Nothing here touches auth or credentials. The row is read-only by construction:
the id is absent from TOGGLE_KEYS, so the write allowlist cannot emit a key for
it, and the route's existing table-driven guard already refuses every
non-config-toggle id.

@lidge-jun
lidge-jun requested a review from Ingwannu as a code owner August 26, 2026 17:32
@coderabbitai

coderabbitai Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (2)
  • ^dev$
  • ^preview$

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 9789793b-bad9-4f40-a8c2-56f7452e4cc7

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the enhancement New feature or request label Aug 26, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Deterministic PR hygiene checks passed.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c220e2335a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

* `Generated with Codex.` PR marker, disabled sends an explicit countermand. So the
* row's condition line must name the policy rather than claiming "always on".
*/
{ id: "git-attribution", class: "runtime-conditional", key: null, default: null, order: null },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Gate the attribution row on actual Codex support

On installations using the official 121_openai-codex build, this unconditional descriptor advertises a prompt layer that does not exist: the upstream audit explicitly records that this checkout lacks ext/git-attribution (devlog/_plan/260827_dev_regression_and_prompt_variants/000_plan.md:8-10). Because serialize() sends the complete static inventory and the GUI renders every descriptor, neither the probe result nor the account policy suppresses the row. Detect the installed binary's capability/version and include this descriptor only for Codex variants that actually provide the extension.

Useful? React with 👍 / 👎.

* `Generated with Codex.` PR marker, disabled sends an explicit countermand. So the
* row's condition line must name the policy rather than claiming "always on".
*/
{ id: "git-attribution", class: "runtime-conditional", key: null, default: null, order: null },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Document the new conditional extension layer

Adding this user-visible row leaves docs-site/src/content/docs/guides/codex-prompt.md:20-28 and its translations directly contradicting the dashboard: they still say extension layers cannot be listed and describe only an “Always on” category rather than an account-controlled runtime condition. Update the prompt-layer guide and localized copies so users can interpret the new row and its neutral ordering marker correctly.

AGENTS.md reference: AGENTS.md:L279-L280

Useful? React with 👍 / 👎.

@lidge-jun

Copy link
Copy Markdown
Owner Author

리뷰 · 우선순위 58 / 80

설명

이 풀 리퀘스트는 #2691 위에 쌓여 있습니다. 베이스는 codex/branch-cleanup-sha-guard 입니다. Codex 프롬프트 패널이 ext/git-attribution 이 넣는 커밋/PR 어트리뷰션 레이어를 목록에 안 보여 주던 구멍을 메웁니다. current devLAYER_INVENTORY(src/codex/prompt-layers.ts 87-103줄) 에는 이 id 가 없습니다.

git-attributionruntime-conditional 로 추가하고 order: null 로 둡니다. GUI 정렬은 order ?? 0 이던 것을 order ?? Number.MAX_SAFE_INTEGER 로 바꿔, 등록 순서 의존 레이어가 맨 위로 가지 않게 합니다. 행과 조건 문구가 "항상 켜짐"이 아니라 계정 어트리뷰션 정책을 말하도록 i18n 키를 넣습니다. prompt-text-probe 는 이 id 를 unmapped 로 두어, 추출기가 실제로 못 본 태그를 있다고 거짓말하지 않게 합니다.

초안이 아닙니다. 다만 스택 의존이라 #2689#2691 이 먼저입니다. mergeable_state 는 unstable 이고 테스트 샤드 실패가 보입니다. GUI 스크린샷이 포함되어 있습니다. types.ts/config.ts 는 건드리지 않습니다. 미리보기 배포는 계획에 없습니다.

src/codex/prompt-layers.ts - git-attribution 을 inventory 에 넣는 방향은 HEAD 대비 맞습니다
gui/src/pages/codex-set-prompt.tsx - null order 를 맨 뒤로 보내는 정렬 수정이 필요합니다
gui/src/components/codex-set/PromptLayerRow.tsx - 조건 키를 행에도 쓰게 한 점이 대화상자와 맞습니다
src/codex/prompt-text-probe.ts - unmapped 로 둔 이유는 본문과 일치합니다
경로/심볼 - #2691 베이스라 current dev 에 단독 합칠 수 없습니다

메인테이너의 판단이 필요한 지점

  • 계정 정책만으로 켜지는 레이어를 패널에 보여줄지, 문서만으로 suff 한지 정해야 합니다
  • 스택을 한 번에 합칠지 PR 단위로 합칠지 정해야 합니다
  • 로케일 키가 모든 언어에 채워졌는지 최종 확인이 필요합니다

너의 추천

스택(#2689, #2691)이 dev 에 들어간 뒤에 합치세요. 패널이 실제 프롬프트 한 층을 빼먹던 문제를 고치는 방향은 맞습니다. CI 초록을 기다리세요. types.ts/config.ts 분할로 닫을 대상이 아닙니다. 미리보기 배포는 계획에 없습니다.

이 댓글은 grok-bot이 작성했습니다

@lidge-jun
lidge-jun force-pushed the codex/branch-cleanup-sha-guard branch from cbeda14 to 0bbc4d1 Compare August 26, 2026 17:53
@lidge-jun
lidge-jun force-pushed the codex/git-attribution-layer branch from 3684a6b to 6ce9bb5 Compare August 26, 2026 17:53
jun added 4 commits August 27, 2026 02:55
…ection

Three more findings from the dev-versus-main review.

The closed-PR branch cleanup matched branches by NAME alone. Any branch whose
same-name historical pull requests were all closed became a deletion candidate,
without checking that the branch still pointed at one of their head commits. A
`codex/`-style name reused for new work inherited the closed history of every PR
that had ever carried that label, and the commits it carried had never been in a
pull request at all. The planner now requires the current tip to match a closed
head SHA, and keeps the branch when either SHA is unknown. The script had no test
of any kind, which is how this shipped; it has eight now, including one per
existing safety rule so the tip check cannot be reordered ahead of them.

glm-5.3-flash was still routed through the vision sidecar on eight providers.
The correction commit fixed the two Alibaba entries and left umans, cline-pass,
nvidia, zai, zhipu-bigmodel-coding, both Volcengine plans and ollama-cloud
behind. That list means "the proxy describes this model's images", so a native
VLM sitting in it got a text description of a picture it could read itself: no
error, a worse answer, an extra call. The parity assertion is now written across
the whole registry rather than per provider, because the defect was entries
drifting apart rather than one being wrong.

It was also missing from ZAI_GLM_53_MODELS, so on Z.AI it advertised a 1M context
with a null effort ladder, no default effort and no output cap while its siblings
carried three tiers, a max default and 131072 tokens. The parity test pinned that
gap in place because it was written from the incomplete state instead of from the
family definition.

And the Moonshot `$ref` normalizer overwrote numeric bounds with the sibling's
instead of intersecting them. Its comment claimed the node "narrows the target",
which is true only when the node happens to be narrower - a node declaring
minLength 1 beside a target declaring minLength 5 emitted minLength 1, a contract
weaker than either side asked for. That is the same failure the required/properties
composition already fixed for set-valued keywords. Lower bounds now take the max,
upper bounds the min, and a test covers both directions so the fix cannot become
"always prefer the target", which would discard a real narrowing.

Each fix is mutation-verified: disabling the tip comparison turns the reused-branch
test red on its own, and the looser-bound test was red before the intersection
landed and green after.
The hygiene gate flagged the new eslint-disable for no-require-imports, and it
was right to: the repo already reaches CommonJS helpers through await import()
(ci-workflows.test.ts:5030), so the suppression was covering for a spelling
choice rather than an unavoidable constraint.

The interop shim is deliberate rather than defensive. A .cjs module reached
through ESM can arrive either directly or under default depending on the loader,
and picking whichever object actually carries the planner keeps the test honest
about what it is calling instead of asserting against undefined.
…state

Both were written from what the registry happened to serve rather than from the
family definition, so adding glm-5.3-flash to ZAI_GLM_53_MODELS broke them.

codex-catalog expected modelSupportsReasoningSummaries to hold exactly the four
older 5.3 ids. That map is derived from the family constant, and flash belongs in
it: the reasoning-summary question and the vision-sidecar question have different
answers for this model, and only the second one excludes it.

cline-pass had my own editing mistake in it - I had added the id to a
noVisionModels literal that is computed from CLINE_PASS_IMAGE_MODELS, so the
expectation contradicted the derivation it was checking. Removed; the list already
excludes flash on its own.
…g always-on

The prompt panel was under-reporting the prompt by a whole developer section.
ext/git-attribution contributes one - the instruction to add a
Co-authored-by: Codex trailer to commits the model writes and a
Generated with Codex. line to pull requests it opens - and LAYER_INVENTORY had
no entry for it.

It is runtime-conditional, not a config toggle. lib.rs:33-80 resolves enablement
from the auth server and caches it per thread, and features/src/lib.rs:277
records the old config flag as removed, so there is no key for this GUI to write
and nothing in [features] to point a user at. The row therefore renders with no
switch at all, which is the existing rule for a layer Codex cannot suppress: a
disabled control would claim a capability that does not exist.

Its order is null because it registers through
extensions.context_contributors() rather than appearing in world_state.rs, so its
position is registration-order dependent. That exposed a sorting bug: the panel
collapsed a null order to 0, which put such a layer at the very TOP and claimed
it is assembled first. Null now sorts last.

Rendering the page in a real browser then caught something a unit test had not.
The row said "Always on" while its dialog described a condition - and so did
plugins, which has been shipping that contradiction since the panel landed. The
condition map existed and only the dialog read it. The row reads it now, so the
two surfaces cannot disagree about the same layer.

The probe reports this layer as not-exposed rather than guessing a tag. The Rust
source names a <git_attribution> marker pair, but a world-state section is
diff-rendered and emits nothing on a turn where its state has not changed: live
codex debug prompt-input at 0.145.0 returned 32978 bytes with no such block. The
probe header already records that inferring tags from Rust constants produced a
wrong mapping once, so this id goes in the unmapped list.

Nine locales carry the three new strings. The about text says what a user needs
to decide with: that Codex resolves this from the account, and that turning it
off sends the opposite instruction rather than sending nothing.
@lidge-jun
lidge-jun force-pushed the codex/branch-cleanup-sha-guard branch from 0bbc4d1 to f25dab8 Compare August 26, 2026 17:56
@lidge-jun
lidge-jun force-pushed the codex/git-attribution-layer branch from 6ce9bb5 to bbfdd65 Compare August 26, 2026 17:56
@lidge-jun
lidge-jun changed the base branch from codex/branch-cleanup-sha-guard to dev August 26, 2026 18:04
@lidge-jun
lidge-jun merged commit ee182a1 into dev Aug 26, 2026
25 checks passed
@lidge-jun
lidge-jun deleted the codex/git-attribution-layer branch August 26, 2026 18:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant