PRESCRIPTION CORRECTED after executable falsification (2026-08-24). The original clause-terminal design could not match its own positive example. The author accepted the falsifier and narrowed the contract in the authoritative correction comment. PR #17738's review then executed the closed list and found another also acts as a determiner (another parser / another run), re-admitting the concrete-noun ambiguity. The close-target is therefore the three tokens that can only be pro-forms.
Context
Direct successor to #16325, which added unless you'd rather for the lane-handback shape. The neighboring spelling below still slips through:
Taking <lane> unless you want something else first.
The lane is already chosen; “something else” manufactures an unspecified permission gate over work the agent already owns. The runtime matcher currently returns null for this specimen.
The Problem
ai/scripts/lifecycle/deferencePhraseMatch.mjs carries nearby phrases such as unless you want me, but nothing detects unless you want followed by an indefinite alternative.
The original ticket mis-analogized this to if you want it and prescribed CLAUSE_TERMINAL_PHRASES. That was wrong: the positive example has a following object, so isClauseTerminal() correctly rejects it. Bending clause-terminality to admit a following word would weaken the working if you want it discriminator.
The real separator is the object:
| specimen |
following object |
verdict |
…unless you want something else first. |
indefinite pro-form; no concrete alternative named |
handback — fire |
…unless you want anything else. |
indefinite pro-form |
handback — fire |
…unless you want deterministic isolation. |
concrete technical noun phrase |
ordinary prose — do not fire |
…unless you want a smaller batch size. |
concrete technical noun phrase |
ordinary prose — do not fire |
…unless you want the other parser. |
concrete technical noun phrase |
ordinary prose — do not fire |
The Architectural Reality
DEFERENCE_PHRASES is deliberately a small data registry; broadening requires bidirectional falsifiers.
matchDeferencePhrase() already normalizes inline emphasis and whitespace before judging a match.
isClauseTerminal() belongs only to phrases whose meaning changes at clause end. It is not the object discriminator here.
stopHookDecision.mjs still gates the matcher out during live operator dialogue. That separate axis remains #16338 and is out of scope.
The Fix
Add unless you want as a registered phrase with a second, explicit data gate: it matches only when the next lexical token belongs to the closed indefinite-pro-form set:
['something', 'anything', 'otherwise']
The set must remain data, not an open regex category, so every future addition is a reviewable semantic expansion. The helper may consume ordinary spaces/tabs or one soft wrap before the token; a paragraph break does not join the object slot.
Do not add the phrase to CLAUSE_TERMINAL_PHRASES, broaden bare unless you want, or change isClauseTerminal().
Contract Ledger
| Target Surface |
Source of Authority |
Proposed Behavior |
Fallback / Edge Case |
Docs |
Evidence |
DEFERENCE_PHRASES entry unless you want |
This corrected ticket + Ada's accepted narrowing comment |
Candidate phrase enters the ordinary boundary matcher, then passes only when its following object token belongs to the closed indefinite set |
A concrete noun phrase, terminal punctuation, or an unlisted token returns no match |
Registry comment names why presence alone is unsafe |
Bidirectional object-corpus unit arms |
| Indefinite-object discriminator |
matchDeferencePhrase() after code/quote/emphasis normalization |
Consume spaces/tabs or one soft wrap, read one lexical token, compare against something / anything / otherwise |
Paragraph break does not bridge; missing token fails closed; another <noun> is a concrete phrase and stays silent |
Helper JSDoc states the grammar boundary |
emphasis + soft-wrap + paragraph-break + another parser negative arms |
Existing unless you want me |
Current registry contract |
Continues matching independently and returns its own longer phrase |
New shorter entry may not shadow or make it redundant |
Existing registry commentary retained |
exact-return-value regression arm |
Existing if you want it clause-terminal path |
CLAUSE_TERMINAL_PHRASES + isClauseTerminal() |
Unchanged |
All existing terminal/predicate/layout cases remain green |
No documentation delta |
full existing suite + explicit regression control |
Acceptance Criteria
Out of Scope
- The
operatorInLoop carve (#16338).
- Bare or clause-terminal
unless you want.
- The false-positive citation-window specimen recorded in the thread; it is the opposite axis and requires its own cost decision.
- Structural/phraseless deference.
Avoided Traps
- Unconditional substring matching: would reserve ordinary technical English and turn the hook into a leash.
- Reusing clause-terminality past its precondition: cannot reach the originating positive and risks weakening
if you want it.
- An open-ended regex for indefinite language: hides semantic expansion inside matcher syntax; the closed data set keeps it reviewable.
- Treating every member of an indefinite class as grammar-equivalent:
another can be a pro-form or a determiner, so admitting the token unconditionally reopens concrete noun phrases.
- Treating one positive as sufficient: the negative corpus is load-bearing because false positives block correct work.
Related
#16325 (neighboring spelling) · #16338 (live-dialogue carve) · #16967 (if you want it clause-terminal precedent) · #16613 · #14420
Decision Record impact: none.
Origin Session ID: 316c8c7f-a8eb-4c57-b682-3c92e9a0daa8
Handoff Retrieval Hints
query_raw_memories: deference detector object slot unless you want third party
- Anchors:
ai/scripts/lifecycle/deferencePhraseMatch.mjs · test/playwright/unit/hooks/deferencePhraseMatch.spec.mjs
Context
Direct successor to
#16325, which addedunless you'd ratherfor the lane-handback shape. The neighboring spelling below still slips through:The lane is already chosen; “something else” manufactures an unspecified permission gate over work the agent already owns. The runtime matcher currently returns
nullfor this specimen.The Problem
ai/scripts/lifecycle/deferencePhraseMatch.mjscarries nearby phrases such asunless you want me, but nothing detectsunless you wantfollowed by an indefinite alternative.The original ticket mis-analogized this to
if you want itand prescribedCLAUSE_TERMINAL_PHRASES. That was wrong: the positive example has a following object, soisClauseTerminal()correctly rejects it. Bending clause-terminality to admit a following word would weaken the workingif you want itdiscriminator.The real separator is the object:
…unless you want something else first.…unless you want anything else.…unless you want deterministic isolation.…unless you want a smaller batch size.…unless you want the other parser.The Architectural Reality
DEFERENCE_PHRASESis deliberately a small data registry; broadening requires bidirectional falsifiers.matchDeferencePhrase()already normalizes inline emphasis and whitespace before judging a match.isClauseTerminal()belongs only to phrases whose meaning changes at clause end. It is not the object discriminator here.stopHookDecision.mjsstill gates the matcher out during live operator dialogue. That separate axis remains#16338and is out of scope.The Fix
Add
unless you wantas a registered phrase with a second, explicit data gate: it matches only when the next lexical token belongs to the closed indefinite-pro-form set:['something', 'anything', 'otherwise']The set must remain data, not an open regex category, so every future addition is a reviewable semantic expansion. The helper may consume ordinary spaces/tabs or one soft wrap before the token; a paragraph break does not join the object slot.
Do not add the phrase to
CLAUSE_TERMINAL_PHRASES, broaden bareunless you want, or changeisClauseTerminal().Contract Ledger
DEFERENCE_PHRASESentryunless you wantmatchDeferencePhrase()after code/quote/emphasis normalizationsomething / anything / otherwiseanother <noun>is a concrete phrase and stays silentanother parsernegative armsunless you want meif you want itclause-terminal pathCLAUSE_TERMINAL_PHRASES+isClauseTerminal()Acceptance Criteria
unless you wantmatches only when followed by an indefinite pro-form from the explicit closed setsomething / anything / otherwise;another <noun>remains ordinary prose.something else firstandanything elsefire;deterministic isolation,a smaller batch size, andthe other parserdo not.unless you want.remains out of scope and does not silently become a match.unless you want mestill matches independently; the tests distinguish which registered phrase fired.if you want itremains green and unchanged.Out of Scope
operatorInLoopcarve (#16338).unless you want.Avoided Traps
if you want it.anothercan be a pro-form or a determiner, so admitting the token unconditionally reopens concrete noun phrases.Related
#16325(neighboring spelling) ·#16338(live-dialogue carve) ·#16967(if you want itclause-terminal precedent) ·#16613·#14420Decision Record impact: none.
Origin Session ID: 316c8c7f-a8eb-4c57-b682-3c92e9a0daa8
Handoff Retrieval Hints
query_raw_memories:deference detector object slot unless you want third partyai/scripts/lifecycle/deferencePhraseMatch.mjs·test/playwright/unit/hooks/deferencePhraseMatch.spec.mjs