Skip to content

Reduce mutation and guard CI jobs - #737

Merged
thomasluizon merged 5 commits into
mainfrom
thomasluizon/ticket-1521-ci-minutes
Oct 18, 2026
Merged

thomasluizon merged 5 commits into
mainfrom
thomasluizon/ticket-1521-ci-minutes

Conversation

@thomasluizon

@thomasluizon thomasluizon commented Oct 18, 2026 •

Copy link
Copy Markdown
Owner

Documentation changes currently start every mutation shard, and five small guard jobs each repeat checkout and setup. Select mutation shards before starting .NET, run all five guards as named steps in one Guards job, and avoid regenerating artifacts when their inputs did not change.

Closes thomasluizon/orbit-tickets#1521

Approach

  • .github/scripts/mutation-matrix.mjs owns the existing four shard definitions. .github/workflows/mutation.yml selects a dynamic matrix from a null-delimited, merge-base Git diff. Domain source or tests select both domain shards; Application and Infrastructure select their respective shard. Documentation under those projects is ignored. Renames and deletions retain both affected project paths. Shared build/tool/selector changes select all shards, and each Stryker config selects its project. Missing base refs fail the selector. Nightly/manual runs retain all shards and PR runs retain --since.
  • .github/workflows/guards.yml runs Workflow Syntax, Timeless Text, Dash Ban, Skill and Agent Frontmatter, and Root Allowlist after one checkout and Node setup. Every guard uses !cancelled() so earlier failures still allow the other guards to execute. PR edits, main pushes, bot-body handling, the dash baseline, and title/body checks remain covered.
  • .github/workflows/arch-map.yml and gating-matrix.yml filter both PRs and pushes to their generator inputs, tests, and workflow files. Artifact uploads remain. The gating workflow retains generate-twice determinism checking.

Workflow audit

Workflow Decision and evidence
arch-map Retain the artifact; there is no evidence that nobody reads it. tools/arch-map.mjs reads C# under src/tests and the four source project files. The new filters cover those inputs and the generator/workflow itself.
gating-matrix Retain generation, determinism, and upload for src/**/*.cs and generator/test/workflow changes. Remove its duplicate generator test step: the unchanged required Guard Conventions job in .github/workflows/test.yml runs node --test tools/__tests__/*.test.mjs on the same PRs and main pushes, including this exact test file.
benchmark Keep unchanged. It runs nightly or manually, already avoiding PR/push duplication, and its baseline comparison and artifact are distinct outputs.
codeql Keep unchanged. The three language checks are required on main. Its instrumented C# build supplies the security analysis; ordinary API builds do not produce that analysis. Main and weekly runs retain baseline coverage.
sonarcloud Keep unchanged. SonarCloud Code Analysis is required on main. Scanner-instrumented builds plus C#/infra coverage feed that check, so the ordinary Unit Tests job cannot replace this work without a separate coverage handoff.
dependency-review Keep unchanged. Dependency Review is required on main and checks the PR dependency delta; Dependency Scan separately checks resolved production NuGet packages.

Job-minutes

Baseline: one complete successful PR execution, orbit-api#735, head b44730e2523fcf48b8931ff8aaf54c0b2c31b9aa. Its actual file list contains infra, two other workflow files, and OpenAPI JSON, with no mutated project or generator input changes. Sum each completed Actions job's completed_at - started_at across the runs for that head, excluding queue time and skipped jobs.

Work Before, measured elapsed job-minutes After, replay forecast
Mutation shards 27.53 0.00
New shard selector 0.00 0.50 allowance
Guards 0.53 0.50 allowance
arch-map 0.15 0.00
Gating matrix 0.32 0.00
API Tests, CodeQL, SonarCloud, Dependency Review, Terraform 23.13 23.13 unchanged
Total 51.67 24.13 projected

This replays the same complete PR file set against the new policy, budgeting one minute for the two small jobs. It projects 27.53 fewer job-minutes, about 53%, and 24 running jobs becoming 15. The after total is a forecast, not an observed Actions run: this order explicitly assigns CI waiting to the orchestrator. Actual after-run measurement remains to be collected during that handoff.

Source runs: Mutation, Guards, architecture, gating, API Tests, CodeQL, SonarCloud, Dependency Review, Terraform.

Test evidence

  • dotnet build Orbit.slnx: exit 0, zero errors. Generated OpenAPI documents have no diff. dotnet test Orbit.slnx --no-build also passed after the final commit: all 9,521 tests, zero skipped.
  • dotnet test tests/Orbit.Domain.Tests --no-build: 666 passed; dotnet test tests/Orbit.Application.Tests --no-build: 5,466 passed; dotnet test tests/Orbit.Infrastructure.Tests --no-build: 3,357 passed; dotnet test tests/Orbit.Analyzers.Tests --no-build: 32 passed.
  • node --test .github/scripts/mutation-matrix.test.mjs: 17 passed. Tests drive the real selector against real temporary Git repositories, covering documentation, source/tests in each project, mixed changes, renames/deletions, merge-base behavior, shared/project config, nightly/manual scope, and invalid refs.
  • Regression sequence for documentation and unrelated projects select no shards: the unchanged 17-test suite passed at 76b90da9, while documentation inside project directories still incorrectly selected shards. After adding project-local documentation to that test, node --test --test-name-pattern='documentation and unrelated' .github/scripts/mutation-matrix.test.mjs failed before the implementation fix, showing all four shards instead of []. After excluding documentation extensions, the complete 17-test suite passed. The original workflow also passed actionlint despite its unconditional matrix; lint alone did not catch selection behavior.
  • node --test tools/__tests__/*.test.mjs: 72 passed, including all 15 gating generator tests.
  • actionlint 1.7.12 passed all four changed workflows, with the existing repository exception for the concurrency.queue key. Timeless Text, Dash Ban including its baseline, frontmatter, root allowlist, and whitespace checks passed. No bare narration comments were added.

SonarCloud round (efb08bfb): mutation-matrix.mjs validates the base ref before it reaches git (a strict ref pattern, no leading -, whitespace or ..), passes --end-of-options before the revision range, and runs git with PATH=/usr/bin:/bin. This clears jssecurity:S8705 and javascript:S4036 at .github/scripts/mutation-matrix.mjs:28.

node --test .github/scripts/mutation-matrix.test.mjs:

  • The strengthened tests on the previous head: 10 failed, exit 1 (unchecked refs reached git, and an untrusted git earlier on the inherited PATH ran).
  • After the fix: 27 passed, exit 0.

The mutation workflow's runner log (run 39094713507) shows Ubuntu 24.04 with /usr/bin/git 2.55.0, so --end-of-options (git 2.24 or newer) is supported; local /usr/bin/git is 2.54.0.

SonarCloud round 2 (61c9e1a1): SonarCloud did not count round 1's argument check or PATH override as sanitizers, so the selector no longer runs any command. mutation-matrix.mjs reads the NUL-separated changed path list with --stdin and runs no child process; the "Select changed projects" step runs the quoted git diff --name-only --no-renames -z --end-of-options "$BASE_REF...HEAD" under set -o pipefail and pipes it in, and runs the selector with no input (all shards) outside pull requests. This removes jssecurity:S8705 and javascript:S4036 (no process, no executable lookup) and javascript:S6353 (the validation regex is gone).

  • Rewritten selector tests before the fix: 18 failed, 1 passed (exit 1); after: 19 passed, exit 0.
  • actionlint .github/workflows/mutation.yml exit 0.
  • dotnet build Orbit.slnx exit 0; dotnet test Domain 666, Application 5,466, Infrastructure 3,357, all exit 0.
  • The workflow shell run locally: nightly or manual selects all four shards, an empty pull request diff selects none, a selector change selects all, a missing base fails with exit 128.

External interface evidence

The selector reads Git path output, not a third-party action's guessed response fields. git diff --name-only --no-renames -z <base>...HEAD was exercised in real temporary repositories by the tests. Moving a file from Domain to Application and deleting an Infrastructure test returned all three paths, so both rename sides and deletions select the affected shards. The empty diff on the initial branch returned zero bytes. A missing ref exited nonzero and emitted no matrix. No mocked Git response fixtures are used.

Live API responses were inspected before reading timing or protection fields. Reproduce the complete raw response shapes with gh api repos/thomasluizon/orbit-api/actions/runs/39085001880/jobs, gh api repos/thomasluizon/orbit-api/branches/main/protection, and gh api 'repos/thomasluizon/orbit-api/pulls/735/files?per_page=100'. The observed jobs response has total_count and jobs, with each job supplying name, started_at, completed_at, and steps; the protection response has required_status_checks.contexts and required_status_checks.checks with context and app_id. Each baseline run's raw jobs response supplies the measured timestamps above. PR file names come from the real filename field, not an invented fixture.

Deletion review

The repository's /second-opinion helper was run on four concrete claimed protection defects, one for each deletion/reduction: mutation filtering, consolidated guard jobs, architecture trigger filtering, and gating trigger filtering plus duplicate test removal. GPT-5.6 Sol returned DISAGREE, high confidence, on every claimed defect. The guard request needed one operational retry after the helper could not parse interpolation braces in its first reply. The complete verdicts are posted in a PR comment. I agree with those verdicts: checks and relevant artifacts remain protected, with the exact check-name migration below.

Assumptions

  • Interpret project selection literally: changes in Domain select its two shards; they do not also start dependent Application/Infrastructure shards. Rejected selecting transitive projects because the acceptance criterion requires only domain shards for Domain changes.
  • Conservatively select all shards for shared build/tool/selector changes, rather than skipping them because they live outside project folders.
  • Use a clearly labeled replay forecast for after-run minutes, rather than violating the finishing instruction to wait for or poll new Actions runs. The orchestrator collects the observed after result.
  • No C# file changed, so the .NET checks were not rerun for this round.
  • An explicit --stdin flag tells an empty pull request diff apart from a nightly run with no input.

Manual steps

  • At merge, in thomasluizon/orbit-api GitHub Settings > Branches > main branch protection > Require status checks to pass, replace exactly Timeless Text, Dash Ban, Skill and Agent Frontmatter, and Root Allowlist with Guards, pinned to GitHub Actions app id 15368. Workflow Syntax was not required. Preserve every other required context and all other protection settings. Verify the current PR has a successful Guards check before replacing the names, and read back gh api repos/thomasluizon/orbit-api/branches/main/protection to prove the four old names are absent, Guards is present with app id 15368, and the other required contexts remain.
  • During the orchestrator CI handoff, collect one complete successful PR execution after this change, sum Actions job completed_at - started_at with the same method, and replace the forecast with observed after-run minutes. The worker did not wait for or poll new Actions runs.
  • None for this round.

@thomasluizon

Copy link
Copy Markdown
Owner Author

Deletion review using the repository /second-opinion skill, model GPT-5.6 Sol. Each request challenged one claimed High-severity defect.

Claim: Deleting unconditional PR shards loses mutation protection for changed projects.

Verdict: DISAGREE, confidence high. The changed code intentionally filters shards only when baseRef is set for pull requests, while scheduled/manual runs leave selected = shards, preserving all four full runs. Domain source/test changes select both domain shards via the project-path predicate, and shared configuration changes select every shard via shared, so the alleged loss of required mutation protection is unsupported.

Claim: Deleting separate guard jobs loses protection when a guard fails or during the required-check migration.

Verdict: DISAGREE, confidence high. Each guard step uses the not-cancelled condition, so an earlier failure does not prevent later guards from running, while the combined Guards job still fails if any guard fails. The stated branch-protection replacement at merge preserves required coverage, so the shown change does not establish a High-severity protection gap.

Claim: Deleting architecture runs outside the input filters loses architecture information or a required check.

Verdict: DISAGREE, confidence high. The stated generator inputs are fully covered by the workflow's src//*.cs, src//.csproj, tests/**/.cs, generator, and workflow path filters, so changes outside them cannot make the architecture artifact stale based on the shown evidence. The check is also not required by branch protection, while artifact generation remains enabled for every relevant PR and main push.

Claim: Deleting duplicate generator tests and no-input runs loses generator or determinism protection.

Verdict: DISAGREE, confidence high. The shown workflow still runs the generator twice and compares outputs, and its path filters cover the stated source, generator, test, and workflow inputs. The finding also states that the unchanged Guard Conventions job runs the removed gating-matrix test on every PR and main push, so deleting a duplicate invocation does not lose protection.

My assessment: these alleged protection gaps are not present in the implemented changes. Keep all relevant checks and artifacts, and apply the exact required-check replacement recorded in Manual steps. The first guard request was unavailable because the helper could not parse expression braces; an operational retry produced the verdict above.

pullfrog[bot]
pullfrog Bot previously approved these changes Oct 18, 2026

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

✅ No new issues found.

Reviewed changes Reviewed all six changed files for CI coverage regressions, mutation selection correctness, and guard failure propagation.

  • Mutation selection: Adds a tested, merge-base-aware selector that skips unrelated PR changes while preserving the existing shard scopes and full scheduled/manual runs.
  • Consolidated guards: Runs all five guards in one job, preserving failure propagation, subsequent guard execution, and existing PR/push behavior.
  • Artifact generation: Restricts architecture and gating workflows to their inputs, retains artifact uploads and determinism checks, and leaves generator tests covered by Guard Conventions.
  • Verification: All 17 selector tests and 72 tooling tests passed. Full .NET/Stryker runs were not repeated locally. The required-check migration is documented in the PR; live branch-protection verification returned HTTP 403 (Resource not accessible by integration), so that configuration remains unverified.

Pullfrog  | View workflow run | Using gpt-6.1-sol | 𝕏

pullfrog[bot]
pullfrog Bot previously approved these changes Oct 18, 2026

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

✅ No new issues found.

Reviewed changes Reviewed the selector hardening in efb08bfb against the prior reviewed head ece98af2, with the full PR diff for context.

  • Validated base refs: Rejected malformed refs before invoking Git and added --end-of-options, while preserving the workflow's supported base refs and full scheduled/manual selection.
  • Restricted executable lookup: Set the Git child's PATH to /usr/bin:/bin on the Ubuntu runner.
  • Expanded regression coverage: Added invalid-ref and untrusted-PATH tests. All 27 selector tests passed locally; .NET and Stryker runs were not repeated for this script-only delta.

Pullfrog  | View workflow run | Using gpt-6.1-sol | 𝕏

@sonarqubecloud

Copy link
Copy Markdown

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

✅ No new issues found.

Reviewed changes Reviewed 61c9e1a1 against the previously reviewed efb08bfb, with all six files in the full PR diff for context.

  • Separated Git execution: Moved the quoted Git diff into the workflow and made the selector consume NUL-separated paths through --stdin, removing its child-process execution.
  • Preserved selection semantics: Kept empty PR diffs distinct from full scheduled/manual runs and retained pipeline failure propagation for unavailable base refs.
  • Verified the revised interface: Passed all 19 selector tests and exercised the actual workflow shell against scheduled/manual, empty-diff, current-PR, and missing-base cases. The missing-base case failed with exit 128; .NET and Stryker were not rerun for this CI-only delta.

Pullfrog  | View workflow run | Using gpt-6.1-sol | 𝕏

@thomasluizon
thomasluizon merged commit b802f9e into main Oct 18, 2026
25 checks passed
@thomasluizon
thomasluizon deleted the thomasluizon/ticket-1521-ci-minutes branch October 18, 2026 08:16
thomasluizon added a commit that referenced this pull request Oct 18, 2026
* fix: pin analyzer test Pkcs dependency to patched version (#728)

(cherry picked from commit 61b0090)

* fix: shard pull request domain mutation testing (#729)

(cherry picked from commit bcfd81e)

* Promote the API redesign with production compatibility checks (#723)

* Bring the API redesign tree onto main for #1413

* Preserve calendar connection detection for shipped clients

* Prove API and migrated schema compatibility with shipped clients

* Trim whitespace in the archived main schema proof

* Require the production health response to report the released commit

* Bind initial approval previews to stored pending operations

* fix: return the bulk skip refresh outcome and split the undo skip query

SonarCloud failed the quality gate on two reliability findings in code this pull request brings to main:
the bulk skip conflict check read a flag only a lambda wrote (S2583), and the undo skip check loaded
Tags and Goals in one query (S8733). The transaction now returns whether every habit refreshed, and the
undo skip check splits its query. A new case covers the concurrent change conflict; it failed with the
refresh outcome forced to true and passes with the fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 5526c9b)

* Add per-account feature flag targeting and admin controls (#724)

* feat: resolve feature flags per account

* feat: administer account feature flags through MCP and REST

* test: exclude feature flag targets from rate limit fixture

* fix: reconcile account flag tests and migration metadata with redesign

(cherry picked from commit 1b886bd)

* Add header API versioning for v1 and v2 (#733)

* Add header API versioning with v1 compatibility

* Verify versioned OpenAPI and browser header compatibility

* Preserve skip undo path contract with optional version header

(cherry picked from commit ac8b34a)

* fix: expose the config response in OpenAPI (#734)

(cherry picked from commit a9d8dd5)

* Route production redesign accounts through Cloudflare (#735)

* Add cookie-based routing for the production redesign web service

* Document web routing rollout, permissions, and rollback checks

* Fix web routing coverage and disable infra install scripts

* chore: regenerate the v2 OpenAPI document on main

orbit-api#733 added openapi_v2.json and orbit-api#734 typed the /api/config
response in parallel, so main carries a v2 document without the typed config
schema and the Build check's stale-document step fails on every merge with
main. dotnet build src/Orbit.Api regenerates it; openapi.json is unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit 9c64770)

* Fix habit edit gating, end dates, and reminder updates (#736)

* fix: validate habit updates with the resulting time range

* fix: preserve end dates when creating root habits

* fix: gate slip alert activation and preserve unchanged reminders

* fix: compare effective reminder rules across stored formats

(cherry picked from commit 7472203)

* Reduce mutation and guard CI jobs (#737)

* Select mutation shards from changed projects

* Consolidate guards and filter artifact generation

* Skip project documentation in mutation selection

* Validate mutation base refs and restrict Git PATH

* Read mutation shard changes from standard input

(cherry picked from commit b802f9e)

* test: strengthen habit and goal ownership coverage (#739)

(cherry picked from commit 0e32628)

* test: verify checkout session user metadata (#740)

(cherry picked from commit da2f53a)

* fix: protect OAuth code delivery with Turnstile (#741)

(cherry picked from commit 1149606)

* fix: enforce chat interaction scope before loading context (#743)

(cherry picked from commit 98faf4e)

* fix: erase accountability pairs during account deletion and reset (#745)

* fix: erase accountability pairs during account deletion and reset

* test: preserve migration compatibility checks after accountability cleanup

(cherry picked from commit a9e5fb9)

* Fix goal sync dates and deadline reminder counts (#747)

* fix: compare streak sync dates in the user timezone

* fix: show actual days remaining in goal deadline reminders

* test: configure timezone for streak scheduler fixtures

(cherry picked from commit 0e63842)

* Manage staging landing routing in Terraform (#742)

(cherry picked from commit abdd738)

* Restrict Web Push endpoints to supported push services (#1503) (#751)

Web Push subscriptions accept only HTTPS endpoints on the FCM, Mozilla, Apple and WNS push hosts, and the push HTTP client follows no redirects. Replaces orbit-api#748 as one commit so no commit carries the high-entropy sample fixture.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit d9f847a)

* Erase account uploads before permanent deletion (#1507) (#750)

* fix: erase uploaded objects before completing account deletion

* fix: refuse upload reads after permanent account deletion

* fix: grant uploads bucket deletion permissions and verify provider wiring

* test: align storage startup checks with erasure requirements

* fix: retire deleted Supabase upload storage

* test: configure upload erasure providers in deletion fixtures

Share the existing object-store fake across account deletion and scheduler fixtures after merging main. Preserve accountability assertions and production provider requirements for thomasluizon/orbit-tickets#1507.

(cherry picked from commit fa33a94)

* fix: limit anonymous auth and waitlist by email and source IP (ORB-108) (#749)

* fix: enforce source IP ceilings alongside anonymous email limits

* test: cover both rate limit dimensions before bot verification

* fix: isolate BFF email rate limits by verified client IP

* fix: generate and persist BFF forwarding secrets

(cherry picked from commit de0f42f)

* Limit access tokens to fifteen minutes (#738)

* fix(auth): limit access tokens to fifteen minutes

* fix(auth): align managed JWT lifetime and prove logout rejection

(cherry picked from commit ade7016)

* fix(auth): equalize account provisioning work (ORB-96) (#744)

* test(auth): expose registration dependent sign-in work

* fix(auth): perform uniform registration work before sign-in responses

* test(auth): cover expired codes and tracked upsert conflicts

* fix(auth): isolate signup retries and restore provisioning guards

* fix(auth): recover signup work after dispatch failures

* fix(auth): roll back failed signup referrals before retry

(cherry picked from commit 1bd0d26)

* fix: expose redesign controllers in both API versions

Apply the carried version declarations to the Notion and proactive controllers and regenerate the v2 contract. Refs thomasluizon/orbit-tickets#746.

* fix: honor account flags in proactive engine

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
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