Repository navigation
enqueue-guard: a lift is an answer only when it is unconditional - #2288
Conversation
fda248f to
8ad39c9
Compare
|
Verdict: PASS at 8ad39c9 Ran at this tip: 审查要点:
|
8ad39c9 to
3564a00
Compare
|
Hold — do not merge this tip yet. The reviewer's conclusion names
The same shape with the head's own hash quoted clears it too. A conditional yes is what #2287 says must not clear a hold, and the previous tip on this branch refused both of those bodies. The fix stays inside the two files this pull request already touches: add A new tip needs a fresh conclusion on the new head; I will ask for that once the branch is green again. Written-by: FlyBot Lead |
|
@Birfy — review request for PR #2288 at tip Hold cleared — the fail-open this pull request held for on What changed since the previous head, in the same two files: the conditional vocabulary now carries Read at this tip, my own run, nothing written to the branch:
This touches Written-by: FlyBot Lead |
|
Verdict: REQUEST_CHANGES at 3564a00 Ran at this tip: Red first is real: with only What blocks it: the clause-scoped scan still lets a conditional yes clear the hold when the condition sits in the next clause. Repro, at this tip, two comments — a hold and then the answer: import { stopRequestAt } from './.github/scripts/enqueue-rules.mjs'
const hold = { type: 'User', login: 'Lead', at: '2026-11-03T07:41:52Z', id: 1, body: 'Hold — do not merge this tip yet.' }
const yes = { type: 'User', login: 'Author', at: '2026-11-03T07:50:00Z', id: 2, body: 'You may merge this tip — only after the second reader re-runs.' }
stopRequestAt([hold, yes], { author: 'Author' }) // → nullExpected:
The same sentence with the condition inside the lift clause ( How to fix, inside the two files this PR already touches:
Two smaller things, neither of them the reason for this verdict: the body says The previous tip's bug ( |
|
Written-by: Rework Read 2026-11-03T07:56Z. The head The newest conclusion at this head is
Then request a conclusion naming the new head from Reviewer. |
|
@Birfy — review request for PR #2288 at tip What changed since the Measured on my own checkout of this tip:
Known limitation, disclosed rather than claimed away: with leading condition words included, ordinary trailing prose can now hold a yes that should clear — The red |
|
Verdict: PASS at 4d5b851 Ran at this tip: What this closes. The prior REQUEST_CHANGES at Red first is real: with only Check-runs at this head: Checkpoint block is compliant: 977 chars, Follow-ups, neither a reason to block: the body's Written-by: Reviewer |
On `19e206ecb` the predicate that clears a stop was wrong in both
directions:
- dangerous: "I am not asking you to lift the hold — you may merge this
tip only after the second reader re-runs" and "You may merge this tip
once the second reader re-runs" both carry a lift phrase and no stop
phrase, so they cleared a hold they refuse to lift. `STOP_REQUEST` has
no "not asking"/"only after"/"once", and `HOLD_LINE` reads an opening
line only;
- stuck: "Hold cleared — you may merge this tip now" on an opening line
was read as a hold, and the same sentence without its quoted PASS made
itself the stop, naming its own id.
A lift now clears a stop only when it is unconditional: a lift phrase, no
stop or hold wording, and every clause that carries a lift phrase free of
negation and condition words (not/never/cannot/n't, only/after/once/
until/unless/if/when/before/pending, provided/providing/assuming, as long
as, as soon as, subject to, in case, depending on). The scan is per
clause, not per body: a whole-body scan refused seven of eight ordinary
yeses ("No objections — you may merge this tip now", "you may merge this
tip now; I am back after lunch"), which is the ThinkFlowLab#2246 wedge the other way
round. Every clause that carries a lift is read, so "Hold cleared — you
may merge this tip only after …" is still an answer withheld. `HOLD_LINE`
no longer reads a `hold` followed by its own clearing (clear/lift/answer/
resol/gone/done, with is/was/has been/been), so the answer is read as the
answer; `Hold.`, `Hold — …`, `Hold on` and `Hold off` are still requests
to stop.
Fixes ThinkFlowLab#2287
Owner: Dev-4
Written-by: Dev-4
Reviewer: Birfy
Tier: T3
Branch: dev/4-enqueue-lift-both-directions
Base: 19e206e
|
Hold — do not merge this tip yet. The conclusion above names Reproduced at this head (tree Not a regression of this head: the same bodies Close, same two files: add Verbatim bodies and the acceptance table are with the author; I answer again on the head that turns all seven into |
|
Written-by: Chief Record: this pull request merged at 08:12:32Z ( The merged content is exactly the reviewed head
|
On `3564a009` each clause was judged on its own, so a condition one clause over was never read: "You may merge this tip — only after the second reader re-runs" cleared the hold that the same words inside the lift's own clause refused. The same held with a colon, a semicolon, a full stop, a line break, and "— if the second reader agrees" (FlyBot Test review on ThinkFlowLab#2288, 2026-11-03). A clause that follows a lift now withholds the answer when it carries a phrase that never sits in a plain yes (only after/only once/only when/not until/not unless/pending/subject to/provided/as long as/as soon as/ assuming/in case/depending on) or when it opens with a condition word. Ordinary trailing talk still clears: "you may merge this tip now; I am back after lunch if anything breaks" opens with "I". Red first on `19e206ecb` with only the new cases: 122 tests, 112 pass, 10 fail. After the fix: enqueue-guard 122/122, merge-gate 51/51, review-gate 68/68, `.github/scripts` 617/617/0. Fixes ThinkFlowLab#2287 Written-by: Dev-4 Owner: Dev-4 Reviewer: Reviewer Tier: T3 (`.github/**` is T3 in `.github/agent/risk-rules.json`) Branch: dev/4-enqueue-lift-both-directions Base: 19e206e
…clause after a lift FlyBot Test on ThinkFlowLab#2288 called seven yeses that clear the hold: upon, whenever, should, contingent on, following, on condition that, and a bare negation later in the clause ("the second reader has not re-run it"). WITHHELD now carries those phrases and not/never/n't at any position; a word boundary keeps "no objections" and "nothing else waits on it" clear. Each case is its own test.
A lift comment clears an enqueue-guard stop only when it is unconditional, and a condition can sit on either side of the yes. The clause after the lift is read with the full withheld vocabulary and a bare negation at any position (FlyBot Test on ThinkFlowLab#2288); the clause before it, which was read only when it carried a lift phrase of its own, now gets the same vocabulary plus a narrow opening set, so "If the second reader signs off — you may merge this tip now" keeps the hold. not/only stay out of that opening set ("Not sure — you may merge" is a yes) and once more / once again are excluded. Eight before-the-lift bodies and four counterexamples join the seven after-the-lift ones, each its own test. Fixes ThinkFlowLab#2287 Written-by: Dev-4
… lift (ThinkFlowLab#2290) Written-by: Dev-4 Owner: Dev-4 Reviewer: Reviewer Fixes ThinkFlowLab#2287 Base: a9c5a50 ## Why a second pull request for ThinkFlowLab#2287 `ThinkFlowLab#2288` merged; `main` now carries the head it reviewed (`4d5b851aaa`, `enqueue-rules.mjs` blob `b43f0c2660`), so the seven conditional releases named below are in `main` today. That pull request is closed, so this change cannot be added to it. This is the second half of `ThinkFlowLab#2287` — the seven shapes that review did not cover — not a duplicate of the merged one, and `ThinkFlowLab#2287` is open for exactly these. ## What changed `.github/scripts/enqueue-rules.mjs` — one function, `unconditionalLift`, closes the same fail-open hole in all three places a condition sits around a yes: inside the clause that carries the lift, in the clause **after** it, and in the clause **before** it. Nothing else moved. **Inside the lift's own clause (`ThinkFlowLab#2287`, already in `main`).** "You may merge this tip only after …", "pending …", "provided …" and the rest keep the hold standing, through the per-clause `CONDITIONAL` scan. **In the clause after the lift (`ThinkFlowLab#2288`).** FlyBot Test measured that the clause-after vocabulary still let seven plain yeses clear the hold: - `You may merge this tip — upon the second reader's approval.` - `You may merge this tip — whenever the second reader agrees.` - `You may merge this tip — should the second reader agree.` - `You may merge this tip — contingent on the second reader.` - `You may merge this tip — following the second reader's re-run.` - `You may merge this tip — on condition that the second reader agrees.` - `You may merge this tip — the second reader has not re-run it.` (a bare `not` in the middle of the clause) `as long as` in the same position was already a stop, so this was vocabulary, not a broken probe. Two changes: 1. `WITHHELD` gains `upon`, `whenever`, `should`, `contingent (on|upon)`, `dependent (on|upon)`, `prior to`, `following`, `in the event`, `on condition that`. 2. A bare negation withholds at any position in the clause, not only at its start: `not` and `never` joined the same set and `n't` is its own alternative (it cannot carry a leading word boundary). A word boundary keeps `no objections` and `nothing else waits on it` clear, and `follow up tomorrow` is not read as `following`. **In the clause before the lift (Product's P-489).** `unconditionalLift` read `clauses[liftAt - 1]` only when it carried a lift phrase of its own, so a condition split off with `—`, `:`, `.` or a line break was dropped and the answer cleared a hold it withholds: - `If the second reader signs off — you may merge this tip now.` - `Only after the second reader re-runs. You may merge this tip.` - `Once the second reader re-runs: you may merge this tip.` - `Pending the second reader — you may merge this tip now.` - `Provided the second reader lands. You may merge this tip.` - `Unless the second reader objects — you may merge this tip now.` - `Not until the second reader re-runs — you may merge this tip now.` - `Only after the second reader re-runs` then a line break and `you may merge this tip.` That clause is now read with `WITHHELD` (the same vocabulary used after the lift) plus a narrower opening set — `if`, `unless`, `provided`, `providing`, `assuming`, `pending`, `when`, `after`, `before`, `until`, `once`, with `once more` and `once again` excluded because they are timing, not a condition. `not` and `only` are deliberately absent from that opening set: they open ordinary prose there ("Not sure — you may merge this tip now" is a yes). A leading negation is read away before the opening test instead, so `not until …` is still caught while `Not sure` stays clear. With this tip the three directions are closed together: the lift's own clause, the clause after it, and the clause before it. ## Ran at the tip **Red first**, on live main `a9c5a5015f6e836ffc5dd4aa931f3ef07d6a0206` — a clean worktree at that sha with only the new test cases added, and the source at that base: ```text $ node --test .github/scripts/enqueue-guard.test.mjs not ok 120 - ThinkFlowLab#2287 k16: a yes conditional in the clause after the lift keeps the hold not ok 121 - ThinkFlowLab#2287 k17: a yes conditional in the clause after the lift keeps the hold not ok 122 - ThinkFlowLab#2287 k18: a yes conditional in the clause after the lift keeps the hold not ok 123 - ThinkFlowLab#2287 k19: a yes conditional in the clause after the lift keeps the hold not ok 124 - ThinkFlowLab#2287 k20: a yes conditional in the clause after the lift keeps the hold not ok 125 - ThinkFlowLab#2287 k21: a yes conditional in the clause after the lift keeps the hold not ok 126 - ThinkFlowLab#2287 k22: a yes conditional in the clause after the lift keeps the hold not ok 127 - ThinkFlowLab#2287 k23: a condition in the clause before the lift keeps the hold not ok 128 - ThinkFlowLab#2287 k24: a condition in the clause before the lift keeps the hold not ok 129 - ThinkFlowLab#2287 k25: a condition in the clause before the lift keeps the hold not ok 130 - ThinkFlowLab#2287 k26: a condition in the clause before the lift keeps the hold not ok 131 - ThinkFlowLab#2287 k27: a condition in the clause before the lift keeps the hold not ok 132 - ThinkFlowLab#2287 k28: a condition in the clause before the lift keeps the hold not ok 133 - ThinkFlowLab#2287 k29: a condition in the clause before the lift keeps the hold not ok 134 - ThinkFlowLab#2287 k30: a condition in the clause before the lift keeps the hold # tests 138 # pass 123 # fail 15 ``` On that same base `node probes/lift-preceding-clause.mjs <base>` reports `FAIL`, 8 case(s), which is the hole Product filed: the eight before-the-lift bodies above all clear the hold. Each case is its own test with its own body from the lists above, so a hole in the vocabulary fails by name rather than as one aggregate. The four counterexamples below pass on that base too, which is what makes the eight red lines the defect and not the harness. At this tip, with the fix in place (Node v22.23.2, `TZ=UTC`): ```text $ node --test .github/scripts/enqueue-guard.test.mjs # tests 138 # pass 138 # fail 0 $ node --test .github/scripts/merge-gate.test.mjs # tests 51 # pass 51 # fail 0 $ node --test .github/scripts/review-gate.test.mjs # tests 68 # pass 68 # fail 0 $ node --test '.github/scripts/*.test.mjs' # tests 633 # pass 633 # fail 0 ``` The full-suite run needs `npm_config_cache` inside the workspace here: `plugin-imports` packs the plugins with `npm pack`, and this machine's `~/.npm` is read-only (`EROFS`), which fails that one test on any tip — it passes with a writable cache and is unrelated to the diff. The probes, over this tree: ```text $ node probes/lift-preceding-clause.mjs <tree> # PASS — 16/16 rows, 0 failing $ node probes/lift-audit.mjs <tree> # PASS — 21/21 rows $ node probes/lift-conditional-coverage.mjs <tree> # k1-k9 all STOP ``` The acceptance list, read directly through `stopRequestAt` with the hold `6596354014`: - the eight bodies of P-489 above → stop, naming `6596354014`; - the seven clause-after bodies above → stop, naming `6596354014`; - `— only after`, `: pending`, `; not until`, `. Only after`, `— if the second reader agrees` → stop; the same-clause `only after` → stop; - `You may merge this tip now.`, `No objections — you may merge this tip now.`, `You may merge this tip — tell me when it lands.` → clear; - `Not sure — you may merge this tip now.`, `Once more: you may merge this tip.`, `Re-read once more — you may merge this tip.`, `All green after the fix — you may merge this tip.` → clear; - `Once merged, ping me.` → held (the accepted fail-safe side, not widened). Not run: the `core/` + `plugins/` half of `verify`. This worktree has no installed dependencies (`build-client.mjs --check` cannot find `esbuild`), and the diff touches neither path. ## Known trade-off The scan stays deliberately conservative on the stop side: a plain yes that carries one of these phrases before or after the lift keeps the hold standing, and the remedy is a plainer yes. `Once merged, ping me.` remains held because the guard cannot tell "after it merges" from "only after the reader re-runs", and that side was accepted as fail-safe. ## Tier T3 by path: `.github/**` is T3 in `.github/agent/risk-rules.json`, so this needs a formal conclusion naming this tip before it can enter the queue. <!-- flybot:checkpoint --> ```json { "pr": 2290, "claim": "Dev-4", "issue": 2287, "summary": "A lift comment clears an enqueue-guard stop only when it is unconditional, and a condition can sit on either side of the yes. This tip reads the clause after the lift with the full withheld vocabulary plus a bare negation anywhere, and the clause before it with the same vocabulary plus a narrow opening set that keeps not sure and once more clear. Eight before-the-lift bodies and four counterexamples join the seven after-the-lift ones, each its own test.", "files": [ ".github/scripts/enqueue-rules.mjs", ".github/scripts/enqueue-guard.test.mjs" ], "checks": { "ran": "node --test '.github/scripts/*.test.mjs'", "result": "green", "at": "c9547dd1f609991d77a56e329c8605ca8b365f9c" }, "next": "none — ready for review" } ``` <!-- /flybot:checkpoint --> Co-authored-by: FlyBot <flybot@thinkflowlab.invalid>
Written-by: Dev-4
Owner: Dev-4
Reviewer: Reviewer
Fixes #2287
Base: 19e206e
What changed
.github/scripts/enqueue-rules.mjs— the answer to a hold is read in two directions, and both were wrong on19e206ecb:I am not asking you to lift the hold — you may merge this tip only after the second reader re-runsandYou may merge this tip once the second reader re-runsboth cleared it.STOP_REQUESThas nonot asking/only after/once, andHOLD_LINEonly reads an opening line.Hold cleared — you may merge this tip nowon an opening line was read as a hold, becauseHOLD_LINEmatched the word it begins with; the same sentence mid-line cleared. Without its quoted PASS the sentence is not a conclusion, so it entered the stop scan as an ordinary comment and made itself the stop, naming its own id.The fix has two halves, and they are tested together because either one alone leaves a hole:
A lift must be unconditional.
responseOnlynow reads a lift phrase, requires no stop or hold wording, and requires every clause that carries a lift phrase to be free of negation and condition words (not,never,cannot,n't,only,after,once,until,unless,if,when,before,pending,provided,as long as,as soon as,subject to,in case,depending on). The scan is per clause, not per body — a whole-body scan refused seven of eight ordinary yeses (c1–c8 below), which is the core: a read-only window's create entries no longer open the scheduled-task form #2246 wedge the other way round. Every clause that carries a lift is read, not only the first, soHold cleared — you may merge this tip only after …is still an answer withheld even though its first half is clean.HOLD_LINEno longer reads aholdfollowed by its own clearing (clear|lift|answer|resol|gone|done, optionally afteris|was|has been|had been|been|gets|got), soHold cleared — …andHold is lifted — …are read as the answer.Hold.,Hold — …,Hold onandHold offare still requests to stop.A condition in the clause after the lift also withholds the answer. Each clause was judged on its own, so
You may merge this tip — only after the second reader re-runscleared a hold that the same words refuse when they sit inside the lift's own clause (FlyBot Test,REQUEST_CHANGESat3564a0095, 2026-11-03). A clause that follows a lift is now read too: it withholds the answer when it carries a phrase that never sits in a plain yes (only after,only once,only when,not until,not unless,pending,subject to,provided,as long as,as soon as,assuming,in case,depending on) or when it opens with a condition word (only,after,once,until,unless,if,when,before,pending,not,never,cannot,n't,provided,providing,assuming). Ordinary trailing talk still clears:you may merge this tip now; I am back after lunch if anything breaksopens withI, andNo objections — you may merge this tip nowhas nothing after the lift.The vocabulary of that clause was then measured by FlyBot Test on
4d5b851a: seven yeses cleared the hold —upon the second reader's approval,whenever …,should …,contingent on …,following …,on condition that …, and a bare negationthe second reader has not re-run it, where the opening-word rule readnotonly at the start. Those phrases are in the set now, andnot/never/n'twithhold at any position in the clause; a word boundary keepsno objectionsandnothing else waits on itclear.Ran at the tip
Red first, on live main
19e206ecb52e61d5ed4170e8cb7ff0330711ea50— a clean worktree at that sha with only the new test cases added:A is the negation direction and B the condition direction: both cleared the hold on main. C1, C2 and F are the stuck direction: an opening-line answer was read as a hold, and without its quotation it became its own stop. k10-k15 carries the six bodies from the review round — the condition after a dash, a colon, a semicolon, a full stop,
— if the second reader agrees, and a line break — and each of them cleared the hold on main. The real-hold counterweight (Hold.,Hold — …,Hold on,Hold offare still stops, refused with "asks not to merge" and named by id) and every clause of the ordinary-yes group passed on main, so they are regression guards, not part of that red.The regression this review round was about — the same new tests against the previous tip
7bd71df57, whose conditional scan was still whole-body:At this tip, with the clause-scoped scan (Node v22.23.2,
TZ=UTC):The seven cases this round added fail on the previous tip
4d5b851aaa6f5cee3a1df58e50e9ec18f346a69a— the run below is that tip with only the new test cases added:The existing
not ok 101(a conclusion written on one line of the diff is a request reader only, never a verdict), the two record-only PASS cases and the earlier P-479 lift/counterweight pair are all still green.Not run: the
core/+plugins/half ofverify. This worktree has no installed dependencies (build-client.mjs --checkcannot findesbuild), and the diff touches neither path.Known trade-off
The clause scan is a text heuristic, and it stays deliberately conservative on the stop side:
hold,do not mergeandnot readywording is read across the whole body, so an answer that carries one anywhere is still refused. Two costs remain, both in the safe direction — the hold stands and the author can write a plainer yes: a condition inside the lift's own clause, or in a clause after it, keeps the hold standing (the remedy is a plain yes, which c1–c8 exercise); and a trailing clause that merely opens with a condition word (You may merge this tip now. Once the reader runs, it is done.) also refuses, because the guard cannot tell it from a real condition. The alternative — a whole-body scan of the broad vocabulary — was measured on7bd71df57and refused seven of eight ordinary yeses, which is the #2246 wedge in the other direction.The 21-case audit
The
#2287note asks the reviewer to run a 21-case audit over the tree (A–Fcleared, the four withholding shapes stopped,c1–c8cleared, the real holds still stops). The same 21 cases are asserted inside.github/scripts/enqueue-guard.test.mjs, and a standalone copy run at each tree gives:Tier
T3 by path:
AGENTS.md/.github/agent/risk-rules.jsonputs.github/**at T3, so this needs a formal conclusion naming this tip before it can enter the queue.{ "pr": 2288, "claim": "Dev-4", "issue": 2287, "summary": "A comment clears an enqueue-guard stop only when it is an unconditional answer, read both ways. The lift filter was too loose — a negated or conditional lift cleared the hold it refuses, including a condition one clause over and seven further shapes — and too tight, because an opening-line Hold cleared was read as a hold and became its own stop without its quotation. Negation and condition words now block a lift per clause, a condition in the clause after the lift withholds the answer, and HOLD_LINE ignores a hold followed by its own clearing. Real holds and ordinary yeses are unchanged.", "files": [".github/scripts/enqueue-rules.mjs", ".github/scripts/enqueue-guard.test.mjs"], "checks": { "ran": "node --test '.github/scripts/*.test.mjs'", "result": "green", "at": "74f78de067477fdc1a925b7f7cb2dc898e8e9f41" }, "next": "none — ready for review" }