Repository navigation
chore(ci): CI orchestration hygiene + dependency-review (#420, #416) - #429
Conversation
… review (#420, #416) #420 — concurrency (cancel-in-progress) on PR workflows (test, claude-review, and sonarcloud PR-only); .turbo actions/cache on turbo jobs; nightly.yml hosting #417's full-scope mutation report; deploy/release workflows left uncancelled. #416 (ui slice) — dependency-review.yml (fail-on-severity high) for orbit-ui-mobile. Must merge LAST (references the Lint/Dependency-Review/mutation checks the other PRs add). Closes #420 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Editing claude-review.yml makes the claude-code-action self-skip the PR (reports pass but posts no review), which blocks the required approval. The concurrency block stays on test.yml + sonarcloud.yml where it matters. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review: PR #429
Scope: PR #429 in thomasluizon/orbit-ui-mobile (chore(ci): CI orchestration hygiene + dependency-review (#420, #416))
Recommendation: APPROVE
Summary
Pure CI-configuration diff: adds .github/workflows/dependency-review.yml and .github/workflows/nightly.yml, and adds concurrency + turbo actions/cache steps to sonarcloud.yml and test.yml. No apps/*, packages/shared source, or orbit-api files change, so the UI/parity/i18n/contract/security/backend-hard-rule dimensions are all N/A. The workflow logic itself (cancel-in-progress scoping, cache key scoping) is correct and consistent with the repo's existing conventions. One Medium cross-PR coordination gap was found and verified.
Findings
Critical
None
High
None
Medium
[MEDIUM] nightly.yml duplicates the nightly full-scope mutation job already shipped in PR #427's mutation.yml
· dimension: 1. Correctness (cross-boundary / cross-PR)
· location: orbit-ui-mobile/.github/workflows/nightly.yml:11-35
· issue: This PR adds a new `mutation` job on `schedule: cron: '0 3 * * *'` that runs `npm run mutation` (full scope) in packages/shared and uploads an artifact named `mutation-report-nightly`. PR #427 (open, branch issue-417, same base commit 8ba9c750) independently adds `.github/workflows/mutation.yml`, which already contains a `nightly:` job (`if: github.event_name == 'schedule'`, its own `cron: '0 6 * * *'`) doing the exact same full-scope `npm run mutation` run and uploading an artifact with the identical name/path. Neither PR's diff removes or coordinates with the other's nightly job. Additionally, `packages/shared/package.json` has no `mutation` script and there is no `packages/shared/stryker.config.json` on current main (both only exist on #427) — confirmed via `git show origin/main:packages/shared/package.json` and a repo-wide search for `stryker.config*`. nightly.yml is already live on this branch's merge ref (HEAD 61c4cba0) and would fail nightly with "Missing script: mutation" until #427 lands.
· risk: If #429 merges before #427, the new nightly job fails every night (harmless — non-blocking, report-only) until #427 lands. Once both PRs are on main, main runs the identical full-scope mutation test twice nightly (03:00 and 06:00 UTC), doubling CI minutes for zero benefit and producing two same-named `mutation-report-nightly` artifacts from different workflow runs — the exact kind of redundant CI this PR's own title ("dedupe PR runs") is meant to eliminate.
· fix: Coordinate the two PRs before both land: either drop the `nightly:` job (and the `schedule:` trigger) from #427's mutation.yml now that #429's nightly.yml is meant to own it (per this PR's own body: "nightly.yml … hosting #417's full-scope mutation report run"), or drop nightly.yml's `mutation` job from this PR and let #427's mutation.yml keep owning it. Whichever PR merges second should carry the removal.
· reference: PR #429 body ("nightly.yml … hosting #417's full-scope mutation report run"); CLAUDE.md rule 10 (DRY at the right level — one nightly mutation job, not two)
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, no orbit-api changes |
| security-reviewer | N/A — no orbit-api code changed |
| design-reviewer | N/A — no apps/* or landing-page UI files changed |
Validation
| Check | Result |
|---|---|
| Lint | N/A — diff touches only .github/workflows/*.yml, no lintable source |
| Type check | N/A — no TS/C# source changed |
| Tests | N/A — no source changed; nothing to exercise |
| Build (api) | N/A — orbit-api untouched |
| YAML well-formedness | Manually verified — all 4 files parse as valid, consistently-indented GitHub Actions YAML matching the repo's existing workflow style (automated js-yaml/python -c parse was blocked by sandbox tool approval, so this was a careful manual read rather than a tool-run check) |
Deferred — N/A dimensions & files not verdicted
- Dimensions 2 (dead/stale code), 3 (SOLID/clean-arch), 4 (comment policy), 5 (no-workaround), 6 (type safety), 7 (no console.log), 8 (DESIGN.md/AI-slop), 9 (parity), 10 (i18n), 11 (contract drift), 12 (security — frontend categories), 13 (backend hard rules), 14 (FEATURES.md parity): all N/A — this diff's only surface is
.github/workflows/*.yml; none of these dimensions' trigger surfaces are touched. - All 4 changed files (
dependency-review.yml,nightly.yml,sonarcloud.yml,test.yml) were read in full and given a verdict — nothing deferred among changed files. - Automated YAML parsing (
python3/node -e) was blocked by sandbox permission prompts in this session; substituted with a manual structural read of all 4 files instead. - Writing the report to the skill's default
.claude/reviews/output path was also blocked by a sandbox sensitive-file permission gate; the report was written to/tmp/pr-429-review.mdinstead (the same fallback location the CI reviewer wrapper itself uses). - The
claude-review.ymlconcurrency change mentioned in the PR body was investigated: it was added then reverted within this same PR's commit history (5dd3984bthen82303d1b, see commit message "Editing claude-review.yml makes the claude-code-action self-skip the PR") and nets to no diff against main — confirmed viagh pr diff 429, which correctly shows noclaude-review.ymlhunk. Not a gap, just resolved before the diff was cut.
What's good
concurrencyscoping is correctly differentiated:test.yml(PR-only trigger) cancels unconditionally;sonarcloud.yml(push-to-main + PR trigger) conditionscancel-in-progressongithub.event_name == 'pull_request'so a main-push SonarCloud analysis is never cancelled mid-run — exactly matches the PR body's stated intent.- Turbo cache steps are consistently placed (after
npm ci, before the turbo command) and correctly job-scoped via${{ github.job }}in the cache key across all four jobs they touch, avoiding cross-job cache pollution. dependency-review.ymlandnightly.ymlboth declare minimalpermissions: contents: read.- No script-injection surface introduced (no untrusted
github.event.*context interpolated into arun:block). - The commit history shows the author already caught and reverted one real problem mid-PR (claude-review.yml self-skip) — good self-correction.
Recommendation
Approve as-is; the one Medium finding is a cross-PR coordination item between #429 and the still-open #427, not a defect in this PR's own code, and is safe to resolve as a fast follow-up (drop the duplicate nightly: job from whichever of #429/#427 merges second) rather than blocking this PR.
Avoids running the full-scope mutation report twice nightly (mutation.yml 06:00 + nightly.yml 03:00). nightly.yml is the dedicated home for slow gates. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review: PR #429
Scope: PR #429 in thomasluizon/orbit-ui-mobile (already merged as 85d95a22)
Recommendation: APPROVE
Summary
CI-orchestration hygiene PR: adds dependency-review.yml (fail-on-severity high), adds
concurrency/turbo-cache to test.yml and sonarcloud.yml, and splits the nightly
full-scope mutation run out of mutation.yml into a new nightly.yml. The diff touches
only .github/workflows/*.yml — no application source, no packages/shared contract
surface, no apps/* UI. All five files are internally consistent with the PR's stated
intent and with each other (action versions, cache-key shape, trigger scoping).
Findings
Critical
None.
High
None.
Medium
[MEDIUM] nightly.yml lost the concurrency guard the old nightly job had
· dimension: 3 (SOLID/clean-arch — pattern consistency) / defense-in-depth gap
· location: .github/workflows/nightly.yml:1-12
· issue: In the old mutation.yml, the nightly job was covered by the file-level
concurrency: { group: mutation-${{ github.workflow }}-${{ github.ref }}, cancel-in-progress: true }
block (schedule events all share ref = refs/heads/main, so a slow run would be
protected). The new standalone nightly.yml carries no concurrency: block at all.
· risk: A workflow_dispatch fired manually while the 03:00 cron run is still in
flight (or a cron run that overruns 24h) launches two concurrent full-scope Stryker
runs writing to the same mutation-report-nightly artifact name and consuming double
the runner minutes. Low probability, no correctness break, but the safeguard the old
file had was silently dropped in the split.
· fix: add
concurrency:
group: nightly-mutation
cancel-in-progress: falseto nightly.yml (queue rather than cancel, since this is a report job you don't want
to kill mid-run).
· reference: CLAUDE.md rule 10 (pattern consistency when splitting a file) / severity
ladder "defense-in-depth gap"
Low / Info
[INFO] Turbo cache keys are ${{ runner.os }}-turbo-${{ github.job }}-${{ github.sha }}
with a prefix restore-keys fallback — correct pattern for turbo's own incremental
cache (exact-sha key will always miss on a fresh run; the restore-keys prefix match is
what actually pays off). No action needed, called out because it looks at first glance
like a cache that can never hit.
[INFO] dependency-review.yml, actions/cache@v4, and actions/setup-node@v6 are
pinned by tag, not commit SHA, while sonarcloud.yml's third-party
SonarSource/sonarqube-scan-action is pinned by full SHA. This matches the repo's
existing convention (first-party actions/* by tag, third-party orgs by SHA) — not a
new deviation introduced by this PR.
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, no orbit-api change |
| 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 | N/A — diff is CI-workflow YAML only, not covered by repo lint |
| Type check | N/A — no TS/C# source in the diff |
| Tests | N/A — no application test surface touched |
| Build (api) | N/A — orbit-api not touched |
| YAML syntax (manual read of all 5 files) | PASS — well-formed, consistent indentation, matches GitHub Actions schema |
actionlint / automated YAML validator |
Not available in this sandbox — deferred, see below |
Deferred — N/A dimensions & files not verdicted
- Dimensions 8 (DESIGN.md/AI-slop), 9 (Parity), 10 (i18n), 11 (Contract drift +
backward-compat), 13 (Backend hard rules), 14 (FEATURES.md parity): all gated N/A —
their triggering surfaces (apps/*UI,packages/sharedtypes,orbit-api,
user-facing feature surface) are untouched by this diff. - Dimensions 6 (type safety) and 7 (
console.log): N/A — no TS/C#/application code in
the diff, only YAML. - All five subagents: N/A per the gate table above — none of their trigger conditions
were met. - Automated workflow-syntax validation (
actionlint): not installed in this sandbox,
and shell invocations that would have run a YAML parser (python3 -c) were blocked by
the environment's approval gate — relied on manual read-through instead. Every changed
file was still read and given a verdict. - Branch-protection follow-up the PR body calls for (registering
Lint/
Dependency Reviewas required checks) is an operational step outside the diff itself
— not verified here; flagged as Info, not a code-review finding. - This report could not be written to the repo's own
.claude/reviews/directory: the
sandbox's file-permission layer flags anything under.claude/as a protected path
and blocked bothmkdirandWritethere without an interactive approval this
headless run has no way to grant. Written to/tmp/pr-429-review.mdinstead; full
content is also relayed in this session's final message.
Every one of the 5 changed files (dependency-review.yml, mutation.yml, nightly.yml,
sonarcloud.yml, test.yml) received an explicit verdict above — nothing changed was
skipped.
What's good
- Correctly extracts the nightly full-scope mutation job out of the per-PR workflow
instead of leaving aschedule:trigger bolted onto a PR-triggered file — cleaner
separation of concerns, matches the PR's own stated intent. sonarcloud.yml's conditionalcancel-in-progress: ${{ github.event_name == 'pull_request' }}
is the right call — a push-to-main SonarCloud analysis is exactly the kind of run you
never want silently cancelled by a subsequent push.- Deploy/release workflows (smoke-prod, promote-prod, android-release) were deliberately
left out of the cancel-in-progress rollout, per the PR body — correct, cancelling a
live deploy mid-flight would be far worse than a duplicate CI run. dependency-review.ymluses minimalpermissions: contents: read— no
over-provisioned token scope.
Recommendation
Approve as-is. The one Medium (missing concurrency guard on nightly.yml) is a
low-blast-radius, easily-fixed follow-up — worth a quick one-line addition in a later
commit but not worth blocking this merge over, and the PR is already merged
(85d95a22). No Critical or High findings survive review.
|
… lcov) (#463) The Sonar job's persisted turbo cache (added #429) replayed the `test` task instead of running vitest, so no lcov was generated and the merged coverage/lcov.info was empty -> Sonar reported 0.0% (regressed from ~56%). Declare coverage/** as the test task's turbo outputs and move each workspace's vitest reportsDirectory inside its package so a cache hit restores real coverage; repoint the sonarcloud.yml merge step at the in-package lcov paths. Also exclude the generated Zod snapshot from coverage. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(tools): verify delivery on unprotected bases (#429) * fix: observe unprotected check registration before delivery Require a nonempty rollup to remain unchanged for 60 seconds so one fast successful check cannot bypass --wait-ci before other checks register. Cover registration growth, eventual delivery, empty rollups, and resets. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YGvZW4Q7DU8wCaieYGrzgT * fix: preserve readiness gates on unprotected bases Share confirmed protection-404 classification with delivery verification. Keep live CI and app-pinned independent review mandatory for readiness, and cover unprotected failures alongside the protected-base regression cases. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YGvZW4Q7DU8wCaieYGrzgT * fix: bind readiness to observed check registration --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
The claude engine assertion checked that `--strict-mcp-config` is present and never that `--mcp-config` is absent, so adding the flag left every user-scope MCP server loading into a worker at bypassPermissions with the test still green. Assert the absence. `record-readiness.mjs` supplies the `pullfrog-approval` axis itself when protection names no required check, which is the whole reason an unprotected base is not a free pass (#429). That rule now lives beside the reader it feeds as `reviewChecksFor`, so a test drives the real rule, and the receipt verdict it produces on `redesign/main` with no review is pinned as CI_STALE alone. #598 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Round 3 reconciled the four prose sections and left the tool saying something else. `record-readiness.mjs` supplies `pullfrog-approval` itself when protection names no required check, so on `redesign/main` a named substitute reviewer still produces a blocked receipt, and step 9 said a blocked receipt never permits a merge. One run merged and one run stopped from one tree. Step 9 now carries the single exception and the reasoning for keeping the recorder strict: it reads checks and reviews, never a claim in a comment, and a recorder that accepts an unverifiable claim is worse than a written exception a human reads (#429). Step 7 and the queue name the same branch-conditional bar by reference instead of naming Pullfrog. Hard prohibitions and §5.4.1 now say what they decide: the case where Pullfrog produced neither the check nor an APPROVED review, so neither reads as banning the #440 fallback that step 8 and the tool both allow on `main`. Also: three progress signals rather than four, the live `worker` edit is named in the handoff inventory and the step 11 report, and core.md names the file its two pointers live in. #598 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…tract (#1023) * chore: add a Claude headless worker engine and forbid subagent workers An exhausted Codex allowance stopped every code round today. It should not: the launcher already routes on the `worker` key, so a second engine is all that was missing. `.claude/orchestrator.json` now declares `claude` beside `codex`, running Claude Code headless through the same launcher, order file, worktree, timeouts and wake source. Flags were confirmed from `claude --help`, not assumed, and `--dry-run` resolves both tiers. `§5.4.1` of the orchestrate skill states the switch plainly and forbids substituting a Claude subagent for a worker. Measured today: five subagents ran as workers, registered no wake source so the Stop hook read the run as having no continuation, and each had to be told to commit before a handoff was possible, because a subagent dies with its session and leaves its edits uncommitted in a worktree with no record of what they were. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix: keep codex as the committed default worker tools/test-tools.mjs pins the shipped default implementer to gpt-5.6-sol at high reasoning effort, and committing worker="claude" broke both of those assertions. The gate is right: the shipped default belongs in the file, and running on Claude is an operational switch a session makes locally while the Codex allowance is out, not a new default. D95 forbids editing the gate a run is judged by, so the assertion stands and the config yields. Harness went from 61 failures to 1 with this change. The remaining one is create-worktree.mjs hitting EPERM on a lock file under Temp in its concurrency case, which touches nothing here. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * chore: isolate the claude worker engine and cover every declared engine The headless Claude engine inherited every user-scope MCP server, because --permission-mode bypassPermissions removes the interactive approval and the PreToolUse hooks match Bash and PowerShell only. --strict-mcp-config with no --mcp-config beside it loads zero servers instead. --output-format stream-json keeps the launcher's log-growth progress signal live, so a worker that spends a long model turn without touching the tree is no longer killed as KILLED_NO_PROGRESS. The installed binary refuses stream-json without --verbose. The config reader validates config.workers[config.worker] only, so a typo in the fallback engine surfaced at the first real launch, after reserveWorkerLaunch had spent one of the two launches the branch cap allows. The gate now resolves every declared tier of every declared engine. Refs #598 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: say what the claude worker engine means when an operator sets it core.md loads on every turn and read "Claude never edits code", full stop, so a session that hit an exhausted allowance read that first and stopped the night. That is the ending D104 exists to prevent. It now points at the fallback. Five corrections inside the section the tree contradicted: the launcher's pid is the wake source with the worker's pid beside it, a subagent does NOT end the night silently because the Stop hook blocks, the --dry-run is an argv check rather than a preflight, the calibration stamp goes stale for the duration of the switch and nobody reseeds it, and the worker edit is uncommitted with git checkout -- .claude/orchestrator.json as its revert. The staleness trap in assertNotStale is named beside them, because it fires on the config edit itself. The reviewer clause turns on the word quietly. On protected main no substitute may stand in for pullfrog-approval. On unprotected redesign/main no such check exists to forge, so a separate-agent review is acceptable when the pull request comment names the substitution and lists checkable evidence. The skill is calibrated, so its verdict is rewritten for the new text and the stamp reseeded: 1 entry renewed, 31 carried forward, worker still codex. Refs #598 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: make the merge bar say one thing in all four places Round 3 on #598. The reviewer clause 5.4.1 gained in round 2 contradicted three other sections of the same file, so a night run reached a different merge answer depending on which section it read. - Step 8, Hard prohibitions and 5.4.1 now state one branch-dependent rule: absolute on protected main, a named separate-agent review admissible on unprotected redesign/main, and a green check alone never an approval. - 5.4.1 records that the clause came from a written instruction at entry, not from a contract this run granted itself. - Pin the claude engine's command and both model ids by name, because resolveWorkerInvocation never reads command and reads the model from the block the loop compares against. claude-opus-4-8 is a real id, so that typo would run a whole night in silence. - Name the tools-harness red beside the calibration red, by assertion, and say neither licenses editing a test. - Correct the assertNotStale headline, the stream-json rationale, the launch-cap comment, the launcher line reference and the missing --tier mechanical dry run. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * test(tools): pin the MCP absence and the unprotected review verdict The claude engine assertion checked that `--strict-mcp-config` is present and never that `--mcp-config` is absent, so adding the flag left every user-scope MCP server loading into a worker at bypassPermissions with the test still green. Assert the absence. `record-readiness.mjs` supplies the `pullfrog-approval` axis itself when protection names no required check, which is the whole reason an unprotected base is not a free pass (#429). That rule now lives beside the reader it feeds as `reviewChecksFor`, so a test drives the real rule, and the receipt verdict it produces on `redesign/main` with no review is pinned as CI_STALE alone. #598 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: make the tool and the prose agree on the merge bar Round 3 reconciled the four prose sections and left the tool saying something else. `record-readiness.mjs` supplies `pullfrog-approval` itself when protection names no required check, so on `redesign/main` a named substitute reviewer still produces a blocked receipt, and step 9 said a blocked receipt never permits a merge. One run merged and one run stopped from one tree. Step 9 now carries the single exception and the reasoning for keeping the recorder strict: it reads checks and reviews, never a claim in a comment, and a recorder that accepts an unverifiable claim is worse than a written exception a human reads (#429). Step 7 and the queue name the same branch-conditional bar by reference instead of naming Pullfrog. Hard prohibitions and §5.4.1 now say what they decide: the case where Pullfrog produced neither the check nor an APPROVED review, so neither reads as banning the #440 fallback that step 8 and the tool both allow on `main`. Also: three progress signals rather than four, the live `worker` edit is named in the handoff inventory and the step 11 report, and core.md names the file its two pointers live in. #598 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(calibration): reseed the orchestrate stamp and reconsider its verdict The merge bar moved out of prose alone and into step 9, where `record-readiness.mjs` decides. The verdict says what that costs a reader: the recorder cannot verify a substitution written in a comment, so it blocks on the review axis by design, and the run has to tell that one expected verdict apart from a real blocker reported under the same name. One entry renewed, 31 carried forward. #598 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: correct the new citations and say how the night ends `reviewChecksFor` moved the rule, so step 9 names its real home and the line the recorder calls it from. The exception paragraph now also says what happens after it: a merge admitted there closes the pull request exactly as a READY one does, and when it does not apply the run records the `blocker` string that the Stop hook already accepts as a legitimate BLOCKED ending. Reseeded after the edit: one entry renewed, 31 carried forward. #598 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(orchestrate): let a merged pull request end an unattended night A pull request merged under the step 9 exception could never end the run that merged it. The Stop hook reads the receipt and never the pull request state, and that receipt stays CI_STALE forever because pullfrog-approval will not publish on an unprotected base. Driven against checkSleepStop: a merged row with no blocker returned block:true, the 2026-08-08 deadlock reached after a successful merge. Adds `merged` as the third ledger disposition beside READY and BLOCKED. The bar is the merge commit sha, a fact the run records and a reader can check on GitHub. It is sticky where a blocker is not, because a merge cannot be undone. Also corrects two things the tree contradicted. The expected-FAIL count for the worker switch is four, not two: the earlier reading used --only orchestrator-config, which cannot see the two launch-worker pins. And the MCP absence was asserted on workers.claude.args, while the worker's real argv is the engine array concatenated with the tier's, so --mcp-config in a tier's args passed the gate. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(calibration): reseed the orchestrate stamp for the third disposition Sharpens the CI_STALE signature in step 9, then reseeds. The signature is delivery's ci.checks.pass true beside the receipt's ci.green false, and only a required check absent from the rollup entirely reads that way: one that ran and failed lands in rollup.failing, and a running one in rollup.pending, so either drops delivery's own pass. The verdict text is rewritten before the stamp, so the reseed is not re-blessing old reasoning about new prose. worker stays codex, so workerMoved is false and the other 31 dates carry forward untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(orchestrate): require a real sha for a merged row and announce that ending `merged` accepted any non-empty string, so copying the skill's own template and leaving `<merge commit sha once it is merged, or absent>` unfilled reported an UNMERGED pull request as a finished night, printing nothing. Driven against `checkSleepStop`: that literal and the bare word "yes" both returned null. Both predicates now test `/^[0-9a-f]{7,40}$/`, in the Stop hook and in the recorder, and a new assertion pins the two copies to the same literal so the lenient direction cannot drift back. A merged-only completion returns a terminal MERGED banner naming each pull request and its sha, and `require-wake-source.mjs` writes it beside the BLOCKED one, so the sha reaches a reader. Step 11 now asks for the disposition per pull request, the readiness-receipt assertion compares against the literal `pullfrog-approval` rather than the constant it imports from the module under test, and both SKILL.md and NEXT.md name the load-sensitive `launch-worker.mjs` CPU test so a fifth FAIL is checked against that name first. Refs #598 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(calibration): reseed the orchestrate stamp for the checked merge sha The skill gained a shape-checked `merged` sha, a MERGED terminal banner, the disposition per pull request in step 11, and the note that its fifth FAIL may be the load-sensitive CPU test rather than a defect. The verdict now names the call that change asks for: which self-asserted fact stays honest is the one a reader can check. `worker` stays `codex`, so `workerMoved` is false and the other 31 verdicts carry forward untouched: `stamped 32 file(s) ... 1 verdict(s) renewed, 31 carried forward`. Refs #598 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: attribute the load-sensitive FAIL to the head it was measured at This round did not reproduce `ORBIT TOOLS GATE FAILED (1)` on the CPU test: the full run passes it at 1824 assertions. The measurement belongs to the review at head `4d590110`, so both documents now say so rather than reading as this run's own observation. The mechanics stay confirmed from the tree. Refs #598 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>



Bundles #420 + the orbit-ui-mobile slice of #416.
#420 — orchestration hygiene
concurrency: {cancel-in-progress: true}on PR workflows (test.yml, claude-review.yml); sonarcloud.yml cancels only on PR events (never a main-push analysis). Deploy/release (smoke-prod, promote-prod, android-release) deliberately left uncancelled..turboactions/cacheon the turbo jobs.nightly.yml(0 3 * * *) hosting Testing: scoped mutation testing (StrykerJS) on packages/shared critical logic #417's full-scope mutation report run.#416 — GitHub-native scanning (ui slice)
dependency-review.yml(fail-on-severity: high). CodeQL + secret-scanning already enabled repo-side; landing-page's Dependabot was turned on too.All 5 workflow files YAML-validated.
🤖 Generated with Claude Code