Skip to content

ORB-106: Wire sleep orchestration to strict merge sweep - #625

Merged
thomasluizon merged 20 commits into
mainfrom
feature/orb-106-make-orchestrate-sleep-call-the-merge
Jul 28, 2026
Merged

ORB-106: Wire sleep orchestration to strict merge sweep#625
thomasluizon merged 20 commits into
mainfrom
feature/orb-106-make-orchestrate-sleep-call-the-merge

Conversation

@thomasluizon

@thomasluizon thomasluizon commented Jul 28, 2026

Copy link
Copy Markdown
Owner

Summary

Implements ORB-106 by making /orchestrate --sleep pass its gated headRefOid into the strict sweep:

bash tools/merge-sweep.sh --expected-head <pr-number>=<expected-head-sha> <owner/repo> <pr-number>

Both sweep scripts now accept repeated per-PR expected-head mappings. A bare invocation remains supported and captures each PR head at entry as its expectation. The scripts re-read the head after branch updates and on every poll, emit a distinct HEAD-MOVED skip with both SHAs, and use GitHub's atomic --match-head-commit assertion at the merge call. A last-moment atomic refusal is followed by another head read so the same greppable reason is reported.

The strict script remains the only sleep path. merge-sweep-cov.sh remains an attended choice outside --sleep because its coverage-only path uses --admin.

Condition mapping

Previous condition Script coverage Disposition
reviewDecision is APPROVED Covered. Merge requires APPROVED; the current-head review check must settle first when the workflow exists or workflow lookup is unreadable. Moved to the strict tool.
Every check concluded and none failed Covered. The tool rejects failed conclusions and waits until every status check, required or not, reaches a terminal result. Enforced by the strict tool and retained as a skill preflight.
Update BEHIND, then require CLEAN Partial. The skill updates first and hands off only from CLEAN. The script accepts CLEAN or UNSTABLE, but now only for the exact pinned head. Update ordering and CLEAN handoff remain skill-owned; head identity is tool-enforced.
D7 evidence is satisfied Not covered. Retained as a skill-owned preflight.
No attempts:2 label Not covered. Retained as a skill-owned D9 preflight.
Every reviewer comment is reconciled and no unresolved thread remains Not covered. Retained as a skill-owned preflight and repeated after any head change.

Round 4 decision-path evidence

The real scripts ran against a hermetic gh stub. No GitHub PR was updated or merged.

PASS merge-sweep.sh: matching expected head merges
PASS merge-sweep.sh: changed head skips and names both SHAs
PASS merge-sweep.sh: atomic merge refusal reports a last-moment head change
PASS merge-sweep.sh: invocation without expected head still merges
PASS merge-sweep-cov.sh: matching expected head merges
PASS merge-sweep-cov.sh: changed head skips and names both SHAs
PASS merge-sweep-cov.sh: atomic merge refusal reports a last-moment head change
PASS merge-sweep-cov.sh: invocation without expected head still merges

The coverage matching and atomic-refusal cases exercise the coverage-only --admin path. The matching cases also assert the exact --match-head-commit <expected-sha> argument.

Updated help contracts

$ bash tools/merge-sweep.sh --help
Require-up-to-date merge sweep: squash-merge each APPROVED, green PR server-side.

Usage: merge-sweep.sh <owner/repo> <pr-number>...
       merge-sweep.sh [--expected-head <pr-number>=<sha>]... <owner/repo> <pr-number>...
       merge-sweep.sh --help

Per PR it update-branches and polls until the merge state is decidable, then squash-merges.
A SonarCloud failure counts as a failed check here and SKIPs; use merge-sweep-cov.sh when a
new-code-coverage-only Sonar failure should be admin-overridden instead.

The expected head defaults to the PR's head SHA at entry. Pass --expected-head once per PR to
pin an earlier observed SHA. If update-branch or any later poll sees a different head, that PR
prints HEAD-MOVED and is never merged. The merge API also atomically matches the expected SHA.

It refuses to merge while the `review` check for the CURRENT head SHA is still
running, and re-reads reviewDecision after that check settles, so a pre-update APPROVED can
never carry a merge. Only a workflow lookup that succeeds and shows no .github/workflows/claude-review.yml
skips that wait; a failed lookup keeps the guard on.

After the sweep it re-checks every merged PR's head branch. A branch whose tip moved past the
SHA that was merged carries a post-merge commit that never reached main.

Output (stdout): one MERGED/SKIP/MERGE-REFUSED line per PR, then any ORPHANED-HEAD lines, then
SWEEP-DONE.
Exit codes: 0 every merged head verified clean; 1 at least one orphaned head branch; 2 bad usage;
3 a head branch could not be verified (unknown is not a clean pass).
Exit 0

$ bash tools/merge-sweep-cov.sh --help
Coverage-aware merge sweep: squash-merge each APPROVED, green PR server-side.

Usage: merge-sweep-cov.sh [--expected-head <pr-number>=<sha>]... <owner/repo> <pr-number>...
       merge-sweep-cov.sh --help

Per PR it update-branches, polls until the merge state is decidable, then merges:
  Sonar SUCCESS or absent           -> squash merge
  Sonar FAILURE, new-code coverage  -> admin squash merge (coverage-only override)
  Sonar FAILURE, anything else      -> SKIP

An --expected-head mapping pins that PR to the supplied head SHA. Without one, the current
head SHA is captured before update-branch. Any later head change skips the PR, and the merge
API atomically matches the expected SHA.

It refuses to merge while the `review` check for the CURRENT head SHA is still
running, and re-reads reviewDecision after that check settles, so a pre-update APPROVED can
never carry a merge. Only a workflow lookup that succeeds and shows no .github/workflows/claude-review.yml
skips that wait; a failed lookup keeps the guard on.

After the sweep it re-checks every merged PR's head branch. A branch whose tip moved past the
SHA that was merged carries a post-merge commit that never reached main.

Output (stdout): one MERGED/SKIP/FAIL-ADMIN line per PR, then any ORPHANED-HEAD lines, then
COV-SWEEP-DONE.
Exit codes: 0 every merged head verified clean; 1 at least one orphaned head branch; 2 bad usage;
3 a head branch could not be verified (unknown is not a clean pass).
Exit 0

Gate output

$ npm run lint
Tasks: 3 successful, 3 total
Cached: 3 cached, 3 total
Exit 0 (0 errors; existing warnings replayed by Turbo)

$ npm run type-check
Tasks: 3 successful, 3 total
Cached: 3 cached, 3 total
Exit 0

$ npm run test
Tasks: 4 successful, 4 total
Cached: 4 cached, 4 total
Exit 0

$ node tools/test-tools.mjs
ORBIT TOOLS GATE OK
Exit 0

$ node .claude/hooks/test-hooks.mjs
ORBIT HOOK PARITY OK
Exit 0

Reconciliation notes

  • Normal per-PR skips still exit 0; callers must use the per-PR MERGED, SKIP, MERGE-REFUSED, or FAIL-ADMIN line. Exit 1 remains reserved for an orphaned merged head.
  • The skill's preflight remains stricter than the script's merge-state predicate: handoff requires CLEAN, while the pinned-head script accepts CLEAN or UNSTABLE after rejecting failed conclusions.
  • No visible-effect gate applies. This changes orchestration tooling and its skill, not an app surface.

Decisions taken unattended

  • Used repeated --expected-head <pr>=<sha> mappings so multi-PR callers can pin every candidate while the existing bare positional form remains valid.
  • Kept the strict script as the only --sleep path. The coverage script receives the same head protection but remains an attended tool because it can use --admin.
  • Kept the post-merge expected-head comparison in the skill as a second assertion after the preventive server-side match.
  • Preserved the existing complete tools catalog; no tools/README.md row was missing.

Round 5 merge integration

Merged current origin/main at 58431c61 into the branch as merge commit 89ff7881. The live conflict was in tools/test-tools.mjs; the resolution keeps both ORB-121's ISSUES_WAVE_STUB and explicit --issues cases and ORB-106's expected-head merge-sweep cases. The auto-merged orchestrate skill retains both scope systems.

GitHub now reports mergeStateStatus: BLOCKED, not DIRTY, on head 89ff7881a127aa5ef99d52e223b7d8ede34f12b2.

Context budget evidence

CLAUDE.md: 11828 bytes
.claude/rules/core.md: 3380 bytes
recorded total: 15208 bytes
measured total: 15208 bytes
delta: 0
status: ok

ORB-106 does not change either measured file, so no reseed or context:reseed label applies.

Round 5 gate output

$ node tools/test-tools.mjs
PASS wave-plan.mjs: uses an out-of-set team blocker while displaying only requested issues
PASS wave-plan.mjs: restricts text output to requested identifiers with their blocker state
PASS check-context-budget.mjs: total under baseline exits 0
ORBIT TOOLS GATE OK
Exit 0

$ node .claude/hooks/test-hooks.mjs
PASS docs: every named .claude/hooks/*.mjs path resolves
PASS linear gate: a mutation in a different chunk from the endpoint still blocks
ORBIT HOOK PARITY OK
Exit 0

$ npm run lint
Tasks: 3 successful, 3 total
Exit 0

$ npm run type-check
Tasks: 3 successful, 3 total
Exit 0

$ npm run test
Tasks: 4 successful, 4 total
Exit 0

Round 6 review safety gate

Merged origin/main at 58f40476 through merge commit fa657cd4, preserving ORB-119's sibling-import removal and context baseline. The ORB-106 fix commit is be78cbb6.

Both sweep scripts now require a per-PR --reviewed-through reconciliation boundary before merge. Immediately before every strict, normal coverage, or admin coverage merge path, they fail closed on unreadable review data, refuse unresolved threads, and refuse reviews or issue comments newer than the caller's boundary with the author and timestamp named.

The orchestrate skill records the reconciliation boundary beside the gated head and passes both values into the strict sweep.

Context budget evidence

CLAUDE.md: 11683 bytes
.claude/rules/core.md: 3380 bytes
recorded total: 15063 bytes
measured total: 15063 bytes
delta: 0
status: ok

ORB-106 does not change either measured file, so no reseed or context:reseed label applies.

Round 6 gate output

$ node tools/test-tools.mjs
PASS merge-sweep.sh: matching expected head and clean review lookups merge
PASS merge-sweep.sh: unresolved review threads skip without merging
PASS merge-sweep.sh: a newer review skips without merging
PASS merge-sweep.sh: a newer issue comment skips without merging
PASS merge-sweep.sh: review-thread lookup failure fails closed by name
PASS merge-sweep.sh: reviews lookup failure fails closed by name
PASS merge-sweep.sh: issue-comments lookup failure fails closed by name
PASS merge-sweep.sh: a missing reviewed-through mapping fails closed
PASS merge-sweep-cov.sh: matching expected head and clean review lookups merge
PASS merge-sweep-cov.sh: unresolved review threads skip without merging
PASS merge-sweep-cov.sh: a newer review skips without merging
PASS merge-sweep-cov.sh: a newer issue comment skips without merging
PASS merge-sweep-cov.sh: review-thread lookup failure fails closed by name
PASS merge-sweep-cov.sh: reviews lookup failure fails closed by name
PASS merge-sweep-cov.sh: issue-comments lookup failure fails closed by name
PASS merge-sweep-cov.sh: a missing reviewed-through mapping fails closed
PASS wave-plan.mjs: uses an out-of-set team blocker while displaying only requested issues
PASS wave-plan.mjs: restricts text output to requested identifiers with their blocker state
ORBIT TOOLS GATE OK
Exit 0

$ node .claude/hooks/test-hooks.mjs
PASS docs: every named .claude/hooks/*.mjs path resolves
PASS linear gate: a mutation in a different chunk from the endpoint still blocks
ORBIT HOOK PARITY OK
Exit 0

$ npm run lint
Tasks: 3 successful, 3 total
Exit 0

$ npm run type-check
Tasks: 3 successful, 3 total
Exit 0

$ npm run test
Tasks: 4 successful, 4 total
Mobile: 247 files passed, 1611 tests passed
Shared: 127 files passed, 1599 tests passed
ESLint rules: 2 files passed, 190 tests passed
Exit 0

Round 7 inline activity gate

Fix commit 30aca557 closes the remaining reviewed-through endpoint gap. Both sweep scripts now paginate review submissions, inline review comments, and conversation comments. Inline and conversation comments contribute both created_at and updated_at, so an edit after the cutoff is newer activity. Every lookup fails closed; newer inline activity reports the endpoint, author, and timestamp.

Round 7 gate output

$ node tools/test-tools.mjs
PASS merge-sweep.sh: a newer inline comment on a resolved thread skips without merging
PASS merge-sweep.sh: an older inline comment still merges
PASS merge-sweep.sh: an inline comment edited after the cutoff skips by updated time
PASS merge-sweep.sh: inline-comment lookup failure fails closed by name
PASS merge-sweep.sh: pagination sees a newer inline comment on page two
PASS merge-sweep-cov.sh: a newer inline comment on a resolved thread skips without merging
PASS merge-sweep-cov.sh: an older inline comment still merges
PASS merge-sweep-cov.sh: an inline comment edited after the cutoff skips by updated time
PASS merge-sweep-cov.sh: inline-comment lookup failure fails closed by name
PASS merge-sweep-cov.sh: pagination sees a newer inline comment on page two
ORBIT TOOLS GATE OK
Exit 0

$ node .claude/hooks/test-hooks.mjs
PASS docs: every named .claude/hooks/*.mjs path resolves
PASS linear gate: a mutation in a different chunk from the endpoint still blocks
ORBIT HOOK PARITY OK
Exit 0

$ npm run lint
Tasks: 3 successful, 3 total
Exit 0

$ npm run type-check
Tasks: 3 successful, 3 total
Exit 0

$ npm run test
Tasks: 4 successful, 4 total
Mobile: 247 files passed, 1611 tests passed
Shared: 127 files passed, 1599 tests passed
ESLint rules: 2 files passed, 190 tests passed
Exit 0

Round 8 exclusive cutoff

Fix commit 4fc4e393 closes the same-second boundary. Both sweep scripts now treat --reviewed-through as an exclusive cutoff: activity at or after the timestamp is new. The shared comparison uses >=, so the rule applies to review submissions, inline comment creation and edits, and conversation comment creation and edits. The help contracts state the boundary explicitly.

The harness proves equal, strictly-after, and strictly-before timestamps independently for all three endpoints and for both sweep scripts. All Round 7 cases remain green.

Round 8 gate output

$ bash tools/merge-sweep.sh --help
The cutoff is exclusive: activity at or after that timestamp counts as new.
Exit 0

$ bash tools/merge-sweep-cov.sh --help
The cutoff is exclusive: activity at or after that timestamp counts as new.
Exit 0

$ node tools/test-tools.mjs
PASS merge-sweep.sh: help documents the exclusive reviewed-through cutoff
PASS merge-sweep.sh: reviews exactly at reviewed-through skip without merging
PASS merge-sweep.sh: reviews strictly after reviewed-through skip without merging
PASS merge-sweep.sh: reviews strictly before reviewed-through still merge
PASS merge-sweep.sh: inline comments exactly at reviewed-through skip without merging
PASS merge-sweep.sh: inline comments strictly after reviewed-through skip without merging
PASS merge-sweep.sh: inline comments strictly before reviewed-through still merge
PASS merge-sweep.sh: conversation comments exactly at reviewed-through skip without merging
PASS merge-sweep.sh: conversation comments strictly after reviewed-through skip without merging
PASS merge-sweep.sh: conversation comments strictly before reviewed-through still merge
PASS merge-sweep-cov.sh: help documents the exclusive reviewed-through cutoff
PASS merge-sweep-cov.sh: reviews exactly at reviewed-through skip without merging
PASS merge-sweep-cov.sh: reviews strictly after reviewed-through skip without merging
PASS merge-sweep-cov.sh: reviews strictly before reviewed-through still merge
PASS merge-sweep-cov.sh: inline comments exactly at reviewed-through skip without merging
PASS merge-sweep-cov.sh: inline comments strictly after reviewed-through skip without merging
PASS merge-sweep-cov.sh: inline comments strictly before reviewed-through still merge
PASS merge-sweep-cov.sh: conversation comments exactly at reviewed-through skip without merging
PASS merge-sweep-cov.sh: conversation comments strictly after reviewed-through skip without merging
PASS merge-sweep-cov.sh: conversation comments strictly before reviewed-through still merge
ORBIT TOOLS GATE OK
Exit 0

$ node .claude/hooks/test-hooks.mjs
PASS docs: every named .claude/hooks/*.mjs path resolves
PASS linear gate: a mutation in a different chunk from the endpoint still blocks
ORBIT HOOK PARITY OK
Exit 0

$ npm run lint
Tasks: 3 successful, 3 total
Exit 0

$ npm run type-check
Tasks: 3 successful, 3 total
Exit 0

$ npm run test
Tasks: 4 successful, 4 total
Mobile: 247 files passed, 1611 tests passed
Shared: 127 files passed, 1599 tests passed
ESLint rules: 2 files passed, 190 tests passed
Exit 0

Round 9 verified update and residual race handling

Fix commit and head: `ca73cdcd142d0dee46916e5db19541719814d32e`

Both sweep scripts now distinguish their own routine update-branch merge from an unrelated head move. Before update they verify the current head still equals the expected head. After update, a changed head is adopted only when its parents include both the prior expected head and the current base tip. The adopted head is then fully re-gated: all checks must be terminal and successful, the current-head review check must be settled, reviewDecision must be APPROVED, and the review safety gate must pass before the atomic match-head merge.

The orchestrate skill now enumerates all review submissions, inline comments, and conversation comments without author exclusions. It posts reconciliation replies and resolves threads first, mechanically verifies that reconciliation, then captures reviewed-through as the final reconciliation act at an instant strictly later than every reconciled create, edit, or submission timestamp. Because GitHub timestamps have second precision and equality counts as new, the skill waits for the next second when required and immediately invokes the sweep without another review mutation.

The response-to-merge interval cannot be made atomic with GitHub review activity. The pre-merge safety query is therefore the last operation before each merge call, and the same query runs immediately after a successful merge. Activity found in that residual window is reported exactly as:

```text
POST-MERGE-ACTIVITY # at
```

Post-merge activity or an unverifiable post-merge review state sets exit code 4, stops further unattended merges, and is carried into the Merged ledger.

Updated help contract

```text
A routine update-branch merge is adopted only when its parents
include both the prior expected SHA and the current base tip; any other change prints HEAD-MOVED.
The safety query is the last operation before merge and runs again after success. Post-merge
activity or an unverifiable post-merge review state is reported with a URL when available and
exits 4; this detects but cannot prevent the residual response-to-merge race.
Exit codes: 0 every merged head verified clean; 1 at least one orphaned head branch; 2 bad usage;
3 a head branch could not be verified; 4 post-merge review activity or an unverifiable review
state (unknown is not a clean pass).
```

The normal and coverage-aware help output carry the same contract.

Round 9 tools harness

```text
PASS merge-sweep.sh: help documents the exclusive cutoff and residual post-merge window
PASS merge-sweep.sh: a routine update adopts and rechecks the new head before merging
PASS merge-sweep.sh: an adversarial update without the expected parents is rejected
PASS merge-sweep.sh: a failing check on the adopted head skips without merging
PASS merge-sweep.sh: an unsettled current-head review check skips without merging
PASS merge-sweep.sh: a reconciled reply before the refreshed cutoff merges
PASS merge-sweep.sh: activity in the residual merge window is reported after the merge
PASS merge-sweep.sh: genuine third-party activity at the refreshed cutoff skips
PASS merge-sweep-cov.sh: help documents the exclusive cutoff and residual post-merge window
PASS merge-sweep-cov.sh: a routine update adopts and rechecks the new head before merging
PASS merge-sweep-cov.sh: an adversarial update without the expected parents is rejected
PASS merge-sweep-cov.sh: a failing check on the adopted head skips without merging
PASS merge-sweep-cov.sh: an unsettled current-head review check skips without merging
PASS merge-sweep-cov.sh: a reconciled reply before the refreshed cutoff merges
PASS merge-sweep-cov.sh: activity in the residual merge window is reported after the merge
PASS merge-sweep-cov.sh: genuine third-party activity at the refreshed cutoff skips
ORBIT TOOLS GATE OK
Exit 0
```

Round 9 complete gates

```text
$ node tools/test-tools.mjs
ORBIT TOOLS GATE OK
Exit 0

$ node .claude/hooks/test-hooks.mjs
ORBIT HOOK PARITY OK
Exit 0

$ npm run lint
Tasks: 3 successful, 3 total
Exit 0

$ npm run type-check
Tasks: 3 successful, 3 total
Exit 0

$ npm run test
Tasks: 4 successful, 4 total
Mobile: 247 files passed, 1611 tests passed
Exit 0

Post-push:
$ node tools/test-tools.mjs
ORBIT TOOLS GATE OK
Exit 0

$ node .claude/hooks/test-hooks.mjs
ORBIT HOOK PARITY OK
Exit 0
```

The commit hook dash gate and push target guard also passed. No visible-effect screenshot applies because this round changes orchestration tooling and its skill, not an application surface.

Self-driven review: edited review bodies

Fix commit: `d485cb73bf75099744ad5673528d2d94bd044638`
Integrated head: `f2e02ca6070bbe9c8145a014eca710cefe1ad9f4`

Both sweep scripts now enumerate review submissions through the paginated GraphQL reviews connection and evaluate `submittedAt`, `updatedAt`, and `lastEditedAt` with the existing exclusive cutoff. An edit to an already-submitted `COMMENTED` review is therefore new review activity even when its original submission predates `reviewed-through`. Lookup, pagination, or malformed-data failures remain fail-closed as `source=reviews`, and activity reports retain the author, timestamp, and URL.

The repeated lookup and phase-aware failure handling in each script is consolidated into one helper. The orchestrate skill uses the same GraphQL edit timestamps during reconciliation and sets the final cutoff strictly after both submitted and edited review activity, with no author exclusions.

The all-concluded condition is intentionally strict. Every pending status check blocks, including a non-required check, so the implementation stays aligned with the ticket requirement. The earlier condition-mapping row that described this as skill-only has been corrected.

Current `main` at `f1513383` was merged without rebasing. ORB-113's `/ticket` workflow, scope-completeness section, check-ticket cases, and context baseline all remain intact. The final PR diff remains limited to the four ORB-106 files.

Verification

```text
$ node tools/test-tools.mjs
PASS merge-sweep.sh: an already-submitted COMMENTED review edited after the cutoff skips without merging
PASS merge-sweep.sh: a COMMENTED review edited strictly before the cutoff still merges
PASS merge-sweep-cov.sh: an already-submitted COMMENTED review edited after the cutoff skips without merging
PASS merge-sweep-cov.sh: a COMMENTED review edited strictly before the cutoff still merges
PASS check-ticket.mjs: issue mode rejects zero type labels and names every valid value
PASS check-ticket.mjs: a named dependency with its blockedBy relation is accepted
ORBIT TOOLS GATE OK
Exit 0

$ node .claude/hooks/test-hooks.mjs
ORBIT HOOK PARITY OK
Exit 0

$ npm run lint
Tasks: 3 successful, 3 total
Exit 0

$ npm run type-check
Tasks: 3 successful, 3 total
Exit 0

$ npm run test
Tasks: 4 successful, 4 total
Mobile: 247 files passed, 1611 tests passed
Exit 0

$ node tools/check-context-budget.mjs --check --json
enforcedBytes: 15381
baselineBytes: 15381
deltaBytes: 0
status: ok
```

No baseline was edited or reseeded. No visible-effect screenshot applies.

Self-driven review: cutoff before final reads

Fix commit and head: `24543dedf17dc849ef1e2885140e389adcb5dd84`

The reconciliation protocol now captures `reviewed-through` after all required replies and resolutions but before the final validation reads. It retains a complete reconciled activity snapshot, waits for a strictly later GitHub timestamp second, captures the boundary, then re-reads all three activity sources and the unresolved-thread count.

The final snapshot must be byte-for-byte unchanged, no activity may be at or after the boundary, and unresolved threads must remain zero. The snapshot comparison catches activity that arrives while waiting for the next timestamp second but sorts before the boundary; the sweep cutoff catches activity after the final reads. Any difference restarts reconciliation and requires a new boundary. No author exclusion is used.

Required order:

```text
reconciliation mutations
reviewed-through boundary
final snapshot and unresolved-thread validation
strict sweep
```

Verification:

```text
$ node tools/check-dashes.mjs --files .claude/skills/orchestrate/SKILL.md
Exit 0

$ node tools/check-frontmatter.mjs
frontmatter ok
Exit 0

$ node .claude/hooks/test-hooks.mjs
ORBIT HOOK PARITY OK
Exit 0

$ node tools/test-tools.mjs
ORBIT TOOLS GATE OK
Exit 0
```

The isolated tools harness completed in 811.1 seconds after the first parallel run exceeded its 600-second process bound. The timed-out process tree was terminated and the isolated rerun passed; this was execution contention, not a failed assertion.

@vercel

vercel Bot commented Jul 28, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
orbit-ui-mobile-web Ignored Ignored Jul 28, 2026 3:29pm

Request Review

@claude claude 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.

Code Review: PR #625 — Wire sleep orchestration to strict merge sweep

Scope: PR #625, thomasluizon/orbit-ui-mobile, single file: .claude/skills/orchestrate/SKILL.md
Recommendation: APPROVE

Summary

This PR replaces the hand-rolled --sleep merge decision in the /orchestrate skill's section 4a with a call to the existing bash tools/merge-sweep.sh <owner/repo> <pr-number>... script, keeping only the conditions the script structurally cannot decide (sleep authorization, wave-blocker/Phase-1 gates, D7 evidence, attempts:2, and full reviewer-comment reconciliation). No prior reviews or unresolved threads exist on this PR (first pass). I cross-checked every behavioral claim the new prose makes against the actual tools/merge-sweep.sh / tools/merge-sweep-cov.sh (both unchanged by this diff) and found the descriptions accurate. One internal-consistency defect survived the renumbering from 6 conditions to 5.

Findings

Critical

None.

High

None.

Medium

[MEDIUM] Stale self-reference after condition renumbering
· dimension: 1. Correctness
· location: orbit-ui-mobile/.claude/skills/orchestrate/SKILL.md, section "4a `--sleep`", condition 5
· issue: The diff renumbers the old six-condition list down to five (old condition 6, "every
  comment addressed," becomes the new condition 5), but leaves the worked example inside that
  same condition unchanged: "Measured on PR #621 ... conditions 1 to 5 alone would have merged
  both defects." Under the old numbering this correctly excluded condition 6 (itself). Under the
  new numbering, condition 5 IS the comment-reconciliation gate, so the sentence now says
  "conditions 1 through 5" (including itself) would have merged the defects without the
  comment check — a self-contradiction, since the comment check is condition 5.
· risk: Low operational risk (the surrounding prose still unambiguously requires comment
  reconciliation), but it is a genuine broken cross-reference in a document an unattended agent
  reads verbatim to decide whether to merge a PR while Thomas is asleep — exactly the kind of
  precision this PR's own body holds itself to elsewhere (the five-condition mapping table, the
  UNMET reconciliation notes).
· fix: Change "conditions 1 to 5" to "conditions 1 to 4" in that sentence so it again excludes
  the condition it sits inside.
· reference: CLAUDE.md rule 1 (root-cause correctness) / internal document consistency

Low / Info

None.

Subagents

Agent Verdict
parity-checker N/A — no apps/web/** or apps/mobile/** file changed
i18n-syncer N/A — no user-facing strings or i18n JSON changed
contract-aligner N/A — no packages/shared/src/types/* / endpoints.ts change, single-repo diff
security-reviewer N/A — no orbit-api code changed
design-reviewer N/A — no apps/* / landing UI file changed

Validation

Check Result
Lint PASS (CI: Lint — SUCCESS)
Type check PASS (CI: Type Check — SUCCESS)
Tests N/A per task scope (Build/Unit Tests run as a separate required check, not gated by this skill)
Harness Execution PASS (CI: Harness Execution job — SUCCESS; PR body also cites node tools/test-tools.mjs and node .claude/hooks/test-hooks.mjs PASS output, satisfying rubric dimension 15's execution-evidence requirement)
Build (api) N/A — orbit-api not touched, not checked out in this CI context

Deferred — N/A dimensions & files not verdicted

  • Dimension 2 (Dead/stale code), 3 (SOLID/clean-arch), 4 (Comment policy), 6 (Type safety), 7 (console.log): N/A — the diff is prose in a Markdown skill file, not source code these checks apply to.
  • Dimension 8 (DESIGN.md/AI-slop), 9 (Parity), 10 (i18n): N/A — no apps/* UI, no cross-platform files, no i18n strings touched.
  • Dimension 11 (Contract drift/backward-compat): N/A — no packages/shared/src/types/*, endpoints.ts, or orbit-api DTO touched; not verifiable further since orbit-api is not checked out in this CI context.
  • Dimension 12 (Security), 13 (Backend hard rules): N/A — no orbit-api code in this diff.
  • Dimension 14 (FEATURES.md parity): N/A — this changes agent-skill orchestration behavior, not a user-facing product feature surface.
  • tools/merge-sweep.sh / tools/merge-sweep-cov.sh: not part of this diff (last touched in commit 9b662a06, predates this PR) — read in full to verify the new SKILL.md prose's behavioral claims accurately describe them; no defect found there.
  • Nothing else deferred: the single changed file's full diff was walked hunk by hunk.

What's good

  • The PR body's five-condition mapping table and explicit UNMET call-outs (exit-code semantics, CLEAN-vs-CLEAN|UNSTABLE) are honest about what the tool does and does not cover, rather than overclaiming equivalence.
  • The new prose's description of merge-sweep.sh (workflow-lookup fail-closed, review-check-settle-on-current-head, orphan-head verification, no --admin) was verified against the actual script and is accurate.
  • The full six-state comment-reconciliation paragraph (addressed / disagreed / informational, GraphQL resolve-and-verify) is preserved verbatim — the highest-value part of the old logic wasn't lost in the consolidation.
  • Harness Execution evidence is present both in CI (green) and in the PR body (actual command + PASS output), satisfying dimension 15 without asking the reviewer to trust an unexecuted claim.

Recommendation

Approve as-is; the one Medium finding (stale "conditions 1 to 5" self-reference) is a one-line text fix and does not block merge per the rubric's deterministic Signal gate (NEEDS WORK requires a surviving Critical/High). Worth fixing in this PR or a fast immediate follow-up so the skill's own worked example stays internally consistent for the next --sleep run that reads it.

@thomasluizon
thomasluizon marked this pull request as ready for review July 28, 2026 01:34

@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: b26e7d1421

ℹ️ 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 .claude/skills/orchestrate/SKILL.md Outdated

@claude claude 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.

Code Review: PR #625

Scope: PR #625, thomasluizon/orbit-ui-mobile, single file .claude/skills/orchestrate/SKILL.md
Recommendation: APPROVE

Summary

Replaces the hand-rolled --sleep merge decision in orchestrate's section 4a with a delegated
call to the existing bash tools/merge-sweep.sh <owner/repo> <pr-number>, keeping only the
conditions the script structurally cannot decide (sleep authorization + wave/Phase-1 gates, D7
evidence, attempts:2, full reviewer-comment reconciliation). Every behavioral claim the new
prose makes was cross-checked against the actual tools/merge-sweep.sh / merge-sweep-cov.sh
source (both unchanged, last touched in #623) and is accurate. No Critical/High findings.

Findings

Critical

None.

High

None.

Medium

None. I specifically re-checked the "conditions 1 to 5" self-reference inside condition 6's
PR-#621 anecdote, which a prior automated review on this PR flagged as a stale cross-reference
from a claimed 6-to-5 renumbering. That renumbering did not happen: the list is six items in
both the old and new text (verified line-by-line at the PR head) — only the content of items
1-3 changed; item 6 (comment reconciliation) is untouched by this diff and still sits at
position 6, so "1 to 5" still correctly excludes it. That earlier Medium finding does not
reproduce.

Low / Info

  • INFO — Condition 3 ("mergeStateStatus is CLEAN") is stricter than the script it
    delegates to, which accepts CLEAN or UNSTABLE. This is the exact "UNMET as literal
    equivalence" gap the PR body's five-condition mapping table already discloses, not something
    this diff introduces or hides. No action needed; changing the reviewed tool is explicitly out
    of scope for ORB-106.

Subagents

All N/A: no apps/web/**/apps/mobile/**, no i18n strings, no packages/shared/src/types/*,
no orbit-api, no apps/* UI file in this diff.

Validation

Lint PASS, Type Check PASS, Harness Execution PASS (CI green; PR body also cites executed
node tools/test-tools.mjs / node .claude/hooks/test-hooks.mjs output). Build PASS. orbit-api
build N/A (not touched).

Deferred

Dimensions 2-4, 6-14 are N/A — this diff is prose in a Markdown skill file with no source code,
UI, i18n, contract, or backend surface. tools/merge-sweep.sh / merge-sweep-cov.sh are
unchanged by this diff but were read in full to verify the new prose's claims; no defect found,
and test-tools.mjs already covers both scripts' bad-usage paths, so no new harness-coverage
gap. Inline PR comments beyond the two review bodies were not independently enumerated (gh api
scope not available in this session); not required by this rubric — that reconciliation is
/orchestrate --sleep's own job at actual merge time.

What's good

  • The PR body's five-condition mapping table and explicit "UNMET" call-outs are honest about
    what the delegated script does and does not cover, rather than overclaiming equivalence.
  • Every behavioral claim about tools/merge-sweep.sh (workflow-lookup fail-closed,
    review-check-settle-on-current-head, no --admin, orphan-head verification, per-PR output
    lines, exit codes) was verified against the actual script source and is accurate.
  • The full six-state comment-reconciliation paragraph is preserved verbatim; nothing was lost
    in the consolidation.
  • A prior automated review's Medium finding (stale "conditions 1 to 5" reference) was checked
    against the actual PR-head text and does not reproduce — see Findings above.

Recommendation

Approve as-is. No Critical/High findings survive.

@thomasluizon

Copy link
Copy Markdown
Owner Author

@codex review

@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: 9c04337e47

ℹ️ 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 .claude/skills/orchestrate/SKILL.md Outdated

@claude claude 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.

Code Review: PR #625 — ORB-106: Wire sleep orchestration to strict merge sweep

Scope: single file .claude/skills/orchestrate/SKILL.md (+52/-17)
Recommendation: APPROVE

Summary

This PR replaces the hand-rolled --sleep merge decision in /orchestrate section 4a with a delegated call to bash tools/merge-sweep.sh <owner/repo> <pr-number>, keeping only what the script structurally cannot decide (sleep authorization, wave-blocker/Phase-1 gates, D7 evidence, attempts:2, full reviewer-comment reconciliation). Two full "claude" reviews and one Codex pass already ran on this PR; the current head (9c04337e) is the exact commit the second "claude" review (APPROVE, no Critical/High) assessed — there is no diff since that last review. This pass independently re-verified the one disputed point from the first review and cross-checked the new prose's behavioral claims against the actual tools/merge-sweep.sh and tools/merge-sweep-cov.sh source; both hold up.

Findings

Critical: None.
High: None.

Medium: None survives. The first "claude" review flagged the "conditions 1 to 5" self-reference inside the old item-6 comment-reconciliation anecdote as stale after a claimed 6→5 renumbering. Tracing this against the base file and the PR's unified diff hunks shows: the diff only rewords items 1–3 and replaces the closing paragraph; the entire item-6 block falls in the diff's unshown context gap between the two hunks and is untouched. The list is still 6 items total after the PR, so "conditions 1 to 5" still correctly excludes item 6, the condition it sits inside. The second "claude" review's rebuttal was correct; this does not reproduce.

Low / Info: One holdover, already disclosed and non-blocking — new condition 3 ("mergeStateStatus is CLEAN") is stricter than merge-sweep.sh, which accepts CLEAN or UNSTABLE (tools/merge-sweep.sh:119). The PR body's own five-condition mapping table already discloses this as an explicit UNMET-as-literal-equivalence gap; changing the reviewed tool is out of scope for this ticket.

Independent script verification (both files unchanged by this diff, last touched in #623):

  • tools/merge-sweep.sh: update-branch first, polls mergeStateStatus/review-check settlement (up to ~17 min), workflow-lookup fails closed (line 70-74), squash-merges with no --admin (line 121), verifies no orphaned head post-merge, exit 0/1/2/3 exactly as the new SKILL.md prose describes.
  • tools/merge-sweep-cov.sh: --admin used only for a coverage-only SonarCloud failure (lines 152-160), matching the prose's claim that --sleep never invokes this variant.

Subagents

Agent Verdict
parity-checker N/A — no apps/web/**/apps/mobile/** file changed
i18n-syncer N/A — no user-facing strings or i18n JSON changed
contract-aligner N/A — no packages/shared/src/types/*/endpoints.ts changed, single-repo diff; orbit-api not checked out (not verifiable in CI)
security-reviewer N/A — no orbit-api code changed
design-reviewer N/A — no apps/*/landing UI file changed

Validation (skipped executing /validate per task scope; Build/Unit Tests/SonarCloud are separate required checks)

Check Result
Lint PASS (CI)
Type Check PASS (CI)
Unit Tests PASS (CI)
SonarCloud Analysis PASS (CI, quality gate passed, 0 new issues)
Harness Execution PR body cites executed node tools/test-tools.mjs / node .claude/hooks/test-hooks.mjs PASS output
Build (api) N/A — orbit-api not touched, not checked out

What's good

  • The PR body's five-condition mapping table and explicit UNMET call-outs (exit-code semantics, CLEAN-vs-CLEAN|UNSTABLE) are honest about what the delegated script does and doesn't cover rather than overclaiming equivalence.
  • The six-state comment-reconciliation paragraph (addressed/disagreed/informational, GraphQL resolve-and-verify) is preserved verbatim — the highest-value part of the old logic survived the consolidation.
  • Every behavioral claim about merge-sweep.sh/merge-sweep-cov.sh checks out against the actual scripts.

Recommendation

Approve as-is; no Critical/High findings survive independent verification, and the current head matches what the last full review already assessed.

Severity Count
Critical (incl. breaks old mobile clients) 0
High 0
Medium 0
Low / Info 1

@thomasluizon

Copy link
Copy Markdown
Owner Author

@codex review

@claude claude 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.

Code Review: PR #625 — ORB-106: Wire sleep orchestration to strict merge sweep

Scope: PR #625, thomasluizon/orbit-ui-mobile, single file: .claude/skills/orchestrate/SKILL.md (+66/-17)
Recommendation: APPROVE

Summary

The diff replaces the hand-rolled --sleep merge-decision prose in /orchestrate section 4a with a delegated call to the pre-existing bash tools/merge-sweep.sh <owner/repo> <pr-number> script, keeping only what the script cannot decide (sleep authorization, wave-blocker/Phase-1 gates, D7 evidence, attempts:2, full reviewer-comment reconciliation). This review reads the current head (bbdae7ad, "Verify gated head after sleep sweep"), which adds a safeguard verifying the merged head matches the expected gated head. Every behavioral claim in the new prose was cross-checked against the actual tools/merge-sweep.sh source (unchanged by this diff — exit codes 0/1/2/3, the fail-closed workflow lookup, the CLEAN/UNSTABLE acceptance, the per-PR polling loop) and found accurate.

I specifically chased a race-condition hypothesis: does delegating the merge to a script with an internal poll reopen the exact reviewer-comment-staleness window that condition 6's own cited incident (PR #621) describes? On inspection it does not: each candidate PR's full six-condition check (including fresh comment/thread enumeration) is re-run immediately before that PR's own script invocation, and the newest commit (bbdae7a) adds a closing safeguard — comparing the merged headRefOid against the headRefOid that actually passed conditions 1–6, and refusing to stay silent (stop-all-further-merges + loud report) if the script merges a head the skill's own checks never saw. That closes the gap rather than leaving it open.

Findings

Critical

None.

High

None.

Medium

None.

Low / Info

None surfaced (rubric signal gate — Low/Info not posted as PR findings).

Subagents

Agent Verdict
parity-checker N/A — no apps/web/** or apps/mobile/** file changed
i18n-syncer N/A — no user-facing strings or i18n JSON changed
contract-aligner N/A — no packages/shared/src/types/* / endpoints.ts changed
security-reviewer N/A — no orbit-api code changed (not checked out in this job)
design-reviewer N/A — no apps/* / landing UI file changed

Validation

Skipped per this workflow's CI adaptation — Build / Unit Tests / SonarCloud run as separate required checks. Harness Execution: PR body cites executed node tools/test-tools.mjs and node .claude/hooks/test-hooks.mjs, both PASS; this diff touches no file under tools/**, so merge-sweep.sh's existing test-tools.mjs coverage is unaffected.

What's good

  • The newest commit (bbdae7ad) closes a real gap: recording the pre-invocation headRefOid as the "expected head" and comparing it to the post-merge head, with a loud stop-all-further-merges response on mismatch.
  • The PR body's "Five-condition mapping" table is honest about what the delegated script does and does not cover (script exit-code semantics vs. the old ticket's claim, CLEAN-only vs. the tool's actual CLEAN/UNSTABLE acceptance), rather than overclaiming equivalence.
  • The six-state comment-reconciliation paragraph (addressed / disagreed / informational, GraphQL enumerate-and-resolve, the PR #621 incident evidence) survives the consolidation verbatim.
  • "One PR at a time" is explicitly justified rather than asserted, tying it to why re-batching would reopen the stale-head window.

Recommendation

Approve. No Critical/High findings survive review of the current head.

@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: 782a97a1f8

ℹ️ 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 .claude/skills/orchestrate/SKILL.md Outdated

@claude claude 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.

Code Review: PR #625

Scope: PR #625 in thomasluizon/orbit-ui-mobile (ORB-106: Wire sleep orchestration to strict merge sweep)
Recommendation: APPROVE

Summary

The diff is a single-file change to .claude/skills/orchestrate/SKILL.md: it replaces the
five hand-checked --sleep merge conditions with a six-condition skill-owned preflight plus
a handoff to the existing tools/merge-sweep.sh script for the mechanical merge decision.
Every claim in the new prose was cross-checked against the actual (unchanged)
tools/merge-sweep.sh source and its --help text; the described behavior (review-staleness
guard, fail-closed workflow lookup, no --admin, post-merge orphan-head scan, exit codes)
matches the real script exactly. One documentation gap survived verification (Medium, below);
no Critical/High findings.

Findings

Critical

None.

High

None.

Medium

[MEDIUM] CLEAN-vs-CLEAN-or-UNSTABLE gap disclosed in the PR body never reaches the persisted skill file
· dimension: Correctness (#1) / Harness changes (#15, doc-accuracy angle)
· location: .claude/skills/orchestrate/SKILL.md:327 (condition 3)
· issue: Condition 3 states the skill-owned invariant as "`mergeStateStatus` is `CLEAN`", and
  the "Immediately before handoff" paragraph (~line 383) requires the same strict CLEAN read
  before invoking the script. But `tools/merge-sweep.sh` itself merges on
  `[ "$ms" = "CLEAN" ] || [ "$ms" = "UNSTABLE" ]`, and condition 2 already admits the tool "does
  not keep a non-required pending check from being merged past" - i.e. UNSTABLE can mean a
  non-required check is still running when the script decides to merge. The PR body's own
  "Five-condition mapping" table names both gaps explicitly (rows 2 and 3, "marked UNMET...
  because changing the tool is out of scope"), but that table lives only in the ephemeral PR
  description. Nothing in the persisted `SKILL.md` says the invoked tool's real gate is
  looser than condition 3's stated CLEAN-only text.
· risk: A future `--sleep` run (or a human auditing one after the fact) reads only the skill
  file, not this PR's description. If GitHub's `mergeStateStatus` shifts from CLEAN to
  UNSTABLE during the script's ~17-minute poll window (e.g. a non-required check is still in
  flight), the script will squash-merge anyway - a state the skill's own condition 3 text says
  is disallowed. Unlike condition 6, which documents its own tool gap in-file with the PR #621
  incident, this gap is invisible to anyone who only reads `SKILL.md`.
· fix: Add a short note under condition 2 or 3 (same style as condition 6's incident note)
  stating plainly that `tools/merge-sweep.sh` merges on `CLEAN` OR `UNSTABLE` and does not
  itself wait out a still-running non-required check, so this is a known, accepted gap (or add
  a skill-owned preflight that enumerates non-required pending checks the way condition 6
  enumerates review threads).
· reference: root CLAUDE.md rule 1 (root cause / no undocumented workaround) and the
  documentation-accuracy spirit of rubric dimension 15 ("a claim... must trace to a command
  that ran"); the PR body itself half-discloses this but the artifact that governs future runs
  does not.

Low / Info

None posted (signal gate: style/wording nits on a bounded, well-reasoned prose diff are not
actionable findings).

Subagents

Agent Verdict
parity-checker N/A - no apps/web/** or apps/mobile/** file changed
i18n-syncer N/A - no user-facing strings or i18n JSON changed
contract-aligner N/A - no packages/shared/src/types/* / endpoints.ts / orbit-api DTO changed
security-reviewer N/A - no orbit-api code changed
design-reviewer N/A - no apps/* / orbit-landing-page UI file changed

Validation

Check Result
Lint PASS (PR's live CI: PR Tests / Lint)
Type check PASS (PR's live CI: PR Tests / Type Check)
Tests PASS (PR's live CI: PR Tests / Unit Tests)
Harness Execution (tools/test-tools.mjs + .claude/hooks/test-hooks.mjs) PASS (PR's live CI: Guards / Harness Execution, gated in because the diff touches .claude/skills/**)
Build (api) N/A - orbit-api not touched

Sourced from the PR's own live GitHub Actions check-runs rather than a local re-run: the local
working tree in this session is a detached checkout carrying an unrelated pre-existing
modification to the same file, so re-running lint/type-check/tests locally against it would
validate the wrong state. The PR's server-run CI is the authoritative, higher-fidelity source
for this diff's actual head commit.

Deferred - N/A dimensions & files not verdicted

  • Dimension 8 (DESIGN.md / AI-slop): N/A, no apps/* UI files in the diff.
  • Dimension 9 (Parity): N/A, no apps/web/** or apps/mobile/** files in the diff.
  • Dimension 10 (i18n): N/A, no user-facing strings or i18n JSON in the diff.
  • Dimension 11 (Contract drift + backward-compat): N/A, no packages/shared/src/types/* or
    orbit-api DTO in the diff.
  • Dimension 13 (Backend hard rules): N/A, no orbit-api code in the diff.
  • Dimension 14 (FEATURES.md parity): N/A, this changes an internal agent-orchestration skill
    with no user-facing feature surface.
  • ORB-106's Linear ticket body was not independently re-read against the PR's own
    reconciliation claims (no Linear access in this session); the PR body's stated UNMET items
    are taken at face value and cross-checked only against the actual tools/merge-sweep.sh
    source, not against the ticket text itself.
  • Every changed hunk in the single changed file (.claude/skills/orchestrate/SKILL.md) was
    read and given a verdict; nothing in the diff was skipped.

What's good

  • Every behavioral claim about tools/merge-sweep.sh in the new prose (review-staleness
    guard, fail-closed workflow lookup, no --admin, post-merge orphan-head scan, exit code
    semantics) was checked line-by-line against the actual script and matches exactly - this is
    unusually well-verified documentation.
  • The PR body's own "Five-condition mapping" and "Reconciliation discrepancies" sections
    proactively name the two real gaps between the old five-condition text and the unchanged
    tool's actual behavior, with an explicit, defensible reason each was left out of scope
    (changing the reviewed tool). That transparency is exactly why the one surviving finding
    here is Medium rather than Critical: the gap is real but already reasoned about, just not
    yet carried into the file that future runs actually read.
  • Condition 6 (reviewer-comment reconciliation, unchanged by this diff) already sets the right
    precedent for how to document a known tool gap in-file with a concrete incident (PR #621);
    the fix for the Medium finding above is to extend that same treatment to conditions 2 and 3.
  • CI is green end to end on this diff's actual head commit (Lint, Type Check, Unit Tests,
    Harness Execution, SonarCloud Quality Gate), which is stronger evidence than the PR body's
    self-reported command transcript alone.

Recommendation

Approve as-is; the one Medium finding is a documentation-completeness gap, not a merge
blocker. Worth a quick follow-up (a two-to-three line addition near conditions 2/3) before
this skill is next relied on for an unattended --sleep run, so a future reader of
SKILL.md alone - not this PR's description - knows the tool's real merge gate is
CLEAN-or-UNSTABLE and doesn't itself wait out a still-running non-required check.

@claude claude 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.

Code Review: PR #625 — ORB-106: Wire sleep orchestration to strict merge sweep

Scope: PR #625, thomasluizon/orbit-ui-mobile, single file: .claude/skills/orchestrate/SKILL.md
Recommendation: APPROVE

Summary

The diff replaces the hand-rolled --sleep merge-decision prose in /orchestrate section 4a
with a delegated call to the pre-existing bash tools/merge-sweep.sh <owner/repo> <pr-number>
script, keeping only what the script structurally cannot decide (sleep authorization,
wave-blocker/Phase-1 gates, D7 evidence, attempts:2, full reviewer-comment reconciliation).
This pass reviews the current head (12dabfe6, "Document residual sleep merge race"), the
commit added since my last review on this PR (at 782a97a1). That commit documents the
Dependabot-auto-merge residual race — I verified .github/workflows/dependabot-auto-merge.yml
exists and does exactly what the new prose claims (approves + enables auto-merge for safe
Dependabot lanes independent of any /orchestrate --sleep run), so the new claim is accurate.
It does not touch the one Medium finding from my last review, which still does not reproduce
as fixed at this head.

Findings

Critical

None.

High

None.

Medium

[MEDIUM] CLEAN-vs-CLEAN-or-UNSTABLE gap disclosed in the PR body never reaches the persisted skill file
· dimension: 1. Correctness / 15. Harness changes (doc-accuracy angle)
· location: orbit-ui-mobile/.claude/skills/orchestrate/SKILL.md, section "4a `--sleep`", condition 3
· issue: Condition 3 states the skill-owned invariant as "`mergeStateStatus` is `CLEAN`", and the
  "Immediately before handoff" paragraph requires the same strict CLEAN read before invoking the
  script. But `tools/merge-sweep.sh:119` merges on
  `[ "$ms" = "CLEAN" ] || [ "$ms" = "UNSTABLE" ]`, and condition 2 already admits the tool "does
  not keep a non-required pending check from being merged past" — i.e. UNSTABLE can mean a
  non-required check is still running when the script decides to merge anyway. The PR body's own
  "Five-condition mapping" table names this gap explicitly (row 3, "marked UNMET below because
  changing the tool is out of scope"), but that table lives only in the ephemeral PR description.
  Nothing in the persisted `SKILL.md` says the invoked tool's real gate is looser than condition
  3's stated CLEAN-only text. This is unchanged by the newest commit (`12dabfe6`), which adds a
  different, unrelated disclosure (the Dependabot-auto-merge race) in the same section without
  touching condition 2 or 3.
· risk: A future `--sleep` run, or a human auditing one after the fact, reads only the skill file,
  not this PR's description. If `mergeStateStatus` shifts from CLEAN to UNSTABLE during the
  script's own ~17-minute poll window (e.g. a non-required check is still in flight), the script
  will squash-merge anyway — a state the skill's own condition 3 text says is disallowed. Unlike
  condition 6, which documents its own tool gap in-file with the PR #621 incident, this gap is
  invisible to anyone who only reads `SKILL.md`.
· fix: Add a short note under condition 2 or 3 (same style as condition 6's incident note, or the
  new Dependabot-race paragraph this same commit just added) stating plainly that
  `tools/merge-sweep.sh` merges on `CLEAN` OR `UNSTABLE` and does not itself wait out a
  still-running non-required check, so this is a known, accepted gap — not an undocumented one.
· reference: root CLAUDE.md rule 1 (root cause / no undocumented workaround) and the
  documentation-accuracy spirit of rubric dimension 15 ("a claim... must trace to a command that
  ran"); the PR body itself half-discloses this but the artifact that governs future unattended
  runs does not.

Low / Info

None posted (signal gate: no concretely-actionable Low/Info on this bounded prose diff).

Subagents

Agent Verdict
parity-checker N/A — no apps/web/** or apps/mobile/** file changed
i18n-syncer N/A — no user-facing strings or i18n JSON changed
contract-aligner N/A — no packages/shared/src/types/* / endpoints.ts / orbit-api DTO changed; sibling repo not checked out in this job (not verifiable in CI regardless)
security-reviewer N/A — no orbit-api code changed
design-reviewer N/A — no apps/* / orbit-landing-page UI file changed

Validation

Per this workflow's CI adaptation, Phase 6 (/validate) is skipped — Build / Unit Tests /
SonarCloud run as separate required checks on this PR and all report SUCCESS on the current head
(12dabfe6): Lint, Type Check, Unit Tests, Build, SonarCloud Analysis, Contract Drift,
Cross-Platform Parity, Harness Execution, Dash Ban, Copy Register, Suppressions Ratchet, Expo SDK
Pin, Skill and Agent Frontmatter, arch-map, CodeQL, Dependency Review, Dependabot auto-merge
(skipped, not a Dependabot PR), GitGuardian, React Doctor, Mutation Testing — all SUCCESS/SKIPPED
as appropriate; no FAILURE among them. Harness Execution evidence is also present in the PR body
itself (node tools/test-tools.mjs and node .claude/hooks/test-hooks.mjs, both PASS), satisfying
rubric dimension 15 without asking the reviewer to trust an unexecuted claim.

Deferred — N/A dimensions & files not verdicted

  • Dimensions 2–4, 6–7 (dead code, SOLID, comment policy, type safety, console.log): N/A — the
    diff is prose in a Markdown skill file, not source code these checks apply to.
  • Dimension 8 (DESIGN.md / AI-slop), 9 (Parity), 10 (i18n): N/A — no apps/* UI, no
    cross-platform files, no i18n strings touched.
  • Dimension 11 (Contract drift / backward-compat): N/A — no packages/shared/src/types/*,
    endpoints.ts, or orbit-api DTO touched; not verifiable further since orbit-api is not checked
    out in this CI context.
  • Dimension 12 (Security), 13 (Backend hard rules): N/A — no orbit-api code in this diff.
  • Dimension 14 (FEATURES.md parity): N/A — this changes agent-skill orchestration behavior, not a
    user-facing product feature surface.
  • tools/merge-sweep.sh / tools/merge-sweep-cov.sh: not part of this diff (unchanged, last
    touched before this PR) — read in full to verify the new SKILL.md prose's behavioral claims;
    confirmed accurate, no new defect there.
  • .github/workflows/dependabot-auto-merge.yml: read in full to verify the newest commit's
    residual-race claim; confirmed accurate.
  • Nothing else deferred: the single changed file's full diff, including the commit added since my
    last review on this PR, was walked hunk by hunk.

What's good

  • The PR body's "Five-condition mapping" table and explicit UNMET call-outs (exit-code semantics,
    CLEAN-vs-CLEAN|UNSTABLE, the Dependabot race) are honest about what the delegated script does
    and does not cover, rather than overclaiming equivalence.
  • The newest commit (12dabfe6) closes a real documentation gap: naming
    dependabot-auto-merge.yml as an independent merge actor the "one PR at a time" serialization
    cannot control, and stating plainly that closing it needs a tool change that is out of scope for
    this ticket rather than silently leaving the race unstated.
  • The full six-state comment-reconciliation paragraph (addressed / disagreed / informational,
    GraphQL enumerate-and-resolve, the PR #621 incident evidence) survives the consolidation
    verbatim — the highest-value part of the old logic was not lost.
  • Every behavioral claim about tools/merge-sweep.sh (workflow-lookup fail-closed,
    review-check-settle-on-current-head, no --admin, orphan-head verification, exit-code
    semantics) checks out against the actual script source.

Recommendation

Approve as-is. The one Medium finding is a documentation-completeness gap disclosed in the PR
body but not yet carried into the persisted skill file; it does not block merge per the rubric's
deterministic Signal gate (NEEDS WORK requires a surviving Critical/High). Worth a quick
follow-up — a two-to-three line addition near conditions 2/3, in the same style as the
Dependabot-race paragraph this PR just added — before this skill is next relied on for an
unattended --sleep run.

Severity Count
Critical (incl. breaks old mobile clients) 0
High 0
Medium 1
Low / Info 0

Copy link
Copy Markdown
Owner Author

@codex review

1 similar comment
@thomasluizon

Copy link
Copy Markdown
Owner Author

@codex review

@claude claude 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.

Code Review: PR #625 — ORB-106: Wire sleep orchestration to strict merge sweep

Scope: PR #625, thomasluizon/orbit-ui-mobile. Focused on the diff since the last bot review (commit 12dabfe68429214faa83, "Enforce expected heads in merge sweeps"): .claude/skills/orchestrate/SKILL.md (small update), tools/merge-sweep.sh, tools/merge-sweep-cov.sh, tools/test-tools.mjs. Six prior bot reviews already approved earlier SKILL.md-only revisions of this PR.
Recommendation: APPROVE

Summary

This increment closes the residual merge race the prior revision had only documented. Both merge-sweep scripts now accept repeated --expected-head <pr>=<sha> mappings, re-read headRefOid after update-branch and on every poll, print a distinct HEAD-MOVED skip naming both SHAs, and pass GitHub's atomic --match-head-commit at the merge call so a last-moment base advance (e.g. dependabot-auto-merge.yml) is refused server-side instead of merely detected after the fact. SKILL.md's prose matches the scripts' actual behavior exactly (verified line by line against both scripts). tools/test-tools.mjs adds a hermetic gh stub and four new cases per script (matched / changed-head / atomic-race / bare-invocation); the PR body's "Round 4 decision-path evidence" shows all eight passing against the real scripts.

Findings

Critical

None.

High

None.

Medium

[MEDIUM] New --expected-head input-validation branches ship with zero test coverage
· dimension: 15. Harness changes need EXECUTED evidence
· location: tools/merge-sweep.sh:56-92, tools/merge-sweep-cov.sh:55-106
· issue: Both scripts add ~7 new exit-2 decision paths for `--expected-head`: missing value,
  malformed `<pr>=<sha>` (no `=`, empty pr, empty sha), non-numeric PR, non-hex SHA, wrong
  SHA length (not 40/64 chars), duplicate mapping for the same PR, and a mapping naming a PR
  not in the sweep. `tools/test-tools.mjs`'s `mergeSweepCases()` covers only the merge-time
  behavior (matched/changed/race/bare); the pre-existing `INVALID_INPUT` table only exercises
  a generic unknown-flag case that predates this diff and never reaches the new parsing code.
· risk: A future edit to this argument parser (e.g. relaxing the SHA-length check, or a typo in
  the duplicate-detection loop) regresses silently — the rubric's own rule: "a new decision path
  ... needs its own case, not an extension of an existing assertion."
· fix: Add cases to `mergeSweepCases` (or a sibling) asserting `status === 2` for:
  `--expected-head 615` (no `=`), `--expected-head =abc...` (empty pr), `--expected-head 615=`
  (empty sha), `--expected-head 615=zz...` (non-hex), `--expected-head 615=abc` (wrong length),
  `--expected-head 615=<sha> --expected-head 615=<sha2>` (duplicate), and
  `--expected-head 999=<sha> <repo> 615` (mapping names an unrequested PR).
· reference: rubric.md dimension 15, "a new decision path added to a tool that already has
  coverage ... needs its own case"

Low / Info

None posted (signal gate: Low/Info are not posted on a PR review).

Subagents

Agent Verdict
parity-checker N/A — no apps/web/** or apps/mobile/** file changed
i18n-syncer N/A — no user-facing strings touched
contract-aligner N/A — no packages/shared/src/types/*/endpoints.ts change, only one repo touched
security-reviewer N/A — no orbit-api code in this diff (sibling repo not checked out; not verifiable in CI either way)
design-reviewer N/A — no apps/* UI files changed

Validation

Phase 6 (/validate) skipped per this run's instructions — Build / Unit Tests / SonarCloud already run as required checks on this PR.

Check Result
Lint N/A (skipped)
Type check N/A (skipped)
Tests Not independently re-run this session. PR body cites a real run producing 8/8 PASS for the new merge-sweep.sh/merge-sweep-cov.sh cases; CI's Harness Execution job (guards.yml) is authoritative once it lands.
Build (api) N/A — orbit-api not touched, sibling repo not checked out

Deferred — N/A dimensions & files not verdicted

  • Parity, i18n, Contract drift, DESIGN.md/AI-slop, Backend hard rules, FEATURES.md parity: all N/A — diff touches only .claude/skills/** and tools/**, never apps/*, packages/shared/src/types/*, orbit-api, or a user-facing feature surface.
  • Security (API side): N/A — no orbit-api code in the diff; not verifiable in this CI session regardless since the sibling repo isn't checked out.
  • Every changed file (SKILL.md, merge-sweep.sh, merge-sweep-cov.sh, test-tools.mjs) got a verdict above; nothing left un-reviewed.

What's good

  • Root-cause fix (rule 1): uses GitHub's native --match-head-commit so the server refuses a last-moment change, rather than a client-side workaround.
  • SKILL.md updated in the same edit and stays fully accurate against the actual shell logic — no doc/code drift.
  • New --expected-head validation is strict and fails closed (hex-only, exact 40/64-char length, duplicate-mapping and unrequested-PR checks all error with usage+exit 2).
  • The hermetic gh stub models the "moved exactly at merge time" race via a marker file written inside the stubbed pr merge call — precisely the scenario --match-head-commit exists to catch — and asserts the literal flag+value reached the real invocation.

Recommendation

Approve as-is. The one Medium finding (missing input-validation test coverage for the new --expected-head parsing branches) is a good follow-up but does not block merge.

@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: 89ff7881a1

ℹ️ 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 .claude/skills/orchestrate/SKILL.md Outdated
@thomasluizon

Copy link
Copy Markdown
Owner Author

@codex review

1 similar comment

Copy link
Copy Markdown
Owner Author

@codex review

@claude claude 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.

Code Review: PR #625

Scope: PR #625 in thomasluizon/orbit-ui-mobile — ORB-106: wire /orchestrate --sleep to the strict, head-pinned merge sweep (tools/merge-sweep.sh, tools/merge-sweep-cov.sh, .claude/skills/orchestrate/SKILL.md, tools/test-tools.mjs).
Recommendation: REQUEST CHANGES

This review runs after several prior rounds already resolved a HIGH finding on this PR (head-pinning aborting a routine BEHIND-branch update) via the new adopt_routine_update re-baselining logic — verified present at tools/merge-sweep.sh:346-372 and confirmed not to recur. This pass focuses on what's new/unresolved since that round.

Critical

The PR's own required "Context Budget" CI check is failing.
Confirmed live: gh pr view 625 --json statusCheckRollup shows Context Budget = FAILURE (Guards workflow, both duplicate runs). This diff doesn't touch CLAUDE.md or .claude/rules/core.md, and tools/check-context-budget.mjs --check compares this PR's measured bytes against the baseline committed on main — likely the branch trailing main's post-ORB-118 (context-trim) baseline rather than a defect in this diff's own content. The PR body's gate-output sections never show this check being run or passing. Fix: rebase onto current main and confirm node tools/check-context-budget.mjs --check passes before merge.

High

The pending-checks gate blocks on non-required checks, contradicting the PR's own documented scope.
tools/merge-sweep.sh:383-393,446-450 and tools/merge-sweep-cov.sh:218-227,502-506 compute pending/checks_pending from every row in statusCheckRollup that hasn't reached a terminal state — with no way to filter to required checks, since gh pr view --json statusCheckRollup exposes no isRequired field. This directly contradicts the PR's own "Condition mapping" table, which states: "the tool rejects failed conclusions and waits on merge-blocking state, but it does not enumerate non-required pending checks. The all-concluded requirement remains a skill-owned preflight." The shipped code does exactly what that row says is out of scope.

Practically: GitHub's mergeStateStatus=UNSTABLE (which the script explicitly accepts alongside CLEAN) means required checks passed while a non-required one is still failing/pending — that's the scenario UNSTABLE exists to describe. But checks_pending fires on ANY pending row regardless of required-ness, so a still-running non-required job (CodeQL, Dependency Audit, Vercel Preview Comments — all present on this very PR) blocks the merge anyway, negating the UNSTABLE acceptance in the common case. tools/test-tools.mjs:1608-1612 only exercises this path for the required review check pending, not for a non-required check under ms=UNSTABLE, so the gap is untested as well as undocumented. Given this is the tool --sleep (unattended, ~17min budget per PR) depends on, a PR sitting behind one slow non-required job can spuriously SKIP at timeout instead of merging — undermining ORB-106's purpose.
Fix: either filter pending to required checks (e.g. gh pr checks <n> --required or cross-reference branch protection), or correct the condition-mapping/help text to state that non-required pending checks ARE now enumerated and block merge.

Medium

review_safety_gate's "gh api lookup, fail closed" block is repeated near-identically four times within the function in each script (tools/merge-sweep.sh:272-345, mirrored in merge-sweep-cov.sh), pushing it toward the ~50-line soft cap (rule 7). A small fetch_or_fail() helper would collapse the repetition. (Note: the cross-script duplication between the two sweep files themselves is not a finding — tools/CONVENTIONS.md explicitly permits that until a third caller exists.)

Low / Info

Edited PR-review bodies sit outside the --reviewed-through reconciliation net — a documented GitHub API limitation (no edit timestamp on review bodies) called out honestly in SKILL.md, not an oversight.

Subagents

parity-checker / i18n-syncer / contract-aligner / security-reviewer / design-reviewer: N/A — diff touches only tools/** and .claude/skills/orchestrate/SKILL.md; no apps/*, orbit-api, or packages/shared surface.

Validation

Sourced from live CI (Phase 6 skipped per workflow instructions): Lint PASS, Type Check PASS, Unit Tests PASS, Harness Execution PASS, Harness Lockstep PASS, SonarCloud PASS, Context Budget FAIL (see Critical).

What's good

The adopt_routine_update re-baselining fix from the prior review round is correctly implemented and closes that HIGH cleanly. The --reviewed-through reconciliation gate remains thorough (paginated, edit-aware, exclusive cutoff, fails closed). Harness coverage is extensive and the round-by-round PASS evidence in the PR body is consistent with live CI.


Reviewed via the /pr-review skill against .claude/skills/pr-review/rubric.md.

@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: ca73cdcd14

ℹ️ 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 tools/merge-sweep.sh Outdated

Copy link
Copy Markdown
Owner Author

@codex review

@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: f2e02ca607

ℹ️ 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 .claude/skills/orchestrate/SKILL.md Outdated

@claude claude 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.

Code Review: PR #625 — ORB-106: Wire sleep orchestration to strict merge sweep

Scope: PR #625, thomasluizon/orbit-ui-mobile, head f2e02ca6070bbe9c8145a014eca710cefe1ad9f4 (checked out here as merge commit 2040a6a9). Files: tools/merge-sweep.sh, tools/merge-sweep-cov.sh, .claude/skills/orchestrate/SKILL.md, tools/test-tools.mjs.
Recommendation: APPROVE

Summary

This is at least the 10th review pass on this PR, which has already been through nine documented self-review rounds. It wires /orchestrate --sleep to a head-pinned, review-activity-aware strict merge sweep (--expected-head, --reviewed-through), closing several real unattended-merge races: stale-head merges, unreconciled review/comment activity, edited COMMENTED review bodies, and a residual post-merge activity window. Both blocking findings from the immediately-prior CHANGES_REQUESTED review (13:10 UTC, commit ca73cdcd) are confirmed resolved on this head:

  • Critical (Context Budget CI check failing): confirmed live — both Context Budget check runs now read SUCCESS.
  • High (pending-checks gate silently contradicted the PR's own "non-required checks are skill-owned" claim): resolved not by filtering to required-only checks, but by making the stricter behavior (block on any pending check, required or not) explicit and consistent everywhere — the script --help text, SKILL.md condition 2, and the PR body's condition-mapping table now all agree. The contradiction was the actual finding; it's gone.
  • Medium (review_safety_gate's repeated "gh api lookup, fail closed" block, 4x duplicated): resolved via the new report_review_lookup_failure() / lookup_review_activity() helpers in commit d485cb73.

This pass found two new Medium-severity gaps in the newly-added post-merge safety net. Neither blocks.

Findings

Critical

None.

High

None.

Medium

SKILL.md's exit-4 operator guidance never names two of the three post-merge failure markers the script can print.
· dimension: 1 (Correctness) / 15 (Harness — operator contract)
· location: .claude/skills/orchestrate/SKILL.md (the "Read the script's per-PR output..." paragraph); tools/merge-sweep.sh:244,273,310 and the mirrored lines in tools/merge-sweep-cov.sh
· issue: review_safety_gate <pr> post can print three distinct markers under the identical exit-4 contract — POST-MERGE-ACTIVITY, POST-MERGE-UNRESOLVED-THREADS, and POST-MERGE-REVIEW-LOOKUP-FAILED. SKILL.md only ever names and quotes POST-MERGE-ACTIVITY ("report the exact POST-MERGE-ACTIVITY line" / "A PR named by POST-MERGE-ACTIVITY is already merged...").
· risk: exit code 4 itself is generically handled ("stop further unattended merges"), so the core safety property holds, but an agent following this doc literally has no instruction for what a POST-MERGE-UNRESOLVED-THREADS or POST-MERGE-REVIEW-LOOKUP-FAILED line means or how to log it — those two cases are likely to land in the closing report as an under-detailed generic "exit 4" instead of with the count/source the design elsewhere insists on.
· fix: generalize the paragraph to cover all three POST-MERGE-* markers instead of naming one three times.
· reference: CLAUDE.md rule 8

The post-merge branches for unresolved threads and lookup failure have no test coverage.
· dimension: 15 (Harness changes need EXECUTED evidence)
· location: tools/test-tools.mjs mergeSweepCases(); tools/merge-sweep.sh:305-312 and the tools/merge-sweep-cov.sh mirror
· issue: mergeSweepCases() covers POST-MERGE-ACTIVITY but never drives review_safety_gate's post-phase through the unresolved-threads branch or a lookup-failure branch — the existing "unresolved review threads..." / "...lookup-failed by name" cases only exercise the pre-merge phase.
· risk: per dimension 15, a new decision path (the post/pre phase split and its two other message variants) ships unexecuted; a defect in post-merge unresolved-count parsing or the phase branch of report_review_lookup_failure would go uncaught.
· fix: add two cases mirroring postMergeActivity's shape, with unresolvedThreads/threadsLookupFailure/reviewsLookupFailure gated to fire only after the merge call (the stub's existing postMergeMarker mechanism), asserting POST-MERGE-UNRESOLVED-THREADS #615 count= / POST-MERGE-REVIEW-LOOKUP-FAILED #615 source= with exit code 4.
· reference: .claude/skills/pr-review/rubric.md dimension 15

Low / Info

Not posted per the Signal gate. For the record, non-blocking: squash_merge() in tools/merge-sweep-cov.sh assigns actual=$(head_oid "$pr") without local (unlike its sibling pr/expected/label/branch) — a global leak on a call graph that's top-level-only today, so no live bug, just an inconsistency.

Subagents

Agent Verdict
parity-checker N/A — no apps/web/**/apps/mobile/** file changed
i18n-syncer N/A — no user-facing strings/i18n JSON changed
contract-aligner N/A — no packages/shared/src/types/*/endpoints.ts, no backend repo touched
security-reviewer N/A — no orbit-api code changed
design-reviewer N/A — no apps/*/orbit-landing-page UI file changed

Validation

Sourced from live CI (CI-wrapper context; Phase 7 skipped per workflow instructions; direct in-session execution of node tools/test-tools.mjs was blocked by this sandbox's approval policy for that command class).

Check Result
Lint PASS (live CI)
Type Check PASS (live CI)
Unit Tests PASS (live CI)
Harness Execution PASS (live CI — both duplicate Guards runs SUCCESS at query time)
Harness Lockstep PASS (live CI)
Context Budget PASS (live CI — previously-flagged Critical now resolved)
SonarCloud PASS (live CI)
Build (api) N/A — no orbit-api change

Deferred — N/A dimensions & files not verdicted

  • Dimensions 8, 9, 10, 11, 12, 13, 14: N/A — diff touches only tools/** and .claude/skills/orchestrate/SKILL.md, no apps/*, packages/shared, or orbit-api surface. orbit-api was not checked out in this session.
  • All four changed files were read in full at the PR head and given a verdict; nothing skipped for size.
  • Harness Execution evidence is sourced from the live CI check run rather than a command this session ran itself (sandbox blocked direct execution).

What's good

Nine rounds of self-directed review against real, cited findings (a reproduced production incident on orbit-api#403, a measured reviewDecision-staleness gap on #621) is unusual rigor for a tooling PR. The --match-head-commit atomic assertion, adopt_routine_update's parent-based re-baselining after a routine update-branch, and the three-source (reviews/inline/issue) paginated, edit-aware, exclusive-cutoff reconciliation gate are all correctly implemented and now consistently documented across the script --help text, SKILL.md, and the PR body.

Recommendation

Approve as-is. The two Medium findings are real and worth a fast follow-up but don't weaken the shipped safety property — exit 4 already stops further unattended merging in both under-documented/under-tested cases; it's the operator-facing detail that's thin.

Copy link
Copy Markdown
Owner Author

@codex review

@claude claude 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.

Code Review: PR #625 — ORB-106: Wire sleep orchestration to strict merge sweep

Scope: PR #625, thomasluizon/orbit-ui-mobile, head 24543dedf17dc849ef1e2885140e389adcb5dd84. Reviewed the full diff plus, specifically, the delta since my prior APPROVE (f2e02ca624543ded, commit "Close review cutoff read race", .claude/skills/orchestrate/SKILL.md only).
Recommendation: APPROVE

Summary

This pass covers the last commit added since my previous review: it reorders the reconciliation-cutoff logic in SKILL.md so capturing reviewed-through happens BEFORE the final validation reads (byte-for-byte activity-snapshot comparison + unresolved-thread recheck), rather than after. That closes a real residual race — activity landing during the final re-read could previously sort before the boundary and be missed. The new ordering (reconciliation mutations → boundary → validation reads → sweep) is internally consistent and correctly reasoned. No new Critical/High issues.

Three Medium findings survive from a full-file review, all verified directly against the git-committed head (not the working tree, which carries an unrelated pre-existing uncommitted revert of this same file — confirmed via git status/git diff at session start, not something introduced by this review).

Findings

Critical

None.

High

None.

Medium

[MEDIUM] SKILL.md's illustrative reconciliation query omits `updatedAt`, which the script's own gate checks
· location: .claude/skills/orchestrate/SKILL.md:400-409 (the `reviews(first:100,...){nodes{author{login} body submittedAt lastEditedAt}}` snippet and "Treat both `submittedAt` and every non-null `lastEditedAt` as review activity")
· issue: tools/merge-sweep.sh:317-319 and the merge-sweep-cov.sh mirror query and check THREE fields per review — submittedAt, updatedAt, AND lastEditedAt (verified directly in both scripts). SKILL.md's human-facing example and prose name only two.
· risk: An operator reconciling strictly per the doc can believe reconciliation is complete, then have the script reject the same head because `updatedAt` moved for a reason the doc never mentioned — a confusing false-negative SKIP.
· fix: Add `updatedAt` to the illustrative query and the "treat as activity" sentence.
[MEDIUM] Reviews-connection pagination (--paginate) has no dedicated multi-page test
· location: tools/test-tools.mjs:203 (the `reviews(first:100` stub branch) vs. :250-251 (`ORBIT_MERGE_SWEEP_INLINE_PAGE_TWO`, wired only for inline comments)
· issue: review_safety_gate fetches reviews via `gh api graphql --paginate --slurp`. The stub always returns a single flat page for reviews regardless of `--paginate`; only the inline-comment stub has a page-two injection mechanism.
· risk: A regression in the slurp-flattening jq (losing the outer `.[] |` iteration) would ship silently.
· fix: Extend the reviews stub the same way, with an env-var-gated second page, plus a test case exercising it.
[MEDIUM] Dead PATH-based `sleep` stub in the merge-sweep test harness
· location: tools/test-tools.mjs:277-278 (`MERGE_SWEEP_SLEEP` staged + chmod'd) vs. :279/304 (`MERGE_SWEEP_BASH_ENV`'s `sleep() { :; }` wired via `BASH_ENV`)
· issue: `.sh` fixtures run as `bash <target>`; bash resolves shell functions before PATH executables, so the `BASH_ENV` function shadows the PATH stub — the `merge-sweep-bin/sleep` file can never run.
· risk: None functionally — unused code shipped and maintained for no reason.
· fix: Delete `MERGE_SWEEP_SLEEP` and its `chmodSync` call.

Low / Info

None (Signal gate).

Subagents

Agent Verdict
parity-checker N/A — no apps/web/**/apps/mobile/** changed
i18n-syncer N/A — no user-facing strings/i18n JSON changed
contract-aligner N/A — no shared types/DTOs, single-repo scope
security-reviewer N/A — no orbit-api changed
design-reviewer N/A — no UI files changed

Validation

CI-wrapper context; Phase 6 (/validate) and Phase 7 (posting) skipped per task instructions. At review time PR #625's head (24543ded) shows Build, Unit Tests, SonarCloud, Guards/Harness Execution, Harness Lockstep, and Cross-Platform Parity all green; the PR body documents repeated executed npm run lint/type-check/test + node tools/test-tools.mjs + node .claude/hooks/test-hooks.mjs runs with pass counts.

Deferred

  • Dimensions requiring apps/*, packages/shared, or orbit-api: N/A, no such surface in this diff; orbit-api not checked out in this job.
  • Noted for the record: the checked-out working directory carries a pre-existing, uncommitted local revert of .claude/skills/orchestrate/SKILL.md to stale content (present before this review began — visible in the session's initial git status). All findings above were verified against the actual git-committed blobs (git show <sha>:<path>), not the working-tree file, to avoid reviewing stale content. Worth a human check on why the worktree is dirty, though it is not a PR defect.

What's good

  • The reordered cutoff logic (reconciliation → boundary → validation reads → sweep) correctly closes the race the commit title names, with a clear stated invariant (byte-for-byte snapshot match + no activity at/after cutoff) rather than a vague "re-check".
  • Nine-plus rounds of self-directed review with real, cited, reproduced findings is unusual rigor for a tooling PR.

Recommendation

Approve. The three Medium findings are real and worth a fast follow-up but do not weaken the shipped safety property.

@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: 24543dedf1

ℹ️ 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 tools/merge-sweep.sh

thomasluizon commented Jul 28, 2026

Copy link
Copy Markdown
Owner Author

Reconciled the approved current-head review findings in 4f99b663daf0394ce8ab82572a0309e6e1fdea71. The orchestrate contract now includes review updatedAt everywhere it enumerates safety activity. The harness now proves second-page review pagination, removes the shadowed PATH sleep fixture, and executes the post-merge unresolved-thread and review-lookup-failure branches for both sweep scripts. The operator guidance now covers all three POST-MERGE-* markers. Verified with node tools/test-tools.mjs (ORBIT TOOLS GATE OK) and node .claude/hooks/test-hooks.mjs (ORBIT HOOK PARITY OK).

Copy link
Copy Markdown
Owner Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

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

Reviewed commit: 4f99b663da

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

@claude claude 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.

test

@claude claude 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.

Correction: the previous review I just posted with body "test" was submitted in error (a tool-call mistake, not a verdict) — please disregard it. My actual review of the current head follows in the next comment.

@claude claude 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.

Code Review: PR 625

Scope: PR #625 in thomasluizon/orbit-ui-mobile — "ORB-106: Wire sleep orchestration to strict merge sweep", head 4f99b663daf0394ce8ab82572a0309e6e1fdea71.
Recommendation: APPROVE

(Note: a review posted moments ago on this PR with the body "test" was a tool-call mistake, not a verdict — please disregard it; this review supersedes it.)

Summary

The diff is tooling-only: .claude/skills/orchestrate/SKILL.md, tools/merge-sweep.sh, tools/merge-sweep-cov.sh, and tools/test-tools.mjs. It teaches both merge-sweep scripts an --expected-head/--reviewed-through pinning contract, a review-activity safety gate (reviews, inline comments, conversation comments, all via paginated GraphQL/REST with an exclusive cutoff), atomic --match-head-commit merges, and a post-merge recheck that reports residual-race activity via exit 4. I traced the bash logic line-by-line and independently confirmed against a live gh 2.96.0 install (man pages for gh-pr-merge, gh-pr-update-branch, gh-api) that every flag used (--match-head-commit, --paginate, --slurp, -F/-f, --jq) is real and used correctly — this matters because the test harness stubs gh entirely and could not itself have caught a wrong flag name. No Critical or High finding survived; two Medium findings below.

Findings

Critical

None.

High

None.

Medium

[MEDIUM] Review-safety machinery duplicated verbatim across both sweep scripts
· dimension: 3 (SOLID / clean architecture — DRY, CLAUDE.md rule 10)
· location: tools/merge-sweep.sh:201-369, tools/merge-sweep-cov.sh:233-426
· issue: This diff adds ~200 lines of new, intricate, safety-critical logic —
  review_safety_gate, lookup_review_activity, check_review_items,
  newest_review_item_after, report_review_lookup_failure, adopt_routine_update,
  head_oid, expected_head_for/reviewed_through_for, and the --expected-head /
  --reviewed-through CLI-parsing block — byte-for-byte identical in both
  merge-sweep.sh and merge-sweep-cov.sh. This is not 2-3 similar lines (rule 6's
  "extract on the third use" bar); it is a second verbatim copy of the PR's entire
  new safety contract.
· risk: The PR's own history (visible in its body) shows ~9 rounds where a fix had
  to be re-applied to both files in lockstep. Any future fix to this logic now has
  to be applied twice by hand; a fix landed in one file and missed in the other
  silently reopens the exact merge-safety hole this PR exists to close, in
  whichever script wasn't patched.
· fix: Extract the shared block into tools/lib/merge-sweep-common.sh (the two
  shared flags' arg parsing, review_safety_gate and its helpers,
  adopt_routine_update, head_oid) and source it from both scripts, keeping only
  the genuinely divergent logic (Sonar-aware admin override vs. strict
  single-shot) local to each file.
· reference: CLAUDE.md rule 10; rubric.md dimension 3.
[MEDIUM] New reviewThreads-pagination fail-closed path has zero test coverage
· dimension: 15 (Harness changes need EXECUTED evidence)
· location: tools/merge-sweep.sh:295-298, tools/merge-sweep-cov.sh:317-320
  (mirrored gap in tools/test-tools.mjs's mergeSweepCases)
· issue: review_safety_gate's reviewThreads query caps at first:100 and, when
  pageInfo.hasNextPage is true (a PR with more than 100 review threads), the jq
  filter returns the literal string "PAGINATED" instead of a count. The shell
  then hits case "$unresolved" in '' | *[!0-9]*), correctly failing closed as
  REVIEW-LOOKUP-FAILED source=reviewThreads. This is a genuinely new decision
  path, and a real scenario for this exact workflow — PR 625 itself went through
  nine-plus review rounds by its own body. I confirmed tools/test-tools.mjs never
  sets unresolvedThreads to a non-numeric value (only "0"/"2"), and the string
  "PAGINATED" appears nowhere in the file, so the hermetic gh stub can never
  produce it. The path is fail-closed today (never an unsafe merge), but it is
  unexercised.
· fix: Add a case to mergeSweepCases that sets unresolvedThreads: "PAGINATED" and
  asserts SKIP #615 REVIEW-LOOKUP-FAILED source=reviewThreads, for both scripts.
· reference: rubric.md dimension 15; TESTING.md Harness Execution job.

Low / Info

[INFO] --match-head-commit and the gh api graphql pagination usage verified against a live gh 2.96.0 install
· dimension: 1 (Correctness)
· issue: None — praise. man gh-pr-merge confirms --match-head-commit <SHA> is a
  real, documented flag, and man gh-api confirms the --paginate/--slurp/-F/-f/--jq
  combinations used for the GraphQL reviews connection and the REST
  inline/issue-comments endpoints match the CLI's documented pagination contract
  exactly. This is exactly the class of bug a fully-hermetic test suite cannot
  catch, so I checked it by hand rather than trusting the diff read.

Subagents

Agent Verdict
parity-checker N/A — no apps/web/** or apps/mobile/** file changed
i18n-syncer N/A — no user-facing strings or i18n JSON changed
contract-aligner N/A — no shared types/DTOs changed; orbit-api untouched
security-reviewer N/A — no orbit-api code changed
design-reviewer N/A — no apps/* or landing-page UI file changed

Validation

Local session; could not re-run npm run lint/type-check/test or the harness
independently (sandbox blocked git worktree add/a separate local checkout of the
PR branch). Relied on the PR body's own extensive, repeated gate output instead —
most recently node tools/test-tools.mjs giving ORBIT TOOLS GATE OK and
node .claude/hooks/test-hooks.mjs giving ORBIT HOOK PARITY OK, both exit 0 —
while independently reading and tracing the current ~600-line mergeSweepCases
suite in tools/test-tools.mjs against both scripts' logic by hand (see the
Medium findings above for what that turned up).

Deferred

  • Dimensions 8/9/10/11/13/14 (DESIGN.md, Parity, i18n, Contract drift, Backend
    hard rules, FEATURES.md): N/A, this diff touches no apps/*, packages/shared, or
    orbit-api surface.
  • Dimension 6 (Type safety): N/A in the TS/C# sense — changed files are POSIX sh
    and a plain untyped .mjs tool script, the established tools/ convention.
  • All four changed files got a verdict; nothing changed-but-unreviewed.
  • Local lint/type-check/test/harness re-execution: not run this session (sandbox
    constraint noted above under Validation).

What's good

  • The --match-head-commit atomic merge, the pre/post review_safety_gate pairing,
    and adopt_routine_update's parent-check (distinguishing a routine
    update-branch merge from an adversarial or unrelated head change) together
    close the exact race the script's own header comment documents
    (orbit-api#403: a stale APPROVED shipping a CHANGES_REQUESTED finding).
  • The --reviewed-through cutoff is exclusive (>=) and covers three independently
    fail-closed activity sources (reviews via GraphQL
    submittedAt/updatedAt/lastEditedAt, inline comments, conversation comments),
    each with its own lookup-failure case in tools/test-tools.mjs — genuinely
    thorough boundary testing (exactly-at, strictly-after, strictly-before, per
    source, per script).
  • merge-sweep.sh structurally cannot accept --admin (unrecognized flags hit the
    --*) "unknown argument" branch), enforcing in code, not just in prose, that
    the unattended --sleep path can never bypass a red check.
  • The exit-4 post-merge recheck honestly reports what it cannot prevent (the
    residual response-to-merge race) rather than pretending the pre-merge check
    was sufficient.

Recommendation

Safe to merge as-is — nothing found rises to Critical or High. The two Medium
findings (duplicated safety logic across the two scripts, and the untested

100-review-threads fail-closed path) are legitimate but don't block: worth a
fast follow-up so the next change to this machinery has one copy to edit and one
more real decision path gets its own test case.

Copy link
Copy Markdown
Owner Author

@codex review

@claude claude 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.

test

@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: b0ce9ac5a4

ℹ️ 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 tools/merge-sweep.sh
Comment on lines +366 to +368
printf '%s\n' "$parents" | grep -Fxq "$old_expected" || return 1
printf '%s\n' "$parents" | grep -Fxq "$base_tip" || return 1
UPDATED_EXPECTED="$UPDATED_ACTUAL"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Reject head replacements based only on parentage

When another actor pushes while gh pr update-branch is in flight, the resulting commit can name both the recorded head and current base tip as parents while containing an arbitrary tree, such as additional edits or custom conflict resolutions. These checks then promote that commit to UPDATED_EXPECTED, so the unattended sweep can merge it without rerunning the skill-only head gates, including visible-effect evidence validation. Treat every changed head as HEAD-MOVED and require the caller to repeat the full preflight rather than authenticating an update from parent SHAs alone; the same logic in merge-sweep-cov.sh needs the same correction.

Useful? React with 👍 / 👎.

@claude claude 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.

Code Review: PR #625 (ORB-106: Wire sleep orchestration to strict merge sweep)

Scope: PR #625 in thomasluizon/orbit-ui-mobile, head b0ce9ac5a4e15a7fa9411de2eeb96671ab40855b.
Recommendation: APPROVE

(Note: a review posted moments ago on this PR with body "test" was a tool-call mistake made during this same review pass, not a verdict — please disregard it; this review supersedes it.)

Summary

The diff is confined to orchestration tooling: .claude/skills/orchestrate/SKILL.md, tools/merge-sweep.sh, tools/merge-sweep-cov.sh, and their harness coverage in tools/test-tools.mjs. It threads a per-PR expected-head SHA and a --reviewed-through review-activity cutoff through both merge-sweep scripts, adds --match-head-commit for an atomic server-side head assertion, requires every status check (not just required ones) to reach a terminal state, and adds a post-merge residual-race check that reports (but cannot prevent) activity landing between the last safety read and the merge call. No apps/*, packages/shared, or orbit-api surface is touched, so platform-parity, i18n, contract-drift, DESIGN.md, and backend-hard-rule dimensions are all out of this diff's surface (see Deferred). The new logic is heavily covered by new cases in tools/test-tools.mjs, and CI's Harness Execution job passed on this exact head. No Critical or High finding survived review; three Medium findings are concrete and actionable but don't block merge.

Findings

Critical

None.

High

None.

Medium

[MEDIUM] Missing test case for the initial head-lookup failure ("both unavailable") path
· dimension: 15 (Harness changes need EXECUTED evidence)
· location: tools/merge-sweep.sh:394-402 (mirrored at tools/merge-sweep-cov.sh:428-436)
· issue: When no --expected-head mapping is supplied AND the first head_oid "$n" call itself fails (network/auth hiccup, PR not found, etc.), both expected and the fallback head_oid read are empty, and the script prints SKIP #$n HEAD-MOVED expected=<unavailable> actual=<unavailable>. This is a new, distinct decision path introduced by this PR's expected-head mechanism. grep -n "expected=<unavailable>" tools/test-tools.mjs returns nothing — no case in mergeSweepCases exercises it.
· risk: A regression here (e.g. a future refactor that drops the second if [ -z "$expected" ] guard) would ship unexecuted, exactly the hole dimension 15 exists to catch.
· fix: Add a mergeSweepCases case where the gh stub's pr view --json headRefOid branch returns empty for one PR with no --expected-head mapping supplied, and assert the exact SKIP #<n> HEAD-MOVED expected=<unavailable> actual=<unavailable> output with zero merge calls.
· reference: rubric.md dimension 15 ("a new decision path... needs its own case, not an extension of an existing assertion")

[MEDIUM] Missing test case for the reviewThreads pagination-overflow fail-closed branch
· dimension: 15 (Harness changes need EXECUTED evidence)
· location: tools/merge-sweep.sh:295-298 (mirrored at tools/merge-sweep-cov.sh:317-320)
· issue: The reviewThreads GraphQL query is capped at first:100 with no --paginate, and the --jq filter deliberately emits the literal string "PAGINATED" instead of a count when pageInfo.hasNextPage is true; review_safety_gate then treats that as an unparseable count and fails closed. This is a real, new fail-closed branch (any PR with >100 review threads — and this very PR's own history shows nine-plus review rounds). Neither tools/test-tools.mjs nor its gh stub ever produces "PAGINATED" for unresolvedThreads.
· risk: If this branch silently regressed (sentinel string changed, or the numeric-only case guard loosened), a PR with heavy review activity could fail-closed forever (denial of service on legitimate merges) or, worse, parse "PAGINATED" as falsy/zero and merge past unresolved threads. Neither failure mode would be caught before shipping.
· fix: Add a case to mergeSweepCases where the reviewThreads GraphQL stub reports hasNextPage:true, and assert SKIP #<n> REVIEW-LOOKUP-FAILED source=reviewThreads with zero merges, for both scripts.
· reference: rubric.md dimension 15

[MEDIUM] ~150-200 lines of review-safety-gate logic duplicated verbatim between the two scripts
· dimension: 3 (SOLID / clean architecture — DRY at the right level)
· location: tools/merge-sweep.sh:211-369 is byte-for-byte identical (aside from the merge-sweep.sh: vs merge-sweep-cov.sh: error-message prefix) to tools/merge-sweep-cov.sh:233-416: reviewed_through_for, newest_review_item_after, report_review_lookup_failure, lookup_review_activity, check_review_items, review_safety_gate, head_oid, adopt_routine_update, and expected_head_for.
· issue: This PR is what introduces this specific block into both files — it is new functionality, not an existing pattern being extended. The security-critical review-reconciliation gate, the one mechanism standing between an unattended sweep and merging past an unresolved or edited review, now exists in two copies that must be kept identical by hand.
· risk: A future fix (a new activity source, a cutoff-comparison bug, a new fail-closed case) landing in one script and not its twin silently reopens the exact race this PR was written to close, in whichever script was missed. The PR's own round-by-round history shows this exact lockstep-maintenance cost already happening repeatedly during development.
· fix: Extract the shared functions into a single sourced library, e.g. tools/lib/merge-sweep-safety.sh, and have both scripts source it. gate()/mstate() and the Sonar-specific admin-override logic can stay script-local since they genuinely differ.
· reference: CLAUDE.md rule 10

Low / Info

[INFO] The pending computation in gate()/mstate() also lists the review check itself as pending while it runs, which overwrites block_reason with the generic "checks... never all concluded" text instead of the more specific stale-review message. Cosmetic only — the merge is correctly blocked either way (review_stale still gates it) — not actionable per the Signal gate.

Subagents

Agent Verdict
parity-checker N/A — no apps/web/** or apps/mobile/** file changed
i18n-syncer N/A — no user-facing strings or i18n JSON changed
contract-aligner N/A — no packages/shared/src/types/*/endpoints.ts or orbit-api DTO changed
security-reviewer N/A — no orbit-api code changed
design-reviewer N/A — no apps/*/orbit-landing-page/src/** UI file changed

Manual security pass (dimension 12, since no subagent fires for pure tooling): all new gh api graphql calls pass repo owner/name/PR number as parameterized -F GraphQL variables, never string-interpolated into the query text, so there is no injection surface from attacker-controlled review content flowing back only into echo output. --admin remains confined to merge-sweep-cov.sh's coverage-only path and is never reachable from --sleep, matching root CLAUDE.md's "Never --admin" convention for the unattended path. No secrets or credentials appear in the diff.

Validation

Local execution (node/bash/gh pr checks) was unavailable in this review session. Falling back to GitHub Actions' own run against this exact head (b0ce9ac5a4e15a7fa9411de2eeb96671ab40855b), fetched via gh pr view --json statusCheckRollup:

Check Result
Lint PASS (CI)
Type Check PASS (CI)
Unit Tests PASS (CI)
Harness Execution (test-tools.mjs + test-hooks.mjs) PASS (CI)
Harness Lockstep PASS (CI)
SonarCloud Code Analysis PASS (CI)
Build (api) N/A — orbit-api not touched

This satisfies dimension 15's execution-evidence bar via an independent CI run rather than a self-reported claim, but is not a rerun performed by this review pass itself — stated honestly rather than presented as something this session executed.

Deferred — N/A dimensions & files not verdicted

  • Dimensions 8 (DESIGN.md/AI-slop), 9 (parity), 10 (i18n), 13 (backend hard rules), 14 (FEATURES.md parity): N/A, diff touches no apps/*, orbit-api, or packages/shared surface these dimensions gate on.
  • Dimension 11 (contract drift + backward-compat): N/A, no packages/shared/src/types/* or orbit-api DTO changed.
  • Local validation re-run (Phase 7): deferred to CI's own result (see Validation table) because this session could not execute node/bash locally.
  • All four changed files (SKILL.md, merge-sweep.sh, merge-sweep-cov.sh, test-tools.mjs) received a verdict above; nothing in the changed-file inventory was left unexamined.

What's good

  • The expected-head + --match-head-commit mechanism closes a real race (an independent actor advancing the base or a PR's head between the sweep's last read and its merge call) with a server-side atomic assertion, not just a client-side re-check.
  • The --reviewed-through gate treats every author (including bots and the orchestrator itself) as reconcilable activity, with no author-based exemption that could hide a late finding.
  • Test coverage for this diff's core new behavior (matching/changed head, atomic merge-race refusal, routine-update adoption vs. adversarial rejection, exclusive-cutoff boundary cases across all three activity sources, post-merge residual-race reporting) is thorough and specific, not just happy-path.
  • The scripts fail closed by default on every new lookup (thread count, reviews, inline comments, issue comments), consistent with the script's established fail-closed convention.

Recommendation

Approve as-is. The two dimension-15 test-coverage gaps and the cross-script duplication are real but Medium: none block a merge tool that is otherwise well-tested and already green in CI. Worth a quick follow-up to add the two missing cases and consider the shared-lib extraction before a third script ever needs this same safety gate.

thomasluizon added a commit that referenced this pull request Jul 28, 2026
* chore(tools): reject acceptance criteria with no provable finish line

check-ticket.mjs now fails an acceptance criterion that quantifies over an
open set (every/all/any/each with nothing in the same criterion bounding it)
or trails off into an unnamed remainder (etc., and so on, ellipsis).

Measured cause: ORB-122 (PR #633) took 24 review rounds and 12 hours because
"block every phrasing an agent could emit" is not a set anyone can enumerate,
so every round the reviewer legitimately found one more member and was right.
ORB-106 (PR #625) shows the same shape at 32 reviews. A criterion that names
its enumeration (a count, a named list, a file, or a backticked command) has a
finish line; one that does not cannot be proven done by anyone.

The gate runs at every point a ticket already passes through: /ticket and
/feature on the draft, /orchestrate again per ticket at wave-plan time, and
the four /audit-* skills plus /prod-readiness on what they propose.

node tools/test-tools.mjs      -> ORBIT TOOLS GATE OK
node .claude/hooks/test-hooks.mjs -> ORBIT HOOK PARITY OK

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3TtwQhtWZdAndmpfmf8rC

* fix(tools): tie the bound to the clause its quantifier is in

Review finding on PR #638: BOUNDED_BY searched the whole criterion, so an
unrelated token rescued an unbounded claim. Measured bypass, which the gate
accepted before this commit and rejects after it:

  "every phrasing a worker could emit is blocked and the command exits 1"

The stray 1 satisfied the bound while the unprovable finish line survived
untouched. The bound now has to appear in the same clause as the quantifier.
Code spans and file paths are masked before the split so a backticked command
containing dots is not cut in half by its own extension.

Six measured cases through the real tool, all correct: the bypass above and
"...blocked, see tools/check-ticket.mjs" reject; "every phrasing rejected by
`node tools/check-ticket.mjs` is blocked", "The three docs each carry a WHY
note" and "all 5 call sites route through the shared reader" pass; "any single
check failing produces a FAIL row" rejects.

node tools/test-tools.mjs      -> ORBIT TOOLS GATE OK
node .claude/hooks/test-hooks.mjs -> ORBIT HOOK PARITY OK

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3TtwQhtWZdAndmpfmf8rC

* fix(tools): accept a bound stated before its quantifier, and hyphenated compounds

Two precision defects found by running the gate against the three ticket
bodies still queued in this run, before their workers start.

ORB-107: "A clean environment produces an all-PASS table and exit 0" was
rejected because "all-PASS" is a hyphenated compound adjective, not a
quantifier. The quantifier now requires no following hyphen.

ORB-120: "`design-specialist.md` and `product-manager.md`, which currently
declare no `model:`, each gain one" was rejected because the two named files
sit in clauses BEFORE the one holding "each". A bound now counts when it
appears in the quantifier's own clause or any clause preceding it, never in a
clause after it, which is exactly the direction the PR #638 review bypass
exploited ("...is blocked and the command exits 1").

Verdicts after this commit, via --issue against the live bodies:
ORB-89 clean, ORB-107 "ticket ok", ORB-120 clean.

node tools/test-tools.mjs      -> ORBIT TOOLS GATE OK
node .claude/hooks/test-hooks.mjs -> ORBIT HOOK PARITY OK

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G3TtwQhtWZdAndmpfmf8rC

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@claude claude 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.

Review Complete

Scope: PR #625 in thomasluizon/orbit-ui-mobile — ORB-106: Wire sleep orchestration to strict merge sweep
Recommendation: APPROVE

Severity Count
Critical (incl. ⚠️ old-client breaks) 0
High 0
Medium 2
Low / Info 0

Summary

Pure tooling change — tools/merge-sweep.sh / tools/merge-sweep-cov.sh gain --expected-head + --reviewed-through pinning, an atomic --match-head-commit merge assertion, a fail-closed review/inline/issue-comment reconciliation gate with exclusive-cutoff semantics, post-merge race detection (exit 4), and a stricter "every check must conclude" rule; .claude/skills/orchestrate/SKILL.md documents the new --sleep protocol. No apps/*, orbit-api, or packages/shared files touched, so parity/i18n/DESIGN.md/contract-drift/backend-hard-rules/FEATURES.md are all N/A and none of the five gated subagents fire.

The merge/skip control flow in both scripts (head-pin match/mismatch, the "routine update adopted only if parents include both the old expected head and the base tip" adversarial-update rejection, the review-safety gate called immediately pre- and post-merge, exit-code precedence) was hand-traced and no path was found that lets a stale-headed or unreviewed PR merge. Test coverage in tools/test-tools.mjs (mergeSweepCases) maps to every new decision path found by reading the scripts.

Findings (Medium, both concrete follow-ups, neither blocking)

  1. tools/README.md catalog rows (lines 22-23) are stale. The merge-sweep.sh/merge-sweep-cov.sh entries still show the pre-PR usage (bash merge-sweep.sh <repo> <pr...>) with no mention of --expected-head, --reviewed-through, --match-head-commit, or exit code 4 — even though both scripts' own --help text and SKILL.md were updated correctly. The PR's "no tools/README.md row was missing" note addresses row presence, not row accuracy.
  2. The review-safety-gate helpers (~150 lines: review_safety_gate, check_review_items, newest_review_item_after, adopt_routine_update, etc.) are byte-identical between the two scripts, unlike this repo's other intentional "twin" files (e.g. second-opinion.mjs), which are protected by check-lockstep.mjs. Nothing in CI would catch the two copies drifting apart on a future fix.

Validation

Not independently re-run in this session (npm/node execution declined by the sandbox's permission system on every attempt; per the CI-adaptation instructions, Phase 6//validate is skipped here since Build/Unit Tests/SonarCloud run as separate required checks). Relying on the PR body's own Round 9 evidence (node tools/test-tools.mjsORBIT TOOLS GATE OK, node .claude/hooks/test-hooks.mjsORBIT HOOK PARITY OK on the final commit), cross-checked by reading the added test cases directly rather than trusting the claim at face value.

Deferred / not verifiable in CI

Dimensions 8, 9, 10, 11, 13, 14 — all N/A, diff surface never touches apps/*, i18n, packages/shared/types, or orbit-api. Cross-repo dimensions (contract-aligner, orbit-api side of backward-compat guard) are not applicable — no orbit-api changes in this diff, and the sibling repo is not checked out in this job regardless. All four changed files got a full verdict; nothing deferred at the file level.

@thomasluizon
thomasluizon merged commit edccbe8 into main Jul 28, 2026
30 checks passed
@thomasluizon
thomasluizon deleted the feature/orb-106-make-orchestrate-sleep-call-the-merge branch July 28, 2026 15:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant