Skip to content

fix(github): verify receipts and recover partial delivery - #264

Merged
frankekn merged 26 commits into
mainfrom
codex/issue102-delivery-receipt
Oct 19, 2026
Merged

frankekn merged 26 commits into
mainfrom
codex/issue102-delivery-receipt

Conversation

@frankekn

@frankekn frankekn commented Oct 16, 2026 •

Copy link
Copy Markdown
Owner

Why

Refs #102。相同 head 的 marker 與任意同名 check 不能證明交付完成。GitHub 已存 POST 卻回 5xx 時,必須核對實際 artifact,避免同次執行重送。

Behavior

  • v2 receipt 綁定 check ID、App、PR、head、round 與 run。完成 check 與 receipt 都符合才允許 same-head skip。跨 head 配對保留已完成 round;歷史不可讀時,以 owned terminal failure 停止,不重貼 review。
  • review、comment、pending check 的 POST 每次只送一次。模糊 5xx 最多三次只讀查證。review 或留言預讀失敗仍嘗試一次 POST,不做查證或重送。
  • 普通恢復最多自動 full re-review 一次。只有人工 recheck input 或 @needlefish recheck 重置預算。到達上限時,check 與錯誤留言明確提示下一步。
  • 直接 CLI 的 custom App installation token 由 GH_TOKEN 提供,以 NEEDLEFISH_CHECK_APP_ID pin 身份。從符合 pin 的 GitHub check metadata 取得精確 Bot 作者,舊 marker 另須核對完整完成 receipt,更新 prior review 前也須符合目前 App pin。缺失、錯誤或其他 App 身份不能抑制審查。bundled workflow 固定使用 GITHUB_TOKEN,明確清空 custom App pin。
  • workflow、composite action 的 author-skip 與 caller-retry-required check 都寫完整 v2 metadata。reconcile 保留其 terminal 行為,不重複 dispatch 或重貼同一操作錯誤。完整 v2 驗證不放寬;legacy fallback 只接受正整數 run ID。

Limits

舊 v1 marker 不足以證明完成。升級後的第一輪會完整重審,可能重發舊 inline。無法建立或核對 pending check 時,停止模型工作,可能沒有新的 terminal check。

部分 inline、查證窗口用盡或程序崩潰後,full re-review 仍可能重發 artifact。本 PR 不保證任意延遲下的 exactly-once,未新增服務、資料庫、依賴或 runtime。

Validation

  • terminal producer 回歸先紅後綠。真實 workflow shell 加 fake gh 的 79 項測試通過,涵蓋兩個 skip producer 與 retry report 的實際 POST roundtrip。4fab20f 的 Linux CI 通過 1,480 項,0 fail、0 skip。
  • 9a22b0a 的 custom App regression 先紅後綠,另涵蓋錯誤身份、receipt 與歷史讀取失敗。完整 posting/termination suite 為 180 pass、0 fail、10 Linux-only skip;該 head 的 Linux CI 已補驗全部 1,489 項,0 fail、0 skip。
  • 35991e2 在選取 prior review 前驗證 App ownership。切換 App 的同 head/跨 head cases 先紅後綠,驗證新 review POST 與零舊 review PUT。最後相關 suite 46/46 pass,TypeScript、全 src/eval lint、diff check 通過。此 head 的 required Linux CI 已通過全部 1,490 項,0 fail、0 skip。
  • 完整 branch 的第 8 輪 P2 autoreview 及之後各次 delta review 均無未解 actionable findings。最後三次 exact dirty-delta P2 autoreview 均 exit 0、findings [],保存並核對實際 bundle。CLI 與 shell 測試使用 stub API,沒有冒充 live posting。

gateClass: R,R gate pending。 PAT deployment 的 v2 receipt 會進入後續 prMeta。真實模型 full93×3/holdouts、GitHub posting canary 尚未完成,不能宣稱 merge-ready。既有 Actions 使用 installed binary,不能充當本 PR adapter 的 live 證據。

依賴 #257。這只完成 #102 的 delivery 子範圍,不自動關閉 issue。未部署。

