feat(ci)(hard-gate): lint-required-workflows-no-paths-filter #670
Merged
core-devops
merged 1 commits from 2026-05-12 05:50:37 +00:00
infra/lint-required-no-paths-filter into main
1 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| c0f594cd22 |
feat(ci)(hard-gate): lint-required-workflows-no-paths-filter (structural enforcement of feedback_path_filtered_workflow_cant_be_required)
Some checks failed
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 9s
Lint curl status-code capture / Scan workflows for curl status-capture pollution (pull_request) Successful in 12s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 15s
qa-review / approved (pull_request) Failing after 14s
CI / Detect changes (pull_request) Successful in 26s
security-review / approved (pull_request) Failing after 15s
E2E API Smoke Test / detect-changes (pull_request) Successful in 29s
sop-tier-check / tier-check (pull_request) Successful in 18s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 29s
gate-check-v3 / gate-check (pull_request) Successful in 25s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 30s
Runtime PR-Built Compatibility / detect-changes (pull_request) Successful in 29s
CI / Platform (Go) (pull_request) Successful in 7s
CI / Canvas (Next.js) (pull_request) Successful in 6s
CI / Python Lint & Test (pull_request) Successful in 5s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 5s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 5s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 4s
Runtime PR-Built Compatibility / PR-built wheel + import smoke (pull_request) Successful in 3s
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 5s
CI / all-required (pull_request) Successful in 2s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 1m15s
audit-force-merge / audit (pull_request) Successful in 5s
Add `.gitea/workflows/lint-required-no-paths.yml` + supporting script
and tests that fail a PR if any workflow whose status-check context
appears in `branch_protections/main.status_check_contexts` carries a
`paths:` or `paths-ignore:` filter in its `on:` block.
Why
---
A required-check workflow with a paths filter silently degrades the
merge gate. If a PR's diff doesn't match the filter, the workflow never
fires; Gitea (1.22.6) treats the required context as `pending` (NOT
`skipped == success`), so the PR cannot merge. A docs-only PR against
`paths: ['**.go']` would be wedged forever — no human action produces
a green.
Previously this was prevented only by reviewer vigilance + the saved
memory `feedback_path_filtered_workflow_cant_be_required`. This commit
makes it a structural CI gate.
Empirical baseline (verified 2026-05-11 against
git.moleculesai.app/molecule-ai/molecule-core/branch_protections/main):
status_check_contexts:
- "Secret scan / Scan diff for credential-shaped strings (pull_request)"
- "sop-tier-check / tier-check (pull_request)"
- "CI / all-required (pull_request)"
All three workflows (`secret-scan.yml`, `sop-tier-check.yml`,
`ci.yml`) have NO paths/paths-ignore filter today. This lint locks
that contract: a future PR adding `paths:` to any of them — or to
any new required workflow per RFC#324 Step 2 (qa-review,
security-review) — fails fast at PR time.
How
---
- Workflow runs on `pull_request: [opened, synchronize, reopened]`
+ `workflow_dispatch`. Deliberately NO `paths:` filter on itself —
the workflow is self-evidently a meta-required-check.
- Reads `branch_protections/main` via `DRIFT_BOT_TOKEN` (same secret
ci-required-drift.yml uses — repo-admin scope required for the
endpoint per Gitea 1.22.6).
- Parses each context `<workflow_name> / <job_name> (<event>)`, walks
`.gitea/workflows/*.yml` for a file whose `name:` matches, then
YAML-AST-walks the `on:` block for `paths` / `paths-ignore` keys.
Behavior-based gate per `feedback_behavior_based_ast_gates` — NOT
grep-by-name, so reformatting / event moves still detect.
- Token-scope fallback: if `branch_protections` returns 403/404, exits
0 with a loud `::error::` rather than red-X every PR. Token issues
should be fixed at the token.
Tests
-----
20 tests in `tests/test_lint_required_no_paths.py`, all green:
- parse_context (3): standard, slash-in-job-name, malformed
- resolve_workflow_file (2): match-by-name, missing
- detect_paths_filters (8): clean, paths, paths-ignore, push.paths,
both, on-string-shorthand, on-list-shorthand, on-event-null
- run() end-to-end (7): empty contexts, clean workflow, paths fails,
paths-ignore fails, unknown-context warns-not-fails, multi-required
one-bad-one-good, protection-403 skip
Live smoke (DRIFT_BOT_TOKEN against molecule-ai/molecule-core/main):
all 3 required workflows clean — exit 0.
Cross-links
-----------
- `feedback_path_filtered_workflow_cant_be_required` (the rule now
structurally enforced)
- `feedback_behavior_based_ast_gates` (PyYAML AST walk, not grep)
- ci-required-drift.yml (precedent for DRIFT_BOT_TOKEN reuse +
branch_protections-read scope-fallback pattern)
- Charter §SOP-N rule (f): required-checks must run unconditionally
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|