bbc6f5c287
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 29s
Harness Replays / detect-changes (pull_request) Successful in 24s
Lint curl status-code capture / Scan workflows for curl status-capture pollution (pull_request) Successful in 24s
CI / Detect changes (pull_request) Successful in 1m8s
E2E API Smoke Test / detect-changes (pull_request) Successful in 1m17s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 1m19s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 1m24s
review-check-tests / review-check.sh regression tests (pull_request) Successful in 29s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 25s
qa-review / approved (pull_request) Failing after 32s
gate-check-v3 / gate-check (pull_request) Successful in 36s
Runtime PR-Built Compatibility / detect-changes (pull_request) Successful in 1m1s
security-review / approved (pull_request) Failing after 23s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 1m44s
sop-checklist-gate / gate (pull_request) Successful in 22s
lint-continue-on-error-tracking / lint-continue-on-error-tracking (pull_request) Successful in 2m19s
sop-tier-check / tier-check (pull_request) Successful in 27s
Lint workflow YAML (Gitea-1.22.6-hostile shapes) / Lint workflow YAML for Gitea-1.22.6-hostile shapes (pull_request) Successful in 1m57s
lint-mask-pr-atomicity / lint-mask-pr-atomicity (pull_request) Successful in 2m40s
Lint pre-flip continue-on-error / Verify continue-on-error flips have run-log proof (pull_request) Successful in 2m35s
lint-required-context-exists-in-bp / lint-required-context-exists-in-bp (pull_request) Successful in 2m49s
Harness Replays / Harness Replays (pull_request) Successful in 14s
CI / Platform (Go) (pull_request) Successful in 31s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 17s
CI / Canvas (Next.js) (pull_request) Successful in 23s
CI / Python Lint & Test (pull_request) Successful in 22s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 20s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 24s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 23s
Runtime PR-Built Compatibility / PR-built wheel + import smoke (pull_request) Successful in 18s
sop-checklist / all-items-acked (pull_request) acked: 7/7
CI / Canvas Deploy Reminder (pull_request) Has been skipped
CI / all-required (pull_request) Injected: all individual CI jobs passed
135 lines
6.1 KiB
YAML
135 lines
6.1 KiB
YAML
name: lint-mask-pr-atomicity
|
|
|
|
# Tier 2d hard-gate lint (per mc#774) — blocks PRs that touch
|
|
# `.gitea/workflows/ci.yml` and modify ONLY ONE of {continue-on-error,
|
|
# all-required.sentinel.needs} without a `Paired: #NNN` reference in
|
|
# the PR body or in a commit message.
|
|
#
|
|
# Why this exists
|
|
# ---------------
|
|
# PR#665 (interim `continue-on-error: true` on `platform-build`) and
|
|
# PR#668 (sentinel-`needs` demotion of the same job) were designed as a
|
|
# pair but merged solo — #665 landed at 04:47Z 2026-05-12, #668 was
|
|
# still open at 05:07Z when the main-red watchdog (#674) fired. Result:
|
|
# ~20 minutes of `main` red and a cascade of false-positives on
|
|
# unrelated PRs. This lint structurally prevents that class.
|
|
#
|
|
# How the gate works
|
|
# ------------------
|
|
# 1. The workflow runs on every PR whose diff touches ci.yml (paths
|
|
# filter). It is NOT a required check on `main` because the rule is
|
|
# diff-based — running it on PRs that don't touch ci.yml would
|
|
# produce a `pending` status forever (per
|
|
# `feedback_path_filtered_workflow_cant_be_required`).
|
|
# 2. The script reads `BASE_SHA:ci.yml` and `HEAD_SHA:ci.yml`, parses
|
|
# both via PyYAML AST (per `feedback_behavior_based_ast_gates` — no
|
|
# grep, no regex on the raw text — so a YAML-shape refactor still
|
|
# detects).
|
|
# 3. Walks `jobs.*.continue-on-error` on each side; flags any value
|
|
# diff. Reads `jobs.all-required.needs` on each side; flags any
|
|
# set diff (order-insensitive — `needs:` is engine-unordered).
|
|
# 4. If both predicates fired → atomic, OK. If neither → no risk, OK.
|
|
# If exactly one fired → require `Paired: #NNN` in PR body OR in
|
|
# any commit message between base..head; else fail.
|
|
#
|
|
# Phase contract (RFC internal#219 §1 ladder)
|
|
# -------------------------------------------
|
|
# This workflow lands at `continue-on-error: true` (Phase 3 — surface
|
|
# regressions without blocking PRs while the rule beds in).
|
|
# Follow-up PR flips to `false` once we have ≥3 days of clean runs on
|
|
# `main` and no false-positives. Tracking issue: mc#774.
|
|
#
|
|
# Cross-links
|
|
# -----------
|
|
# - mc#774 (the RFC that specs this lint)
|
|
# - PR#665 / PR#668 (the empirical split-pair)
|
|
# - mc#774 (the main-red incident the split caused)
|
|
# - feedback_strict_root_only_after_class_a
|
|
# - feedback_behavior_based_ast_gates
|
|
#
|
|
# Auth: only needs the auto-injected GITHUB_TOKEN (read-only, repo
|
|
# scope). No DRIFT_BOT_TOKEN needed — Tier 2d does NOT call
|
|
# branch_protections (Tier 2g/f do).
|
|
|
|
on:
|
|
pull_request:
|
|
types: [opened, synchronize, reopened, edited]
|
|
# `edited` is included because the rule depends on PR_BODY: a user
|
|
# may add `Paired: #NNN` after first push to satisfy the lint. The
|
|
# rerun on `edited` lets the PR turn green without an empty
|
|
# commit. Gitea 1.22.6 fires `edited` on body changes — verified
|
|
# via gitea-source/models/issues/pull_list.go::triggerNewPRWebhook.
|
|
paths:
|
|
- '.gitea/workflows/ci.yml'
|
|
- '.gitea/scripts/lint_mask_pr_atomicity.py'
|
|
- '.gitea/workflows/lint-mask-pr-atomicity.yml'
|
|
- 'tests/test_lint_mask_pr_atomicity.py'
|
|
|
|
env:
|
|
# Belt-and-suspenders against the runner-default trap
|
|
# (feedback_act_runner_github_server_url). Runners are configured
|
|
# with this env via /opt/molecule/runners/config.yaml, but pinning
|
|
# at the workflow level protects against a runner regenerated
|
|
# without the config file.
|
|
GITHUB_SERVER_URL: https://git.moleculesai.app
|
|
|
|
permissions:
|
|
contents: read
|
|
pull-requests: read
|
|
|
|
# Per-PR concurrency — re-pushes cancel previous runs to keep the
|
|
# queue short. The lint is cheap (one git show + log + a YAML parse).
|
|
concurrency:
|
|
group: lint-mask-pr-atomicity-${{ github.event.pull_request.number || github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
# bp-exempt: meta-lint advisory during mask burn-down; CI / all-required gates merges.
|
|
scan:
|
|
name: lint-mask-pr-atomicity
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 5
|
|
# Phase 3 (RFC #219 §1): surface broken shapes without blocking
|
|
# PRs. Follow-up PR flips this to `false` once recent runs on main
|
|
# are confirmed clean (eat-our-own-dogfood discipline mirrors
|
|
# PR#673's same-shape comment). Tracking: mc#774.
|
|
# mc#774: pre-existing continue-on-error mask; root-fix and remove, do not renew silently.
|
|
continue-on-error: true
|
|
steps:
|
|
- name: Check out PR head with full history (need base SHA blobs)
|
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
with:
|
|
# `git show <base-sha>:<path>` needs the base SHA's blobs.
|
|
# Shallow=1 would miss it. Same rationale as PR#673 and
|
|
# check-migration-collisions.yml.
|
|
fetch-depth: 0
|
|
- name: Set up Python (PyYAML for AST parsing)
|
|
uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
|
|
with:
|
|
python-version: '3.12'
|
|
- name: Install PyYAML
|
|
# Same pin as ci-required-drift.yml + the rest of the Tier 2
|
|
# lint family — keep runner-cache hits uniform.
|
|
run: python -m pip install --quiet 'PyYAML==6.0.2'
|
|
- name: Ensure base ref is reachable locally
|
|
# fetch-depth=0 usually pulls the base too, but explicit-fetch
|
|
# is cheap insurance against runner-version drift (matches the
|
|
# comment in check-migration-collisions.yml and PR#673).
|
|
run: |
|
|
git fetch origin "${{ github.event.pull_request.base.ref }}" || true
|
|
- name: Run lint-mask-pr-atomicity
|
|
env:
|
|
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
|
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
|
# PR body — the script greps for `Paired: #NNN`.
|
|
PR_BODY: ${{ github.event.pull_request.body }}
|
|
CI_WORKFLOW_PATH: .gitea/workflows/ci.yml
|
|
SENTINEL_JOB_KEY: all-required
|
|
run: python3 .gitea/scripts/lint_mask_pr_atomicity.py
|
|
- name: Run lint-mask-pr-atomicity unit tests
|
|
# Run the test suite in-CI so the lint's own behaviour is
|
|
# verified on every change. Matches lint-workflow-yaml.yml.
|
|
run: |
|
|
python -m pip install --quiet pytest
|
|
python3 -m pytest tests/test_lint_mask_pr_atomicity.py -v
|