Owner 在 2026-10-18 豁免 Class R gate。full93×3/holdouts 與 GitHub posting canary 都沒有跑。合併依據是目前 head 上綠的 needlefish-ci 和 Needlefish LGTM。

@frankekn

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ⚠️ Failed 2026-10-16T09:44:17.794194Z d41a68f Manual request
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@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: 3c8621f9a0

ℹ️ 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".

Comment thread src/adapters/github.ts Resolved
Comment thread src/adapters/github.ts Resolved

@frankekn frankekn left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

老審 staff review @ 3c8621f

方向對:receipt 綁 check id、app、PR、round、run,POST 5xx 先讀回再決定,比「marker 加任意同名 check」可信得多。錯配矩陣(app/head/round/run/id/author/pr)、stored 5xx、foreign app 的測試都紮實。問題在 adapter 和 workflow 的契約沒對齊,描述裡的預算保證在實際 workflow 不成立。

P2

  1. reconcile 自動 dispatch 被當成人工 --recheck,繞過 recovery budget。 workflow_dispatch 一律設 NEEDLEFISH_RECHECK_INPUT=1,reconcile job 看到 Needlefish: review failed 就自動 dispatch。本 PR 新增的失敗(history unreadable、inline delivery incomplete、recovery limit reached)全都是這個 title。實際序列:普通 run 失敗 → reconcile 自動 recheck(recoveryAttempt 歸 0,不受限)→ 失敗後 if (prior.recheck) break 把計數重置 → 下一次普通事件又能 full re-review。上限其實是 reconcile 的 infra_fails >= 2 和 adapter budget 疊起來的結果,不是描述的「普通重跑最多一次、recheck 保留給人工」。有了 v2 receipt,reconcile 已經不需要靠 recheck 繞過 same-head skip(能 skip 的情況 reconcile 本來就不會 dispatch)。要求:reconcile dispatch 不再設 recheck(例如加一個 input 區分人工和 reconcile),讓 adapter budget 生效;補一個 workflow 測試,證明 dispatch 加 adapter 的總 full review 次數有上限。
  2. 附議 Codex P2(L1509),而且不只是 backoff。 reconciledPost 在 5xx 之後立刻讀回、立刻重 POST。GitHub 的 review/comment/check 列表不保證 read-after-write,剛存好的 artifact 可能還讀不到,這時就會重複發文,正好是本 PR 要防的情況。要求:讀回前等 ghPostRetryBaseMs() * attempt,並沿用 NEEDLEFISH_GH_POST_RETRY_MS。
  3. 附議 Codex P1(AGENTS.md L81-83 gate)。 這 PR 改的就是 posting 和 retry 行為,posting canary 和 delivery gate 還沒跑、P2 autoreview 也還沒完成,現在不算 merge-ready。

P3

  1. reconcile 的 terminal 判斷(review.yml L523 valid=)還是「任意同名 Needlefish check」,不看 app,也不看 v2 prNumber,正是本 PR 說不能當證明的東西。系統現在有兩套「已交付」定義。至少同樣過濾 app 和 v2 prNumber,或在描述說明為什麼可以不一致。描述的 Blast Radius 沒提 reconcile。
  2. L70-77 的 retry 註解還寫著「retrying a POST would then double-post」,跟 reconciledPost 的新行為矛盾,要更新。
  3. pending check 建不出來時,原本 completion 會自己建 check,現在整輪直接不跑,PR 上不會有任何 Needlefish check(required check 會停在 Expected,不是紅叉)。理由成立(未知的 id 不能拿來完成 check),但這是行為變更,描述要寫。
  4. 部署後所有 open PR 的 v1 marker 失效:第一次 push 不配對、不 minimize、不 PUT 舊 review,整份重發。描述只寫「保守恢復」,要把這個可見成本寫出來。
  5. 格式:新增行混用 1-space 和 tab(github-posting.test.ts 的 prNumber、storedReviewPost5xx 一帶,L120/L128/L133),github.ts L695、L742、L1701、L1738 一行塞完整個條件。repo 沒有 formatter 擋,請整理成既有風格。

其餘見 inline。

CI:Needlefish、review、needlefish-ci 都還在 in_progress。這次跑的 Needlefish check external_id 仍是舊格式的 run ID,所以 CI 跑的不是本 PR 的 adapter,CI 綠燈也證明不了 v2 路徑。

結論:adapter 內沒有 correctness blocker。#1、#2 是 dedupe 或預算保證實際上不成立,#3 是 repo gate,三項處理完再 merge。

— 老審

Comment thread src/adapters/github.ts Resolved
Comment thread src/adapters/github.ts Resolved
Comment thread src/adapters/github.ts Resolved
Comment thread src/adapters/github.ts Resolved
Comment thread src/adapters/github.ts Resolved
Comment thread src/adapters/github.ts Resolved
Comment thread src/adapters/github.ts Resolved
@frankekn

Copy link
Copy Markdown
Owner Author

@codex review

@frankekn frankekn left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

老審 staff review @ 24e0ee3(delta 3c8621f..24e0ee3)

這一推把 adapter 內的 double-post 與跨 head fate 收乾了;workflow 契約仍沒對齊,而且「模糊 5xx 一律 defer」讓 #1 更吃重。

本 delta 做對的

  • reconciledPost 不再對負讀重 POST:負讀 ≠ 沒存,直接丟給下一輪恢復。lag fixture +「defers recovery when no artifact is visible」把 Codex P2 / 前次 #2 關掉,而且比加 backoff 更乾淨。
  • completedCheckVerdict 與 hasNewerRound 拆開:same-head skip 才看 newer round,跨 head fate 只認 receipt 自己的 check。前次 inline「H1 成功 → recheck 失敗 → push H2 丟掉 prev」關掉。
  • deliveredCheckId 之後才算 recovery:已完成交付後的暫態 history read 不會把預算吃光;preserves one recovery 測試對症。
  • trailing marker(parseState 要求 --> 後無內容)+ quoted v2 receipt 不能 skip:防模型把 receipt 抄進正文。
  • L70-77 註解現在跟「POST 不重試」一致,前次 #5 關掉。

仍開

P2

  1. reconcile 仍一律 --recheck(前次 #1,未動 review.yml)。 NEEDLEFISH_RECHECK_INPUT: workflow_dispatch && 1 還在。本 delta 把更多模糊 5xx 變成 Needlefish: review failed,reconcile 會更常自動 dispatch;每次都把 recoveryAttempt 歸 0,adapter 剛補的 budget 照樣被繞過。要求不變:reconcile 與人工 recheck 分開,補 workflow 測試釘總 full-review 上限。
  2. 附議 Codex P1 / 前次 #3:delivery gate 與 posting canary 未跑。 描述自己也寫了。posting/retry 行為變更,沒有 gate 報告不算 merge-ready。

P3(前次未關)

  1. reconcile terminal 仍是任意同名 Needlefish check(#4)。
  2. pending 建失敗行為變更、v1 失效可見成本,描述仍沒寫(#6、#7)。
  3. 格式混用 1-space/tab 與超長一行(#8);本 delta 測試新增行同樣是 1-space。
  4. inline 仍開:app.slug === "github-actions" 硬編、recoveryAttempt 先寫粗估值再建 check、postIssueComment pre-read 失敗即不發、delivery? 選填、marker 註解刪 rational。

CI:目前只看到 needlefish-ci in_progress。CI 綠燈仍證明不了 v2 adapter 路徑(self-managed binary ≠ 本 PR head)。

結論:#2 與跨 head 關掉。合併前至少 #1(workflow recheck)與 #2(gate/canary)。

— 老審

Comment thread src/adapters/github.ts Resolved
Comment thread src/adapters/github.ts Resolved
Comment thread src/adapters/github.ts Resolved
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 👍

Reviewed commit: 24e0ee3171

ℹ️ 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".

@github-actions github-actions 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.

NEEDS HUMAN 👀 — The new reconciled POST read loop has no source-defined caller deadline or per-read timeout, so aggregate timeout behavior cannot be computed from this surface.
Hardens GitHub review delivery with verifiable v2 receipts, bounded recovery, App identity pinning, and explicit manual recheck routing.
❔ 1 undetermined

⛔ Why not green:

  • ⛔ The new reconciled POST read loop has no source-defined caller deadline or per-read timeout, so aggregate timeout behavior cannot be computed from this surface.

Coverage: 19/19 changed files deep-reviewed across 3 hotspots

Review target: PR #264 3046b30..69df42c

Human callouts (non-blocking):

  • workflow: .github/workflows/commands.yml, .github/workflows/hosted-review.yml, .github/workflows/review.yml

Findings

No actionable findings. Prefer this over padding weak ones.

Checked (2)
  • Inspected changed GitHub delivery and workflow hunks.
  • reconciledPost bounds retry iterations at 3; absent a configured per-read timeout alone does not establish a caller-visible defect or block the verdict.
Residual risk (1)
  • ⛔ The new reconciled POST read loop has no source-defined caller deadline or per-read timeout, so aggregate timeout behavior cannot be computed from this surface.

5 calls · map 2m 11s → deep:Operator documentation for recheck and custom App identity 33s → deep:GitHub review receipt validation and idempotent delivery 2m 5s → deep:Workflow recheck routing, reconciliation, and author-skip receipts 2m 36s → critic 25s · total 5m 12s

Comment thread src/adapters/github.ts Resolved
Comment thread src/adapters/github.ts Resolved
Comment thread src/adapters/github.ts Resolved
@github-actions github-actions Bot added the needlefish:changes-requested Needlefish review verdict label Oct 16, 2026
@frankekn

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Delightful!

Reviewed commit: 5c61ad4f31

ℹ️ 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".

@github-actions

This comment has been minimized.

Comment thread src/adapters/github.ts Resolved
Comment thread src/adapters/github.ts Resolved
@frankekn

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep it up!

Reviewed commit: 511dc0bd6e

ℹ️ 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".

@github-actions

This comment has been minimized.

Comment thread src/adapters/github.ts Resolved
Comment thread .github/workflows/review.yml Resolved
Comment thread README.md Resolved
@frankekn

Copy link
Copy Markdown
Owner Author

@codex review

@frankekn frankekn left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

老審 staff review @ d41a68f(delta 35991e2..d41a68f)

兩 commit:把 pin 後的作者信任收成「只認 pinned Bot」,再把同一套 configuredCheckAppId() 接到 explain。對症,沒有新 blocker。

本 delta 關掉的

  • Needlefish P2(explain 沒傳 App pin):附議,已修。 configuredCheckAppId 抽出共用;runGithubExplain 傳 expectedAppId 進 findPreviousReview。測試改走真實 CLI spawn,涵蓋 default/4812/非法 pin(status 1、不寫 prompt/POST)。
  • isTrustedStateAuthor 在 pin 下仍認 Actions/self: appBotLogin !== null 時只比對該 Bot login + type Bot;customAppBotLogin(null pin) 回 null 才走預設。新增 actions-bot、self-user mutation,以及「pinned App 不能 reconcile Actions 存下的 review POST」回歸(stderr Cannot verify ownership of the stored review)。

仍開

P1

  1. Class R gate 與 posting canary。 描述仍寫 R gate pending;canary 要斷言讀回的 external_id 與送出字串相同(含約 96 字 v2 receipt)。沒有 gate 報告就不算 merge-ready。

P3

  1. 換 App 後舊 App 的 review/inline 仍留在 PR。Limits 還沒寫「切換 App 的第一輪會另開新 review,舊的不更新」。
  2. findPreviousReview 的 TRUST BOUNDARY 仍寫它決定 PUT 的 review id;實際 App ownership 閘門在 runGithub 的 previousCheckOwned。註解還沒指過去。
  3. Validation 停在 35991e2,沒有本 delta(ce48c9f/d41a68f)的測試結果或 CI 連結。
  4. receipt 三份解析仍不一致(TS/workflow JS/reconcile jq),可放 follow-up。

CI:head d41a68f 上 needlefish-ci in_progress、review queued。mergeable_state=unstable,label 仍是 needlefish:changes-requested。

結論:程式面 P2 清空。合併前仍差 P1 #1。

— 老審

@github-actions

This comment has been minimized.

@github-actions github-actions Bot added needlefish:needs-human Needlefish review verdict and removed needlefish:changes-requested Needlefish review verdict labels Oct 16, 2026
The delivery receipt requires the check-runs POST to return an owned app
identity and reads prior Needlefish check runs before review. The fake gh
in main's connection tests predates both, so delivery stopped before the
review ran.
@frankekn
frankekn changed the base branch from codex/issue145-incomplete-failure to main October 18, 2026 12:07

@frankekn frankekn left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

老審 staff review @ eec0a58(delta d41a68f..eec0a58)

這一推是 db0f201 merge main(含 #257、#155、#171 等)加 eec0a58 修 main 帶進來的 fake gh。PR 自身 diff 對 main 現在是 21 commits/19 檔。我逐檔比過 merge 前後的 PR patch:workflow、action、scripts、explain 全部不變;README 段落改搬到 docs/reference.md 與 docs/reference.zh-TW.md,內容一致。真正需要看的是 github.ts 與 #257 的衝突解法。

本 delta 核過的

  • #257「最新完成的 check 決定 same-head」:語意保住,而且更嚴。 merge 後整段舊 completedCheckVerdict(repo, headSha) 被本 PR 的 receipt 版取代:verdict 只認 receipt 綁的 checkId,再由 hasNewerRound 擋掉同 head、同 App、同 PR 的任何較新 round。#257 移植的四個「newer terminated/failed/superseded/neutral 不被舊 pass 覆蓋」和「newer pass 仍覆蓋舊 terminated」都改用 receiptRoundCheck 跑,斷言沒放寬。
  • resolveConnectionOptions 位置:照 main 放在 createPendingCheck 之前,credential 拒絕不會開出孤兒 pending check,沒有回歸。
  • eec0a58:只讓 #171/#155 的兩個 integration fake gh 在 POST 回 app、對 check-runs? 回空 list。這是本 PR 新要求的 owned-app 身分,屬必要的測試適配,不是放寬。

本 delta 新增

P3

  1. #257 的說明註解在 merge 中消失,行為也反過來了,卻沒留一句話。 main 寫的是「in-progress checks 被忽略,孤兒 in_progress 不擋 skip,用 --recheck 回收(#102)」。現在 hasNewerRound 不看 status,較新的 in_progress round(孤兒或並行)會讓 skip 失效,進入 recovery 預算。這正是 #102 要的,bundled workflow 也有 per-PR concurrency(cancel-in-progress: false)所以不會並行。但直接 CLI 沒有這層保護,下一個人讀 code 只會看到 hasNewerRound 沒有 status 過濾,看起來像漏寫。見 inline。
  2. 移植測試的 fixture 不真實。 same-head exit uses the receipt-bound completed verdict 兩個 check 共用 roundId: "fixture-round",external_id 還帶已移除的 recoveryAttempt: 0。正式流程中同一 round 只會有一個 check(createPendingCheck 的 reconcile 遇到兩個會 throw)。目前測試會過,是因為 receipt 用 checkId 挑 check。請改成不同 roundId,並拿掉 recoveryAttempt,讓測試證明「id + round 都要對」而不是只靠 id。
  3. docs/reference.md:140 的 merge 結果有一行超長(...do not let old receipts suppress recovery. A same-head skip keeps the),zh-TW 對應段落沒有改。格式問題,不擋。

仍開

P1

  1. Class R gate 與 posting canary。 描述仍寫 R gate pending。這次 merge 把 #257 的 same-head 語意也併進同一條路徑,canary 除了要斷言讀回的 external_id 與送出字串相同(含 v2 receipt),還應涵蓋一次「同 head 重觸發 → skip」與一次「較新 terminated round → recovery」。沒有 gate 報告就不算 merge-ready。

P3

  1. Limits 還沒寫「切換 App 的第一輪會另開新 review,舊的不更新」。
  2. findPreviousReview 的 TRUST BOUNDARY(github.ts:917)仍寫它決定 PUT 的 review id。實際的 ownership 閘門在 runGithub 的 previousCheckOwned,註解還沒改。
  3. Validation 停在 35991e2。d41a68f 與這次 merge 都沒有測試結果或 CI 連結;merge 改了 same-head 測試,這筆更該補。
  4. receipt 三份解析不一致(TS/workflow JS/reconcile jq),可放 follow-up。

CI:head eec0a58 上一個 needlefish-ci cancelled(被同 head 新 run 取代),另一個 in_progress;review queued。mergeable_state=blocked,label 是 needlefish:needs-human。沒在 box 上重跑,以 CI 為準。

結論:merge 解得乾淨,#257 語意有保住,沒有新 blocker。新增的都是 P3(註解、fixture)。合併前仍差 P1 #1,另外要等這個 head 的 CI 結果。

— 老審

Comment thread src/adapters/github.ts Resolved
@github-actions

This comment has been minimized.

@github-actions github-actions Bot added needlefish:pass Needlefish review verdict and removed needlefish:needs-human Needlefish review verdict labels Oct 18, 2026
@github-actions

This comment has been minimized.

Comment thread src/adapters/explain.ts Resolved
@github-actions github-actions Bot added needlefish:changes-requested Needlefish review verdict and removed needlefish:pass Needlefish review verdict labels Oct 18, 2026

@frankekn frankekn left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

老審 staff review @ 9d2c41c(delta eec0a58..9d2c41c)

兩 commit:d782eb0 照我的 suggestion 補 hasNewerRound 註解;9d2c41c 修 Needlefish 在 d782eb0 抓的 explain receipt 問題。另外 owner 在描述與 thread 宣告豁免 Class R gate。

本 delta 關掉的

  • Needlefish P2(explain 吃未驗證的 prior state):附議,已修。 findDeliveredReview 要求 v2 receipt、check id 與 App ownership 相符、completedCheckVerdict 非 null,unpinned 也走同一套;legacy v1 與 in_progress v2 都回 null。我核過寫入順序:postCheck 在 persistStateMarker 之前(github.ts:1952 → 1961),所以正常流程的最新 marker 一定已交付,新閘門不會讓 explain 平白失去 context,真正被擋的是升級前的 v1 和崩潰留下的半成品。
  • P3 #6:hasNewerRound 註解已補(d782eb0)。
  • P1 #1(Class R gate/posting canary):以 owner 豁免結案,不是以證據結案。 風險照講:v2 receipt 經真實 GitHub external_id 的 round-trip 從沒在 live 跑過,所有證明都是 stub gh。合併後第一個真實 PR 請盯一次:同 head 重觸發要 skip,@needlefish explain 要拿得到 findings。

本 delta 新增

P2

  1. PR 描述自相矛盾。 中段仍寫「gateClass: R,R gate pending … 不能宣稱 merge-ready」,最後一段又寫 owner 豁免、合併依據是綠 CI 加 Needlefish LGTM。描述是合併紀錄,請把舊段落改成豁免版本,只留一個結論。

P3

  1. 交付判斷現在寫了兩份。 findDeliveredReview(github.ts:953)和 runGithub 的 previousVerdict && previousCheckOwned(github.ts:1711-1715)是同一個 predicate,下次只改一邊就分岔。另外 pinned 模式的 findPreviousReview 會往回找已交付的舊 marker,unpinned 只看最新一筆、不合格就 null,新註解「Legacy and undelivered markers give it no context」只對 unpinned 精確。見 inline。
  2. 測試沒涵蓋 Needlefish 要的 mismatched-v2。 矩陣只有 legacy 和 in_progress;check 已 completed 但 round/run 不符這條沒測,而那正是 completedCheckVerdict 對 external_id 的比對。見 inline suggestion,加了之後在 d782eb0 上應該會紅。

仍開

  • P3 #2:Limits 還沒寫「切換 App 的第一輪會另開新 review,舊的不更新」。
  • P3 #3:findPreviousReview 的 TRUST BOUNDARY(github.ts:917)仍說它決定 PUT 的 review id。真正的閘門在 runGithub 的 previousCheckOwned,現在 explain 也改走 findDeliveredReview,註解更該指過去。
  • P3 #4:Validation 仍停在 35991e2。9d2c41c 的測試結果只寫在 thread reply。豁免後合併依據就是 CI,請補這個 head 的 needlefish-ci 連結。
  • P3 #5:receipt 三份解析不一致(TS/workflow JS/reconcile jq),follow-up。
  • P3 #7:github.test.ts 的 same-head fixture 仍共用 roundId: "fixture-round" 並帶已移除的 recoveryAttempt: 0。
  • P3 #8:docs/reference.md:140 超長行。

CI:head 9d2c41c 上 needlefish-ci in_progress、review queued。上一個 head d782eb0 的 needlefish-ci success、Needlefish changes_requested(就是本 delta 修的那條)。label 仍是 needlefish:changes-requested,mergeable_state=blocked。沒在 box 上重跑,以 CI 為準。

結論:fix 對症,程式面沒有新 blocker。合併前要 #9 改描述,再加上豁免段落自己定的條件:這個 head 的 needlefish-ci 綠、Needlefish LGTM。#10、#11 建議順手收,不擋。

— 老審

Comment thread src/adapters/github.ts Resolved
Comment thread src/adapters/explain.test.ts Resolved
Comment thread src/adapters/explain.test.ts Resolved
@github-actions

This comment has been minimized.

Comment thread .github/workflows/review.yml Resolved

@frankekn frankekn left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

老審 staff review @ 9a1b53c(delta 9d2c41c..9a1b53c)

兩 commit:155a451 修 Needlefish 在 9d2c41c 抓的「workflow receipt 吃掉 recovery 預算」;9a1b53c 補我上輪 #11 要的 mismatched-v2 測試。

本 delta 關掉的

  • Needlefish P2(workflow skip receipt 計入 recovery):附議,已修。 我核過三個 workflow writer 都不寫 recheck:author-skip(review.yml:192)、caller-retry-required(review.yml:541)、composite action(action.yml:100);adapter 一律寫 boolean(github.ts:1765)。所以 prior.recheck === undefined 目前確實只命中 workflow receipt。新測試每種放兩筆 receipt,修前 priorRounds.length = 2 會直接 RecoveryLimitError,剛好打在邊界上,有效。retry-required 排除也合理:它前面那幾次 adapter 失敗本身已帶 recheck 被計數,不會漏算。
  • P3 #11:receipt: "mismatched"(roundId 不符、check 已 completed)走到 latestReview === undefined 的分支,涵蓋了 completedCheckVerdict 的 external_id 比對。

本 delta 新增

P3

  1. 「是不是 review attempt」靠欄位缺席判斷。 語意現在藏在三個 jq writer「沒寫 recheck」上,跨 TS/YAML 兩種語言、沒有任何一邊寫明。哪天有人想在 workflow receipt 記下 recheck input,預算計算就悄悄變了,而新測試用的是手寫 fixture,不是 workflow 實際送出的 external_id,擋不住。見 inline。

仍開

  • P2 #9:PR 描述自相矛盾。 中段仍是「gateClass: R,R gate pending … 不能宣稱 merge-ready」,最後一段是 owner 豁免。沒動。這是合併紀錄,請收成一個結論。
  • P3 #10:findDeliveredReview(github.ts:953)與 runGithub 的 previousVerdict && previousCheckOwned 仍是兩份同一 predicate。
  • P3 #2:Limits 還沒寫切換 App 的第一輪會另開新 review。
  • P3 #3:findPreviousReview 的 TRUST BOUNDARY 註解仍指錯閘門。
  • P3 #4:Validation 仍停在 35991e2。豁免後合併依據就是 CI,請補 9a1b53c 的 needlefish-ci 連結。
  • P3 #5:receipt 三份解析不一致(TS/workflow JS/reconcile jq)。#12 讓這件事多一個維度,follow-up 時一起收。
  • P3 #7:github.test.ts same-head fixture 共用 roundId 且帶已移除的 recoveryAttempt: 0。
  • P3 #8:docs/reference.md:140 超長行。

CI:head 9a1b53c 上 needlefish-ci in_progress、review queued。上一個 head 9d2c41c 的 Needlefish 是 changes_requested(就是本 delta 修的那條),label 仍是 needlefish:changes-requested,mergeable_state=blocked。沒在 box 上重跑,以 CI 為準。

結論:修法對症,程式面沒有新 blocker。合併條件不變:#9 改描述,加上豁免段落自己定的這個 head needlefish-ci 綠、Needlefish LGTM。#12 不擋。

— 老審

Comment thread src/adapters/github.ts
if (!isRecord(check.app) || check.app.id !== pending.appId || check.head_sha !== headSha || check.name !== "Needlefish") continue;
const prior = parseCheckRound(check.external_id);
// A workflow receipt is not a review attempt, so it spends no recovery budget.
if (!prior || prior.prNumber !== prNumber || prior.recheck === undefined) continue;

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

#12(P3):recheck === undefined 當成「workflow receipt」的判別,靠的是 review.yml:192/:541 和 action.yml:100 三個 jq 都剛好沒寫 recheck。這個約定只寫在這裡跟 CheckRound 的註解,writer 那邊沒有任何提示。

二選一就夠:

  • 在三個 jq writer 旁各加一行註解「不要加 recheck:缺席代表非 review attempt,github.ts 的 recovery 計數靠它」;或
  • 把測試的 receipt 改成從 workflow shell 測試實際送出的 external_id 取(fake gh 的 POST log 已經有),讓 writer 一改就紅。

長期更乾淨的是 v2 加明確欄位(例如 source: "workflow"),但那要動 reconcile 的 jq 驗證,留給 #5 的 follow-up。

@frankekn frankekn left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

老審 staff review @ 69df42c(delta 9a1b53c..69df42c)

一個 commit:69df42c 把 explain 與 review 的「已交付」判斷收成 deliveredVerdict(github.ts:1531)。

本 delta 關掉的

  • P3 #10:已修。 findDeliveredReview(github.ts:954)和 runGithub(github.ts:1716)現在走同一個 predicate。我核過行為等價:舊版對 v1 或 check 不屬於 receipt App 的 state 仍會算出非 null 的 previousVerdict,但它只在 1718–1720 用到,且同時要求 prev,而 prev 舊版本來就要 previousCheckOwned。所以沒有任何路徑的結果改變。findDeliveredReview 的 v !== 2 前置檢查和 helper 內重複,但前者兼做 previous null guard,不用動。純重構、沒改測試,可以接受,既有 same-head skip/explain 測試會覆蓋。

本 delta 新增:無。

仍開

  • P2 #9:PR 描述自相矛盾。 中段仍是「gateClass: R,R gate pending … 不能宣稱 merge-ready」,最後一段是 owner 豁免。這輪還是沒動,請收成一個結論。
  • P3 #12:「是不是 review attempt」靠 workflow writer 沒寫 recheck 判斷,仍沒有註解或用真實 external_id 的測試。
  • P3 #4:Validation 仍停在 35991e2,請補 69df42c 的 needlefish-ci 連結。
  • P3 #2、#3、#5、#7、#8:同上輪,沒動。

CI:head 69df42c 上 needlefish-ci in_progress,Needlefish review check 還沒出現。label 仍是 needlefish:changes-requested,mergeable_state=blocked。沒在 box 上重跑,以 CI 為準。

結論:程式面沒有 blocker。合併條件不變:#9 改描述,這個 head needlefish-ci 綠,加上 Needlefish LGTM。

— 老審

@github-actions

Copy link
Copy Markdown

Needlefish re-review @ 69df42c — ❔ 1 undetermined · ❌ 0 still open · 🆕 0 new → NEEDS HUMAN
NEEDS HUMAN 👀 — The new reconciled POST read loop has no source-defined caller deadline or per-read timeout, so aggregate timeout behavior cannot be computed from this surface.

@github-actions github-actions Bot added needlefish:needs-human Needlefish review verdict and removed needlefish:changes-requested Needlefish review verdict labels Oct 18, 2026
@frankekn
frankekn merged commit ad22eff into main Oct 19, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needlefish:needs-human Needlefish review verdict

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant