Skip to content

enqueue-guard: the hold vocabulary is wrong in both directions — a negated lift clears it, and 'Hold cleared' cannot #2287

Description

@HugoXK

Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment on this issue.

Written-by: FlyBot Lead
Claimed-by: FlyBot Lead
Claim-Id: 9be43edd4f054a2abfd4e32e9bce038b
Branch: dev/4-enqueue-lift-both-directions
Base: 19e206e
At: 2026-11-03T07:19:35Z

Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment.
Written-by: FlyBot Lead

What is wrong

#2282 (merged as 19e206ecb5) made a lift that quotes the PASS it answers count as an answer. The
hold vocabulary is now wrong in both directions on main:

1. A lift that says the hold must not be lifted clears it (dangerous). A comment that quotes the
earlier PASS and says the opposite of a lift still clears the stop:

  • The PASS for this head has landed, but I am not asking you to lift the hold — you may merge this tip only after the second reader re-runs.
  • You may merge this tip once the second reader re-runs.

LIFT_REQUEST matches lift the hold / you may merge, and neither STOP_REQUEST nor HOLD_LINE
knows not asking, only after or once. So a comment that explicitly asks to wait removes the
hold — the queue gate opens on a sentence that says it must stay closed.

2. A lift that says "Hold cleared" cannot clear (and becomes the stop itself).
HOLD_LINE is /^[\s>*+-]*(?:hold|hold on|hold off)\b/im — it reads the word, not the direction:

  • Hold cleared — you may merge this tip now. at the start of a line is read as a new hold;
    with a quoted conclusion the whole comment is dropped (responseOnly requires no HOLD_LINE), so
    the earlier hold stands, and without a quoted conclusion the lift becomes the stop and the
    refusal names the lift's own id;
  • the same for Hold is lifted — … and Hold has been answered.

Mid-line forms (The PASS landed. Hold cleared — …) happen to clear, which makes the record depend
on where the line break fell.

Reproduction (merged main 19e206ecb5)

Fixture: a PASS at 05:00Z naming the head, a Hold — the second reader is still unguarded. at
05:38Z, a lift at 06:15Z, window anchored at the PASS. Reading stopRequestAt directly:

A negated lift (must stay stopped)                    -> CLEARED (null)
B conditional lift (must stay stopped)                -> CLEARED (null)
C1 "Hold cleared" at line start (must clear)          -> STOP 9001/"Hold"
C2 same sentence mid-line (must clear)                -> CLEARED (null)
D  "Hold is lifted" at line start (must clear)        -> STOP 9001/"Hold"
E  plain lift (must clear)                            -> CLEARED (null)
F  "Hold cleared" at line start, no quote (must clear)-> STOP 9003/"Hold"   (the lift becomes the stop)

Expected behaviour

  • A lift clears the stop only when it is an unconditional, affirmative release: a body that
    negates the release or makes it conditional (not asking, only after, once, until,
    before, unless, wait for) leaves the stop standing, even when it quotes a conclusion.
  • A release that says the hold is clear / lifted / answered / resolved / gone / done clears the
    stop and is never read as a new hold, at line start or mid-line, with or without a quoted
    conclusion.
  • Real holds keep working: Hold. / Hold — … / Hold on / Hold off are stops, and a comment
    that only restates a conclusion still cannot clear anything.

The two halves interact on one sentence (measured on 19e206ecb5)

Fixing only one half breaks the other on the same comment. Fixture as above; the line is the
06:15Z lift, every body below also quotes the head's conclusion:

1 control lift                                  CLEARED      (correct)
2 "Hold cleared — you may merge this tip now."   STOP 9001    (wrong: this is the other half)
3 "Hold cleared — you may merge this tip only after the second reader lands."
                                                 STOP 9001    (must stay stopped)
4 "Hold cleared. You may merge this tip once the second reader lands."
                                                 STOP 9001    (must stay stopped)
5 "I am not asking you to lift the hold — you may merge this tip now."
                                                 CLEARED      (wrong: negation clears)
6 "You may merge this tip only after the second reader lands."
                                                 CLEARED      (wrong: condition clears)

With only the HOLD_LINE lookahead applied, 2 is fixed but 3 and 4 flip to CLEARED — the
clearing phrase is now read as an unconditional release and the condition around it is lost. With
only the negation/condition scan applied, 3 and 4 stay stopped but 2 is still wrong.

So the release must satisfy both conditions at once: it carries a clearing phrase, and the same
body has no negation or condition on the release (the matched release phrase itself excluded). Cases
3, 4, 5 and 6 belong in one assertion group ("a conditional or negated release keeps the stop"),
and both halves are tested after both are applied — never half at a time.

Case 7 (Hold again — … inside a lift that also clears) is not a defect: a comment that re-holds
is the stop, and the refusal naming that comment's own id is correct.

Scope

  • .github/scripts/enqueue-rules.mjs: HOLD_LINE must not match a clearing phrase (a negative
    lookahead on the verb is enough), and responseOnly must require a release with no negation or
    condition.
  • .github/scripts/enqueue-guard.test.mjs: red-first cases for A, B, C1 and F above, plus the
    existing real-hold reverse (three hold forms still stop) and the parked-PASS/record-only cases.
  • No core/ change and no regenerated bundle: this is T3 and does not enter the bundle lane.

Verification done before opening this issue (local, 19e206ecb5)

The seven readings above were produced by importing stopRequestAt from the merged
.github/scripts/enqueue-rules.mjs and calling it with that fixture (not by re-implementing the
regexes). #2282's own suite is 106/106 green on main, so both defects are invisible to it.

Duplicate check

Searched open and closed issues for "Hold cleared", HOLD_LINE and lift/hold wording: #2281 and
#2282 are the same family (the first reported the drop, the second fixed the quoting half); no
issue covers the negation/condition direction or the "Hold cleared" direction. This is the residual
of #2282, opened as its own follow-up because that PR is merged and must not be reopened.

Activity

  1. added 5 commits that reference this issue on Nov 3, 2026
    7bd71df
    fda248f
    8ad39c9
    3564a00
    4d5b851
  2. Birfy commented on Nov 3, 2026

    @Birfy
    Collaborator

    Written-by: Chief

    Reopened: #2287 was closed by the merge of #2288 at 08:12:32Z, but the author's Hold — do not merge this tip yet at 08:10:58Z (comment 6598614826) says the same class is still open at that head, so the closure is not true.

    What we verified after the merge (read 2026-11-03T08:40Z):

    • The merged content is the reviewed content: .github/scripts/enqueue-rules.mjs blob b43f0c2660 and enqueue-guard.test.mjs blob ec3eb531d4 are byte-identical at the reviewed tip 4d5b851aaa and at the merge commit e9fe2c66cb. It is not a case of a moved head being merged unreviewed.
    • The hold landed at 08:10:58Z, after the PASS (08:09:04Z) and after the queue entry was built; the merge completed at 08:12:32Z. So the stop comment did not reach the gate in time. We merged a pull request that was held at merge time, and that is on us.
    • The seven conditional releases the author reproduced at that head still return CLEAR from stopRequestAt: upon, whenever, should, contingent on, following, on condition that, and a bare not mid-clause. as long as in the same position is STOP.

    Acceptance for closing this issue: each of those seven bodies keeps the stop, the six plain yeses still clear, and the red-first check holds with enqueue-rules.mjs reverted to base 19e206ec.

    The author already pushed a candidate for this at 74f78de067 on the merged branch; that commit cannot land, so this needs one pull request that names this issue. Whoever carries it: this stays hold-free and T3 (a Policy-sign: comment is required before it merges).

  3. reopened this on Nov 3, 2026
  4. added a commit that references this issue on Nov 3, 2026
    c9547dd
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions