muditagrawal2007 commented on PR #74909:
URL: https://github.com/apache/airflow/pull/74909#issuecomment-6238571514

   Thanks for the careful layering here — stacking on #74825 keeps the diff 
focused, and the "post the closing comment first" fix is a real correctness 
improvement that was hard to notice in the previous PR. Reading the design 
across the five follow-ups: the protect-the-gate guard closes the same 
self-deciding hole that motivated the original gate, the shared-head handling 
makes a subtle invariant explicit, and the documented ambiguity in #74905 / 
#74906 is now resolved by code rather than left as future work.
   
   A few observations, mostly small and offered as questions rather than 
blockers:
   
   ### 1. The `gate_modified` marker and `gate_checked`
   
   The other closure paths — `gate_bypassed`, the inactivity-close, the regular 
state branches — all now write `Marker(..., gate_checked=gate_checked)`. The 
new `gate_modified` branch writes `Marker(ForkState.GATE_MODIFIED.value, 
pr.head_sha, now)` without it. The doc says:
   
   > The state comment's marker records `"gate_checked": true` with the head 
commit only when this check ran for it
   
   The check *did* run for the head commit, so on a literal reading the flag 
should be set. The reason it isn't is the maintainer-reopen-without-restore 
path: `already_checked` depends on `marker.gate_checked` being False so the 
next reconcile re-runs the check and closes again. So the code is internally 
consistent, but the doc doesn't capture that exception.
   
   Two ways it could read better, whichever you prefer:
   
   - Mirror the doc to the code: always write `gate_checked=gate_checked`, and 
extend `already_checked` with `pr.last_reopened_at is None or 
pr.last_reopened_at <= marker.since`. Same behaviour, no doc exception.
   - Or add a note next to that doc sentence saying the gate-modified close 
path keeps `gate_checked` unset so a maintainer reopen re-runs the check.
   
   Whichever direction you take, `test_a_change_to_the_gate_closes` only 
asserts on `"state": "gate_modified"` in the marker, so the current choice is 
invisible to CI. Tightening to `'"state": "gate_modified"' in 
plan.marker_comment and "gate_checked" not in plan.marker_comment` would lock 
it in either direction.
   
   ### 2. `classify` reads as several branches stacked together
   
   After this PR it does, in order: `use project ci` short-circuit, ungated 
short-circuit, gate tampering, gate-modified tamper, reopen-since-closing, fork 
state observation, shared-head decision, draft/labels, status, marker. Each 
branch is short on its own. Nothing is broken — it's just that as a single 
~270-line function it's harder to follow than it deserves to be when five 
follow-ups land in the same diff. A small refactor (e.g. `_plan_closure(...)` 
for the three close paths and `_plan_record_state(state, since)` for the three 
record-only paths, with `classify` as a thin orchestrator) would keep the 
existing `Plan` shape and tests intact while making future reviews easier.
   
   ### 3. `observe_fork_state` has no `gate_modified` case
   
   ```python
   if fork.workflow_state is None:
       if marker is not None and marker.state == ForkState.AWAITING_PUSH.value:
           return ForkState.AWAITING_PUSH, None
       return None, "no fork run and the fork workflow state was not read"
   ```
   
   For a re-opened `gate_modified` PR with no fork run, this returns `(None, 
"no fork run...")` rather than `(GATE_MODIFIED, None)`. Tolerant today because 
the gate-modified check runs above `observe_fork_state`. If 
`observe_fork_state` becomes the source of truth for that state later, an 
explicit case would be needed — otherwise we'd start emitting `unknown` lines 
per run.
   
   ### 4. `_other_pull_requests` paginated once per `find()` call
   
   Result depends only on `(head_repo, head_ref, window_start)` and `find()` is 
invoked once per branch. Hoisting it into `BypassFinder.__init__` (computed 
once with the same six-hour window) would save one paginated call per PR per 
reconcile. Small thing — visible mainly in the `GitHub calls: {...}` log line.
   
   ### 5. `_set_status` URL consistency
   
   The new branch hardcodes 
`f"https://github.com/{repo}/pull/{pr.number}/files"` while the other paths use 
`fork.run_url or fork_actions_url(pr.head_repo)` or 
`f"https://github.com/{repo}/pull/{pr.number}/checks"`. The files URL is the 
right target semantically — a `gate_modified` PR has no fork run to link — but 
a `gate_changed_files_url(pr.number)` helper would keep call sites uniform and 
easy to grep.
   
   ### 6. `_list_jobs` fail-open path
   
   ```python
   try:
       jobs = self._list_jobs(run)
   except GitHubError:
       result = True
   ```
   
   On first read, "no jobs listed → counts as project CI" sounds backwards. The 
intent (better to flag a run that was CI than miss one) is correct, but the 
intent isn't captured in a comment. The existing 
`test_finder_catches_triggers_the_base_branch_does_not_have` was updated to 
provide non-empty jobs, which pins the False path; a small test for the raise 
path would round it out and stop a future refactor from flipping the fail 
direction.
   
   ### 7. `base_at` boundary
   
   ```python
   for changed_at, previous, _ in reversed(pr.base_changes):
       if when < changed_at:
           base = previous
   ```
   
   A run whose `created_at` equals the retarget `createdAt` applies the new 
base; a run a millisecond earlier applies the old base. The asymmetry is 
intentional and reads sensibly, but a one-line comment ("runs at or after a 
retarget are against the new base") would help the next reader who hits the 
strict-less-than.
   
   ### 8. `GATE_PROTECTED_PATHS` and the four back-references
   
   It's one frozenset at module scope plus four "this file is part of the gate" 
header comments. Adding a new gated file requires updating the set *and* all 
four comments. Optional, but a small test that pulls those header strings and 
asserts they reference entries in `GATE_PROTECTED_PATHS` would catch a 
forgotten update before someone changed a gate file and slipped past the check.
   
   ### 9. Minor
   
   - `Marker(ForkState.GATE_MODIFIED.value, pr.head_sha, now)` and the 
state-string literal could share a const — at this size it's fine to leave 
inline.
   - `from dataclasses import replace` is used in `is_own_pilot_change` exactly 
as expected; flagging only so it isn't read as a stray import.
   - The "Gate protection" section in 
`08_fork_ci_for_external_contributors.md`, the table-row addition, and the 
"**New comments go first.**" rule are all genuinely good changes — reading 
those gave the rest of the diff a lot of context.
   
   Overall the design holds together: the gate-tampering and shared-head fixes 
map cleanly onto #74905 / #74906 / #74907, the test density matches the risk of 
each new branch, and the run-time invariants (once per head commit, exactly one 
closure comment, retarget resets trust) all hold. The only thing I'd genuinely 
want resolved before merge is the `gate_modified` marker flag alignment in #1 — 
whichever way you settle it, a one-line comment at the construction site would 
document the choice for whoever reads it next.
   
   Happy to expand any of these into a separate PR or thread if that'd be more 
comfortable.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to