Files
molecule-core/.gitea/workflows/lint-mask-pr-atomicity.yml
T
molecule-code-reviewerandClaude Opus 5 862d68fffd
CI / Python Lint & Test (pull_request) Successful in 37s
Lint forbidden tenant-env keys / Scan for repo-host token write into tenant workspace surface (pull_request) Successful in 42s
Lint forbidden hand-written mcp__ tool-id literals / Scan for hand-written mcp__ tool-id literals (pull_request) Successful in 45s
Lint curl status-code capture / Scan workflows for curl status-capture pollution (pull_request) Successful in 49s
Concierge Creates Workspace Hermetic / Concierge Creates Workspace Hermetic (pull_request) Successful in 52s
lint-no-coe-on-required / lint-no-coe-on-required (pull_request) Successful in 57s
Block integration-tester contamination artifacts / Block staging-trigger / invalid manifest contamination (pull_request) Successful in 1m0s
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 1m2s
ci-required-drift / drift (pull_request) Successful in 1m9s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 1m2s
Lint forbidden tenant-env keys / Scan workspace_secrets writers for forbidden env keys (pull_request) Successful in 1m19s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 45s
lint-bp-context-emit-match / lint-bp-context-emit-match (pull_request) Successful in 1m30s
lint-required-workflows-docker-host-pinned / Lint docker-host pin on docker-touching workflows (pull_request) Successful in 37s
lint-continue-on-error-tracking / lint-continue-on-error-tracking (pull_request) Successful in 1m28s
Lint publish-runner timeout-minutes / Lint publish-runner timeout-minutes (pull_request) Successful in 1m12s
lint-setup-go-cache / lint-setup-go-cache (pull_request) Successful in 52s
Lint schedule budget / Zero-cron ratchet (pull_request) Successful in 51s
lint-staging-tenant-cd-gate-chain / lint-staging-tenant-cd-gate-chain (pull_request) Successful in 49s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 18s
CI / Detect changes (pull_request) Successful in 2m42s
E2E Chat / E2E Chat (pull_request) Successful in 2m43s
sdk-route-milestone-contract-drift / SDK route + milestone derive-gates (pull_request) Successful in 1m22s
Local Provision Lifecycle E2E / Local Provision Lifecycle E2E (stub) (pull_request) Successful in 1m53s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 1m25s
sdk-pin-drift / SDK consumer pin vs molecule-ai-sdk main (pull_request) Successful in 1m40s
Ops Scripts Tests / Ops scripts (unittest) (pull_request) Failing after 1m42s
pin-provenance-guard / Pin provenance (pull_request) Successful in 2m40s
E2E API Smoke Test / detect-changes (pull_request) Successful in 4m18s
lint-env-coupling-dismissal / lint-env-coupling-dismissal (pull_request) Successful in 4m16s
E2E Ephemeral CP Happy Path / detect (pull_request) Successful in 4m22s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 5s
lint-required-context-exists-in-bp / lint-required-context-exists-in-bp (pull_request) Successful in 4m3s
template-delivery-e2e / detect-changes (pull_request) Successful in 3m9s
CI / Platform (Go) (pull_request) Failing after 2m4s
template-delivery-e2e / Template-asset delivery (fresh seo-agent — config+prompts via asset channel, seo-all via plugin reconcile) (pull_request) Successful in 3s
lint-mask-pr-atomicity / lint-mask-pr-atomicity (pull_request) Successful in 2m47s
lint-context-set-shrink / lint-context-set-shrink (pull_request) Successful in 5m31s
CI / Canvas (Next.js) (pull_request) Has been cancelled
CI / Canvas Deploy Status (pull_request) Has been cancelled
CI / Shellcheck (E2E scripts) (pull_request) Has been cancelled
CI / all-required (pull_request) Has been cancelled
E2E Ephemeral CP Happy Path / E2E Ephemeral CP Happy Path (pull_request) Has been cancelled
lint-phantom-gate / lint-phantom-gate (pull_request) Has been cancelled
Lint workflow YAML (repository compatibility policy) / Lint workflow YAML for repository compatibility policy (pull_request) Has been cancelled
Local Provision Lifecycle E2E / Local Provision Lifecycle E2E (real image + MiniMax LLM, advisory) (pull_request) Failing after 10m36s
Lint pre-flip continue-on-error / Verify continue-on-error flips have run-log proof (pull_request) Failing after 13m38s
chore(governance): retire two continue-on-error masks instead of renewing them a sixth time
`lint-continue-on-error-tracking` has been red on `main` since 2026-08-14
because mc#4959 and mc#4960 crossed the 14-day cap. Under BP
status_check_contexts=["*"] that blocks every PR touching
`.gitea/workflows/**` — currently #5201.

The 14-day cap exists to force a decision, so this is the decision. Both
masks are removed rather than re-tracked. Full reasoning is in the inline
decision record on each job; the short version:

lint-mask-pr-atomicity / scan
  The mask's only stated justification was the paths-filtered pre-flip
  catch-22 (mc#4344). fb426286 (2026-07-20) fixed exactly that: it
  paginates commit statuses and accepts a paths-filtered workflow's
  PR-event runs as proof, naming this workflow and mc#4344 in its body.
  The blocker was already gone ten days before mc#4959 restated it. The
  job is green in every completed run in the sampled window but one
  (run 652757, PR #5168 @cb5265ff), and that one is not a lint verdict:
  #5168's ci.yml diff moves neither predicate the script tests.

publish-canvas-image / build-and-push
  mc#4960's own definition of done was "collect clean post-main
  build-and-push logs, then flip false". They exist: runs 631493,
  632082, 632983, 633875 (2026-08-08) and 647095 (2026-08-11) all report
  "Job succeeded" with a proven immutable candidate digest. The failure
  the mask actually covered — Infisical 404 on MOLECULE_REGISTRY_TOKEN,
  recorded on mc#4391 from run 543305 — was fixed by c58c0a58
  (2026-07-23). mc#4391 stays open as the P0 of record: it is about
  promotion of a mutable `:latest`, which this job does not do.

Neither mask was buying what it was renewed for. Job-level
continue-on-error does not suppress the posted commit status on this
Gitea: run 652757 posted `...(pull_request)=failure` with the mask in
place, and run 543305 posted `...(push)=failure` likewise. The masks
cost a 14-day renewal treadmill (mc#774 -> #1982 -> #2654 -> #3436 ->
#4344 -> #4959) and suppressed nothing observable.

MASK_WAIVERS in lint_no_coe_on_required.py is now empty — the two
entries this change retires plus three the lint had been reporting
prune-me (their masks were already gone). There is now no job-level
`continue-on-error: true` anywhere in .gitea/workflows.

Verified locally: lint_continue_on_error_tracking.py exit 0 ("all 0
directives"), lint_no_coe_on_required.py OK with 0 waived,
lint-workflow-yaml.py clean, 40 unit tests pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0173QX3sXjRSV8BWY5aE8c79
2026-09-03 13:54:11 -07:00

161 lines
7.8 KiB
YAML

name: lint-mask-pr-atomicity
# Tier 2d hard-gate lint (per mc#1982) — 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#1982.
#
# Cross-links
# -----------
# - mc#1982 (the RFC that specs this lint)
# - PR#665 / PR#668 (the empirical split-pair)
# - mc#1982 (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. The repository webhook contract includes `edited` for body
# changes; the original behavior was source-verified during the 1.22.6 era.
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 in runner configuration, but pinning it at the workflow
# level protects against a runner regenerated without that setting.
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
# UNMASKED 2026-09-03. Was `continue-on-error: true` under tracker mc#4959.
#
# The mask's only stated justification was the pre-flip catch-22 of mc#4344:
# this workflow is paths-filtered, so it never fires on a push to main, and
# lint_pre_flip_continue_on_error.py accepted main-PUSH run logs as the only
# proof that a flip is safe. That gate was fixed on 2026-07-20 by fb426286
# ("pre-flip gate paginates statuses + accepts PR evidence for paths-filtered
# workflows"), whose body names this workflow and mc#4344 and states it
# "unblocks core#4521 (mask-elimination)". The blocker was therefore already
# gone when mc#4959 renewed the mask on 2026-07-30; that renewal restated it
# without re-checking, and the same text had been carried forward through
# mc#774 -> mc#1982 -> mc#2654 -> mc#3436 -> mc#4344 -> mc#4959.
#
# Evidence behind the flip (governance review, 2026-09-03):
# - 45 runs of this workflow in the sampled window (2026-08-10..2026-09-03).
# The job is green in every completed run but one.
# - The exception, run 652757 (PR #5168 @cb5265ff), is not a lint verdict:
# that PR's ci.yml diff changes neither `continue-on-error` nor
# `all-required.needs`, so both predicates are false and the script
# returns 0. The job died in its own setup and its log has since been
# garbage-collected.
# - The mask was not suppressing what it was renewed to suppress. With it
# in place, run 652757 still posted the commit status
# `lint-mask-pr-atomicity / lint-mask-pr-atomicity (pull_request)
# = failure`, exactly as `publish-canvas-image / Build & push canvas
# image (push)` posted `failure` on 2026-07-18 (recorded in mc#4391).
# Job-level continue-on-error does not suppress the posted context on
# this Gitea, so the mask bought a 14-day renewal treadmill and no
# actual masking.
#
# No tracker comment belongs here now: with no truthy `continue-on-error`,
# Tier 2e has nothing to require.
continue-on-error: false
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