Skip to content

fix(apply): revalidate same-author pairs before closing - #1499

Merged
steipete merged 2 commits into
mainfrom
triage/paired-close-recovery-20260908
Sep 8, 2026
Merged

steipete merged 2 commits into
mainfrom
triage/paired-close-recovery-20260908

Conversation

@steipete

@steipete steipete commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

What Problem This Solves

A same-author issue/PR pair can pass candidate admission, then drift before the final close. Main reuses earlier pair and close-policy facts, allowing the parent to close even when the counterpart has locked, reopened, or become unreadable.

Why This Change Was Made

Share the existing close-reason evaluator between candidate and terminal checks. Keep the candidate prefilter, but bypass its policy and generation caches before the closeout note and final close mutation. Revalidate known counterpart identities even when a prior snapshot was closed, and retain known pair context when a refresh fails.

This is a current-main repair of #1042, preserving @vincentkoc's contribution. The original branch does not allow maintainer edits. Existing managed-locale protections, lease guards, source snapshots, and implementation-provenance issue-first/archive sequencing remain in force. Changelog credit is collected in the final notes PR.

User Impact

The current item stays open if its known counterpart is no longer eligible or cannot be revalidated. Stable pairs preserve their existing processing order. This does not make two GitHub mutations atomic: a remote change after the last read or a later independent mutation failure remains possible.

Real Behavior Proof

Claim and surface: the real compiled apply-decisions CLI checks the counterpart again after its actual parent closeout-note write and before closing. The candidate is compared with baseline 2690dafa8c3382d05e733af9dfdc31c804b7305d (the reverted main tree equals the previously proved baseline) using the same stateful fixture.

Command and environment: Node 24.20.0 on macOS; pnpm run build; node docs/proof/paired-close-drift/run-proof.mjs. Private items, closed, reports, plans, canonical baselines and runtime directories; selected synthetic item numbers; --skip-dashboard. The native GitHub CLI 2.100.0 uses its documented http_unix_socket setting to send real REST and GraphQL requests to a private local HTTP service. Only a synthetic token is supplied, and an unreachable external proxy prevents fallback network access. Its synthetic repository namespace is only a fixture; no external repository is read or changed.

Scenario after the parent closeout note Main Candidate
Stable pair Closes both items Closes both items in the same order
Counterpart locks Closes parent No close; skipped_same_author_pair
Counterpart refresh fails Closes parent No close; preserves identity and reports failed revalidation
Previously closed counterpart reopens Closes parent No close; rechecks the reopened counterpart

Artifact: runnable driver, local API service and instructions in docs/proof/paired-close-drift/; the driver writes per-case mutation traces, apply reports and a compact summary.json.

Limits: native gh and actual HTTP request/response handling over a Unix socket, with a local server modeling GitHub data. This does not prove TLS, live GitHub availability, or production authorization; no live GitHub mutation occurs. The unreadable-counterpart scenario eventually exits nonzero when the CLI independently processes that unreadable issue; its partial report and mutation trace prove that the parent was kept open. Current v2 activity cursors and general pair processing are exercised directly; the implementation-linked issue-first path retains focused regression coverage. This is eligibility revalidation, not a remote transaction or rollback guarantee.

Evidence

The build, all 156 focused policy/pair/apply tests, and the eight baseline/candidate runtime cases pass. Local Codex autoreview is clean through P2. Prepared head: 43db4edcf22e21afab44b81ba15ccd92be91a824. Full local pnpm check passed: 5,639 passed, 13 skipped, zero failures, plus static, build, lint and coverage gates. The preceding production head passed full CI: https://github.com/openclaw/clawsweeper/actions/runs/34247581665. Current-head CI is available in this PR’s Checks. The native proof update passed local Codex autoreview through P2; the unchanged production guards were already reviewed clean.

The earlier author-budget review allegation was withdrawn: pair admission is PR-to-issue only, the live counterpart must match the issue kind, and the unchanged capacity owner rejects author-budget closes on issues. The parent PR still uses its run-aware gate. No unnecessary budget-policy change was made.

OpenClaw Bay Impact

None. This changes internal apply eligibility and mutation sequencing, with no public status, observer schema, or Bay action controls.

Native Client Proof Upgrade

The replacement gh.cjs executable has been deleted. The proof now launches the unmodified /opt/homebrew/bin/gh 2.100.0 and a separate local HTTP API process. Native gh serializes label and PR-close GraphQL mutations and issue-close REST requests, parses server responses, and reports HTTP 422 for the unreadable counterpart. The fixture supplies current v2 review-activity cursors.

All eight baseline/candidate scenarios completed with the expected result. In the three drift cases the candidate trace contains zero close mutations and zero native closePullRequest requests; the baseline sends the parent close. Both stable runs contain native PR-close GraphQL and issue-close REST requests. The main PR body now describes this stronger proof; the former replacement-client limitation is resolved.

The final fixture also rejects unknown GraphQL target/label IDs and non-GET requests to read-only routes. Native negative controls assert failure and unchanged fixture state. Every gh invocation—including version and negative probes—uses a disposable HOME, XDG state and configuration directory, with telemetry disabled. A repeated local full check had one transient maintenance-fixture failure (empty stderr for invalid arguments); that unrelated test passed its isolated retry with a fresh private temporary directory. No assertions or timeouts were weakened.

Revalidate current close policies and known same-author counterparts immediately before close mutations. Preserve pair identity on failed refresh, re-evaluate reopened counterparts, and retain current lease, source, managed-locale and implementation-provenance guards.

Supersedes #1042. This guards eligibility without claiming atomic remote closes.

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
@clawsweeper

clawsweeper Bot commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

ClawSweeper review complete

ClawSweeper finished reviewing this revision. The review result is being finalized.

View the workflow run.

@steipete

steipete commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Head 03493691c1f2387a1b00322d352c553ecb5b7283 is the current-main replacement for #1042, with Vincent Koc credited. Local and committed-branch Codex autoreview are clean through P2. pnpm run check passed: 5,639 passed, 13 skipped, zero failures. All 156 focused regressions passed.

The actual compiled apply-decisions CLI was run against a strict synthetic transport with no live-gh fallback. Main closed the parent after counterpart locking, failed refresh, and reopening; this head suppressed all three closes with explanatory pair skips. Both versions closed the stable pair in the same order. The unreadable-counterpart scenario's later nonzero exit is expected and retains the earlier parent skip in its partial report. The executable proof and limits are in the PR body; no real GitHub item was mutated.

Exact-head CI: https://github.com/openclaw/clawsweeper/actions/runs/34247581665. The PR body records the full proof and the withdrawn, unreachable author-budget allegation. This is eligibility revalidation, not atomic remote closure.

@clawsweeper

clawsweeper Bot commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

🦞👀
Exact review queued.

Re-review progress:

@steipete

steipete commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

The prior review request could not reach review because main's early sparse checkout hid the setup-pnpm local action. #1498 has now reverted that regression. Please review this unchanged prepared head and current body; the runtime proof and clean independent review are already recorded there. This requests review only.

@clawsweeper

clawsweeper Bot commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event exact_review_queue).
Result: when the review finishes, ClawSweeper will create the durable review comment if needed or update the existing comment in place.

Re-review progress:

@clawsweeper clawsweeper Bot added P2 Normal priority bug or improvement with limited blast radius. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Sep 8, 2026
@clawsweeper

clawsweeper Bot commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed September 8, 2026, 12:45 PM ET / 16:45 UTC.

ClawSweeper review

What this changes

Rechecks close eligibility and same-author issue/PR counterparts immediately before closing, with regression coverage and a native GitHub CLI proof harness.

Merge readiness

✅ Ready for maintainer review

This repair remains necessary on current main. No blocking patch defect was found, and the native-client evidence resolves the previous proof blocker.

Priority: P2
Reviewed head: 43db4edcf22e21afab44b81ba15ccd92be91a824

Review scores

Measure Result What it means
Overall readiness 🐚 platinum hermit (4/6) A focused safety repair with sufficient native-client proof and no identified blocking defect.
Proof confidence 🐚 platinum hermit (4/6) Sufficient (terminal): The captured macOS evidence exercises the compiled apply CLI and native gh through actual HTTP serialization: stable pairs close, while three post-note drift scenarios produce no candidate close mutations. The inspected driver matches those observations and resolves the previous replacement-client limitation; live GitHub authorization and remote atomicity are outside the claim.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Verified Sufficient (terminal): The captured macOS evidence exercises the compiled apply CLI and native gh through actual HTTP serialization: stable pairs close, while three post-note drift scenarios produce no candidate close mutations. The inspected driver matches those observations and resolves the previous replacement-client limitation; live GitHub authorization and remote atomicity are outside the claim.
Evidence reviewed 8 items Current main still caches pair admission: The fetched main revision retains sameAuthorPairStartCloseable and returns its cached result. The complete tree comparison against the pinned merge base was empty, corroborating the proof’s baseline equivalence.
Fresh policy checks reach the mutation boundary: The shared evaluator clears guard caches and bypasses generation caching; the mutation runner invokes the final close-policy guard before the operation, including the path without a ledger attempt. The existing close owner supplies matching item_close identities for both PR and issue mutations.
Counterpart identity survives refresh failures: Known relationships are retained for terminal checks. Counterpart refresh validates number, kind, author and state, including previously closed counterparts; unreadable or changed identities block closure.
Findings None None.
Security None None.

How this fits together

ClawSweeper’s apply lane turns reviewed closure proposals into GitHub mutations. These guards compare proposals with current item and counterpart state before allowing a close.

flowchart TD
  A[Reviewed closure proposal] --> B[Candidate eligibility]
  B --> C[Fresh counterpart and policy checks]
  C --> D[Closeout note]
  D --> E[Final mutation guards]
  E -->|Eligible| F[GitHub close and archive]
  E -->|Changed or unreadable| G[Keep item open]
Loading

Before merge

None.

Agent review details

Security

None.

Review metrics

Metric Value Why it matters
Production and validation growth Production +117 net lines; tests +283; proof harness +689 Production growth implements shared terminal validation; most additions support regression coverage and native-client proof.
Recorded runtime scenarios 4 scenarios × 2 revisions The comparison demonstrates stable closure alongside three distinct post-admission drift cases.

Root-cause cluster

Relationship: canonical
Canonical: #1499
Summary: This is the current repair candidate for cached same-author pair eligibility; the earlier draft addresses the same mechanism.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Technical review

Best possible solution:

Keep candidate checks inexpensive while enforcing fresh eligibility at the final close boundary, preserving independent issue archival and documented non-atomic semantics.

Do we have a high-confidence way to reproduce the issue?

Yes. Current-main source retains cached pair admission, and the contributor records before/after runs injecting counterpart drift after the closeout note; this review did not execute them.

Is this the best way to solve the issue?

Yes. Reusing the existing policy evaluator and refreshing known counterparts at mutation boundaries addresses the stale decision without adding a competing policy or changing pair ordering.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning medium; reviewed against 2690dafa8c33.

Labels

Label changes:

  • add proof: sufficient: Contributor real behavior proof is sufficient. The captured macOS evidence exercises the compiled apply CLI and native gh through actual HTTP serialization: stable pairs close, while three post-note drift scenarios produce no candidate close mutations. The inspected driver matches those observations and resolves the previous replacement-client limitation; live GitHub authorization and remote atomicity are outside the claim.
  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🐚 platinum hermit. Replaced prior rating: 🦪 silver shellfish.
  • remove status: 📣 needs proof: Current PR status no longer selects a status label.
  • remove rating: 🦪 silver shellfish: Current PR rating is rating: 🐚 platinum hermit, so this older rating label is no longer current.

Label justifications:

  • P2: This repairs a bounded apply-automation defect that can close a parent after its counterpart becomes ineligible.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🐚 platinum hermit. Replaced prior rating: 🦪 silver shellfish.
  • proof: sufficient: Contributor real behavior proof is sufficient. The captured macOS evidence exercises the compiled apply CLI and native gh through actual HTTP serialization: stable pairs close, while three post-note drift scenarios produce no candidate close mutations. The inspected driver matches those observations and resolves the previous replacement-client limitation; live GitHub authorization and remote atomicity are outside the claim.

Evidence

What I checked:

  • Current main still caches pair admission: The fetched main revision retains sameAuthorPairStartCloseable and returns its cached result. The complete tree comparison against the pinned merge base was empty, corroborating the proof’s baseline equivalence. (src/clawsweeper-apply-close-guards.ts:316, 2690dafa8c33)
  • Fresh policy checks reach the mutation boundary: The shared evaluator clears guard caches and bypasses generation caching; the mutation runner invokes the final close-policy guard before the operation, including the path without a ledger attempt. The existing close owner supplies matching item_close identities for both PR and issue mutations. (src/clawsweeper-apply-decision-workflow.ts:668, 43db4edcf22e)
  • Counterpart identity survives refresh failures: Known relationships are retained for terminal checks. Counterpart refresh validates number, kind, author and state, including previously closed counterparts; unreadable or changed identities block closure. (src/clawsweeper-context-hydration.ts:886, 43db4edcf22e)
  • Native-client proof resolves prior review: The captured PR body reports eight baseline/candidate runs on macOS with Node 24.20.0 and native gh 2.100.0. The compiled apply CLI sends real HTTP requests over a private Unix socket: stable pairs close in order, while locking, refresh failure and reopening after the parent note suppress candidate close mutations. The inspected driver checks post-note reads and mutation traces. Production source is unchanged from the earlier reviewed head. Evidence was assessed from the supplied snapshot and source; this read-only review did not execute the harness. (docs/proof/paired-close-drift/run-proof.mjs:218, 43db4edcf22e)
  • CodeQL observation is confined to the private fixture: The review annotation targets the proof server’s error response. Stack details are written to a private fixture log; the server listens on the disposable Unix socket and receives synthetic data without inherited credentials. No production information-exposure path was established. (docs/proof/paired-close-drift/api-server.cjs:339, 43db4edcf22e)
  • Related work and release boundary: fix(apply): prevent one-sided paired closes after live drift #1042 remains an open, conflicting draft addressing the same pair-drift mechanism; this PR explicitly preserves that contribution. The supplied merged revert: retry control-plane 5xx and propagate retryable admission failures (#1497) #1498 restores unrelated workflow behavior, not this repair. The latest release’s tree lacks the extracted close-guards file, so that lookup does not establish historical release behavior or a shipped fix.

Likely related people:

  • brokemac79: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)
  • steipete: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

Workflow

  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

History

Review history (2 earlier review cycles)
  • reviewed 2026-09-08T16:08:58.673Z sha 0349369 :: needs real behavior proof before merge. :: none
  • reviewed 2026-09-08T16:13:29.680Z sha 0349369 :: needs real behavior proof before merge. :: none

Comment thread docs/proof/paired-close-drift/api-server.cjs Dismissed
@steipete

steipete commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

The native-client proof is committed at 43db4edcf22e21afab44b81ba15ccd92be91a824 and has been rerun from that exact head. Local and committed-branch Codex autoreview are clean through P2.

node docs/proof/paired-close-drift/run-proof.mjs ran the built apply CLI with unmodified gh 2.100.0 over its documented HTTP Unix-socket transport. The old replacement gh executable is deleted. The API service validates methods, repository/item selectors and mutation IDs; native negative controls prove invalid target IDs, unknown label IDs and POST-to-search are rejected without state changes. Every gh process has a disposable home/state/config and disabled telemetry.

Scenario Baseline close requests Candidate close requests
Stable PR and issue PR and issue
Counterpart locks Parent PR None
Counterpart refresh returns HTTP 422 Parent PR None
Counterpart reopens Parent PR None

The traces contain actual native REST and GraphQL requests, including the successful stable close mutations. Current v2 review-activity cursors are used. The unreadable counterpart's later standalone processing exits nonzero as expected; the parent skip is preserved in the partial report. No live GitHub item was mutated.

The earlier full local check passed with 5,639 tests passing and 13 skipped. The repeated proof-update check had one transient maintenance-fixture diagnostic failure; its isolated retry passed unchanged with a fresh private temporary directory. No assertion or timeout was weakened. Exact-head CI: https://github.com/openclaw/clawsweeper/actions/runs/34252522153.

The current PR body records the proof, limits, and resolved fixture findings. The current ClawSweeper review is already running; this comment records proof without requesting a duplicate run.

@steipete
steipete merged commit 3ccf9d2 into main Sep 8, 2026
19 checks passed
@steipete
steipete deleted the triage/paired-close-recovery-20260908 branch September 8, 2026 16:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P2 Normal priority bug or improvement with limited blast radius. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants