Skip to content

fix(ci): budget macOS ARM full release integration - #3360

Merged
Daniel Meppiel (danielmeppiel) merged 1 commit into
mainfrom
danielmeppiel-macos-release-budget
Oct 22, 2026
Merged

Daniel Meppiel (danielmeppiel) merged 1 commit into
mainfrom
danielmeppiel-macos-release-budget

Conversation

@danielmeppiel

Copy link
Copy Markdown
Collaborator

fix(ci): budget macOS ARM full release integration

Description

TL;DR

Raise only macOS ARM's full integration step limit from 60 to 120 minutes. The previous nonpublishing run reached 77% with all four workers still passing cases at the cutoff; full selection, grouping, failure propagation, and publication gates stay unchanged.

Important

This is a bounded CI-budget recovery, not proof of a successful full release run. The 120-minute hosted completion time remains unverified; v0.35.0 is unchanged.

Problem (WHY)

  • Run 39784442659, ARM job 119843776399 ran integration from 2026-10-22T13:21:13Z to 14:21:26Z, then reported The action 'Run integration tests' has timed out after 60 minutes.
  • All four workers passed cases at 77% in the final nine seconds; the last pass was 14:21:24.1787490Z. This supports insufficient budget rather than a demonstrated hang.
  • [!] A rough linear projection is 60 / 0.77 = 77.9 minutes. A 120-minute bound leaves about 42 minutes beyond that estimate for setup/tail variability; it is not a measured completion time.

The diagnosis follows "Read agent execution traces, not just final outputs."; local qualification follows "Only proceed when validation passes". #3353 deliberately left this timeout outside its fixture-repair scope.

Approach (WHAT)

  • Change the existing ARM integration timeout, without creating another job or shard.
  • Retain not live, -n 4 --dist loadgroup, HOME grouping, all failure propagation, and every release dependency.
  • Extend the existing workflow contracts and add one documentation paragraph; no production code, version, credential, or trigger changes.

Implementation (HOW)

File Bounded change
.github/workflows/build-release.yml Set only the ARM integration limit to 120 and record the observed budget rationale.
tests/unit/test_platform_contract_workflow.py Pin 120; reject missing/old ARM limits, missing parallelism, a non-grouped scheduler, or changed worker count. Existing coverage rejects a focused ARM subset and changed upper bounds.
docs/src/content/docs/contributing/integration-testing.md Explain the ARM bound, preserved selection/grouping, and unchanged failure gates. No additional docs or TOC changes are needed for this budget-only diff.

Diagrams

The dashed step is the only behavioral change; downstream validation and publication conditions remain unchanged.

flowchart LR
    subgraph ARM["build-and-validate-macos-arm"]
        I["Run integration tests: 120 minutes"] -->|"success"| R["Run release validation tests: 20 minutes"]
    end
    R -->|"success"| C["create-release: pushed tags only"]
    O["Other required release jobs"] -->|"success"| C
    I -->|"failure or timeout"| F["Publication blocked"]
    classDef changed stroke-dasharray: 5 5;
    class I changed;
Loading

Trade-offs

  • Prefer a larger finite budget to excluding tests or redesigning scarce-runner scheduling. A genuine future hang can now consume up to 120 minutes; this change does not speed up tests.
  • Workflow contracts prove configuration, not hosted completion. The parent release session retains qualification, merge, dispatch, tagging, and publication; no full run was launched by this recovery.

Benefits

  1. ARM retains the full non-live corpus with an explicit 120-minute ceiling.
  2. Regression mutations reject the exhausted 60-minute limit and lost HOME-group scheduling.
  3. Linux, Windows, Intel, release-validation, and publication configurations remain semantically identical.

Issue and approved scope

Issue: Fixes #3356 (bounded timeout recovery; hosted release qualification remains separate).

Human scope-approval comment: No human-authored GitHub approval URL is claimed. The tracking issue transparently records agent-authored provenance for the direct user instruction relayed by the parent release session: "ok so merge it and proceed until release happens, fixing anything that needs fixing on the way and bypass merging the fixes (CI)". The delegated scope is this minimal budget/contracts/docs PR; review contact is Daniel Meppiel (@danielmeppiel).

Does this PR complete the issue, or what remains? It completes the bounded configuration and local regression work. A new trusted full-route run on changed main must establish hosted completion before release qualification; the currently running 39817934984 uses the old workflow at 041642f2b226042652e31130ba3f57b15f044d9e.

Type of change

  • Bug fix
  • New feature
  • Documentation
  • Maintenance / refactor

Testing

  • Tested locally
  • All existing tests pass
  • Added tests for new functionality (if applicable)

Validation

Head: 4d52b815847d59b683ffabd0ddd4873a453adf00. Base: latest fetched/merged main 041642f2b226042652e31130ba3f57b15f044d9e. No product changelog entry or version bump is needed for this CI-only recovery.

Old workflow, after updating the contract but before changing the timeout:

uv run --frozen --extra dev pytest -q tests/unit/test_platform_contract_workflow.py::test_linux_and_arm_retain_non_live_corpus_grouped_parallelism — expected exit 1:

E           AssertionError: assert 60 == 120
1 failed in 2.44s

New workflow:

uv run --frozen --extra dev pytest -q tests/unit/test_platform_contract_workflow.py tests/unit/test_security_workflow_contracts.py tests/unit/test_ado_live_workflow_contract.py — exit 0:

........................................................                 [100%]
56 passed in 1.92s
Canonical lint mirror and test-quality commands — all exited 0
Exact command Actual result
uv run --frozen --extra dev ruff check src/ tests/ scripts/lint_architecture_boundaries.py scripts/architecture_linter/ scripts/windows_native_symlink_probe_entry.py All checks passed!
uv run --frozen --extra dev ruff format --check src/ tests/ scripts/lint_architecture_boundaries.py scripts/architecture_linter/ scripts/windows_native_symlink_probe_entry.py 2091 files already formatted
uv run --frozen --extra dev python -m pylint --disable=all --enable=R0801 --min-similarity-lines=10 --fail-on=R0801 src/apm_cli/ scripts/lint_architecture_boundaries.py scripts/architecture_linter/ scripts/windows_native_symlink_probe_entry.py Your code has been rated at 10.00/10 (previous run: 10.00/10, +0.00)
bash scripts/lint-auth-signals.sh [+] auth-signal lint clean
bash scripts/lint-architecture-boundaries.sh No diagnostics, exit 0
uv run --frozen python scripts/check_test_assertions.py [+] assertion-quality ratchet clean: AQ001=4, AQ002=12
uv run --frozen python scripts/check_exact_test_duplicates.py [+] exact test duplicate ratchet clean: 1415 files, 0 allowed duplicate group(s)
git diff --check No diagnostics, exit 0

The remaining CI lint guards used this portable Python equivalent of CI's regex/path/line checks:

uv run --frozen --extra dev python - <<'PY'
from pathlib import Path
import re
root = Path('src/apm_cli')
yaml_pattern = re.compile(r'yaml\.(safe_)?dump\(.+,\s*[a-zA-Z_]\w*\b')
path_pattern = re.compile(r'str\([^)]*\.relative_to\(')
yaml_violations = []
path_violations = []
for path in root.rglob('*.py'):
    for number, line in enumerate(path.read_text(encoding='utf-8').splitlines(), 1):
        if yaml_pattern.search(line) and path.as_posix() != 'src/apm_cli/utils/yaml_io.py' and '# yaml-io-exempt' not in line:
            yaml_violations.append(f'{path}:{number}:{line}')
        if path_pattern.search(line) and 'portable_relpath' not in line and '.pyc' not in line:
            path_violations.append(f'{path}:{number}:{line}')
assert not yaml_violations, '\n'.join(yaml_violations)
assert not path_violations, '\n'.join(path_violations)
files = [*Path('src').rglob('*.py'), *Path('scripts/architecture_linter').rglob('*.py'), Path('scripts/lint_architecture_boundaries.py')]
length_violations = [(str(path), len(path.read_text(encoding='utf-8').splitlines())) for path in files if len(path.read_text(encoding='utf-8').splitlines()) > 2100]
assert not length_violations, length_violations
print('PASS: CI-equivalent YAML I/O, portable relative-path and 2100-line guards (portable Python regex mirror)')
PY

Result: exit 0, PASS: CI-equivalent YAML I/O, portable relative-path and 2100-line guards (portable Python regex mirror).

Exact workflow-preservation and version check — exit 0
uv run --frozen --extra dev python - <<'PY'
import subprocess
from pathlib import Path
import yaml
base = yaml.safe_load(subprocess.check_output(['git', 'show', 'origin/main:.github/workflows/build-release.yml'], text=True))
current = yaml.safe_load(Path('.github/workflows/build-release.yml').read_text())
job = current['jobs']['build-and-validate-macos-arm']
step = next(s for s in job['steps'] if s.get('name') == 'Run integration tests')
assert step['timeout-minutes'] == 120
step['timeout-minutes'] = 60
assert current == base, 'Workflow changed beyond the ARM integration timeout'
assert subprocess.check_output(['git', 'diff', 'origin/main', '--', 'pyproject.toml', 'uv.lock']) == b''
print('PASS: the only semantic workflow change is ARM integration timeout 60 -> 120; version manifests unchanged')
PY
PASS: the only semantic workflow change is ARM integration timeout 60 -> 120; version manifests unchanged

Diagram: mmdc -i release-budget.mmd -o release-budget.svg --quiet and the corresponding PNG render both exited 0; the rendered diagram was inspected. Artifact paths were session-local.

Scenario Evidence

# Scenario (user promise) Principle(s) Test(s) proving it Type
1 Keep full macOS release coverage without the exhausted one-hour cutoff DevX (pragmatic as npm) tests/unit/test_platform_contract_workflow.py::test_linux_and_arm_retain_non_live_corpus_grouped_parallelism (regression-trap for #3356), test_arm_missing_or_old_timeout_mutation_is_rejected unit
2 Preserve grouped HOME-mutating tests and full non-live selection Secure by default tests/unit/test_platform_contract_workflow.py::test_non_live_unix_parallelism_mutation_is_rejected, test_arm_focused_subset_mutation_is_rejected unit

How to test

  • Run the three-module pytest command above; expect 56 passes.
  • Temporarily restore only the ARM step to 60 minutes and run the named regression test; expect assert 60 == 120. Restore 120 afterward.
  • Run the canonical lint commands and static guards above; expect exit 0.
  • After maintainer merge, qualify changed main through the trusted nonpublishing release route; require actual ARM integration and release-validation success. This PR does not itself launch that run.

Spec conformance (OpenAPM v0.1)

If this PR changes behaviour that an OpenAPM v0.1 req-XXX covers,
confirm the three-step ritual in the
development guide:

  • Spec edit: docs/src/content/docs/specs/openapm-v0.1.md updated
    (new/changed <a id="req-XXX"></a> anchor + prose + Appendix C
    row).
  • Manifest edit: docs/src/content/docs/specs/manifests/openapm-v0.1.requirements.yml
    updated.
  • Test edit: a @pytest.mark.req("req-XXX") test under
    tests/spec_conformance/ added or extended.
  • CONFORMANCE.{md,json} regenerated via
    uv run --extra dev python -m tests.spec_conformance.gen_statement
    and committed.
  • N/A -- this PR does not change OpenAPM-observable behaviour.

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

Raise only the ARM non-live integration step limit from 60 to 120 minutes. Run 39784442659 reached 77% with all four workers still passing at the cutoff; preserve selection, grouped workers, and release gates. Add focused budget and scheduling mutation contracts and document the bound.

Refs #3356

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot-Session: f91bdda0-3177-41e4-87f0-3b441cfb343c
Copilot AI balanced review requested due to automatic review settings October 22, 2026 17:03
@danielmeppiel
Daniel Meppiel (danielmeppiel) merged commit 5a9be32 into main Oct 22, 2026
22 checks passed
@danielmeppiel
Daniel Meppiel (danielmeppiel) deleted the danielmeppiel-macos-release-budget branch October 22, 2026 17:04

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

The narrowly scoped timeout adjustment has matching regression contracts and documentation, with no unresolved findings or changes to release gates.

No open issues 🎉

What changed in this PR

This PR addresses #3356 by increasing the macOS ARM release integration timeout from 60 to 120 minutes, following the timeout left unresolved by #3353.

Changes:

  • Raises only the ARM integration budget, preserving full non-live coverage, grouped four-worker execution, and release gates.
  • Extends regression contracts for timeout and parallelism settings.
  • Documents the budget rationale; hosted completion remains a separate release qualification step.
File Description
tests/​unit/​test_platform_contract_workflow.py Pins the 120-minute budget and tests invalid timeout and parallelism settings.
docs/​src/​content/​docs/​contributing/​integration-testing.md Explains the ARM budget and preserved coverage and failure gates.
.github/​workflows/​build-release.yml Raises the ARM integration timeout and records the rationale.

Review effort: Balanced

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.

[BUG] macOS ARM full release integration exhausts 60-minute budget while progressing

2 participants