Found by the all-65 survey of the #415/#420 comment class. Full table: fleet-board/findings/gates-comment-class.md. Companions: #422 (false-negative half), #423 (false-positive half), #421 (gate-45).
This is the adjacent variant the survey was asked to flag: evidence inside a string literal counts. It splits into two groups, and the second one is the reason this is a single issue rather than sixteen.
Group 1 — blank_strings=False, or no string awareness at all
These are checkers whose comment axis was separately verified clean. Only the literal gets through.
| gate |
site |
fixture |
| 8 unsafe-auth-resolver |
php_mask(src) L101, default blank_strings=False |
$doc = 'catch (\Throwable $e) { return null; }'; in a correctly-rethrowing resolver → FAIL — 1 fail-open pattern(s) |
| 10 initial-state |
js_comment_mask keeps strings |
const doc = "document.getElementById('fx-settings').dataset.version" → FAIL — 1 DOM read(s) |
| 11 admin-router |
same |
the routes.push({path:'/settings', component: AdminRoot}) text as a double-quoted constant → FAIL — 1 admin route/import |
| 13 modal-isolation |
runner inline PYMI masks comments, never literals |
const help = 'use <NcDialog> for confirmations' → FAIL — 1 file(s) with inline modal/dialog |
| 18 notification-dialect |
tokens matched against json.dumps(rule) |
a description warning against the legacy dialect → FAIL — 1 legacy-dialect token(s) |
| 53 effective-manifest-crossref |
stripJsComments() L247 applied but not string-aware |
glob: '/*.vue' on a registry entry → FAIL — 1 cross-reference failure |
| 59 unclosable-gate |
php_mask L174 IS used (its suite mutation-checks it) but blank_strings=False |
$hint = "call setValueString('fixapp','configuration_version','3') next"; → PASS (false negative — a string closes the gate) |
| 64 apphost-autoload-prelude |
strip_comments() L207 is a proper state machine and IS applied; it preserves literals |
$hint = "we call \OC_App::registerAutoloading('openregister', $p) before this"; → PASS (false negative) |
⚠️ blank_strings is a per-caller decision and both settings are right somewhere. gate-34 and gate-58 look for string literals (window['confirm'], 'networkidle') and blanking contents would turn a fixed gate into a dead one. gate-48's 'OCS-APIRequest': 'true' is a header name that IS a string. So this is not a flag to flip globally — it is eight separate judgements, each needing a paired arm showing the real evidence survives.
Group 2 — the delimiter hole, which is ONE bug in the SHARED library 🔴
source_scope.markup_mask treats <!-- as opening a comment wherever it appears, including inside a string or a Vue interpolation. Measured directly:
markup_mask("<p>{{ '<!--' }}</p>\n<img alt=\"\" src=\"/a/avatarUrl.png\">\n<p>{{ '-->' }}</p>")
→ "<p>{{ ' \n \n ' }}</p>"
The <img alt=""> between them is blanked, and the gate goes green over live markup.
That single property makes gates 35 and 36 affected today. And six more checkers each carry their own private copy of COMMENT = re.compile(r'<!--.*?-->', re.DOTALL) with the same hole — 37, 39, 40, 42, 43, 44 — plus php_template_scope.py for 41.
🔑 This is why the obvious consolidation is currently the wrong move. Routing those eight into markup_mask — the library extracted in #184/#249 precisely to stop this drift — would fix their comment half and leave this half armed, while making it look like the class had been closed at one site.
Order of operations
- Harden
source_scope.markup_mask so <!-- inside a quoted attribute value or a {{ }} interpolation is not a comment opener, with a mutation-checked arm (flip the branch, the arm goes red).
- Prove gates 35 and 36 flip on the fixture above.
- Then migrate the six private copies +
php_template_scope, one site, with each gate's existing suite as the regression.
⚠️ A mask that over-blanks is a gate that reports nothing — which is the failure mode this whole programme exists to remove. source_scope's own header already records one instance (<template> nests, and a lazy quantifier silently dropped a real unlabelled <NcSelect> at openconnector EditMapping.vue:376). Same trap, same file.
Found by the all-65 survey of the #415/#420 comment class. Full table:
fleet-board/findings/gates-comment-class.md. Companions: #422 (false-negative half), #423 (false-positive half), #421 (gate-45).This is the adjacent variant the survey was asked to flag: evidence inside a string literal counts. It splits into two groups, and the second one is the reason this is a single issue rather than sixteen.
Group 1 —
blank_strings=False, or no string awareness at allThese are checkers whose comment axis was separately verified clean. Only the literal gets through.
php_mask(src)L101, defaultblank_strings=False$doc = 'catch (\Throwable $e) { return null; }';in a correctly-rethrowing resolver →FAIL — 1 fail-open pattern(s)js_comment_maskkeeps stringsconst doc = "document.getElementById('fx-settings').dataset.version"→FAIL — 1 DOM read(s)routes.push({path:'/settings', component: AdminRoot})text as a double-quoted constant →FAIL — 1 admin route/importPYMImasks comments, never literalsconst help = 'use <NcDialog> for confirmations'→FAIL — 1 file(s) with inline modal/dialogjson.dumps(rule)descriptionwarning against the legacy dialect →FAIL — 1 legacy-dialect token(s)stripJsComments()L247 applied but not string-awareglob: '/*.vue'on a registry entry →FAIL — 1 cross-reference failurephp_maskL174 IS used (its suite mutation-checks it) butblank_strings=False$hint = "call setValueString('fixapp','configuration_version','3') next";→ PASS (false negative — a string closes the gate)strip_comments()L207 is a proper state machine and IS applied; it preserves literals$hint = "we call \OC_App::registerAutoloading('openregister', $p) before this";→ PASS (false negative)blank_stringsis a per-caller decision and both settings are right somewhere. gate-34 and gate-58 look for string literals (window['confirm'],'networkidle') and blanking contents would turn a fixed gate into a dead one. gate-48's'OCS-APIRequest': 'true'is a header name that IS a string. So this is not a flag to flip globally — it is eight separate judgements, each needing a paired arm showing the real evidence survives.Group 2 — the delimiter hole, which is ONE bug in the SHARED library 🔴
source_scope.markup_masktreats<!--as opening a comment wherever it appears, including inside a string or a Vue interpolation. Measured directly:The
<img alt="">between them is blanked, and the gate goes green over live markup.That single property makes gates 35 and 36 affected today. And six more checkers each carry their own private copy of
COMMENT = re.compile(r'<!--.*?-->', re.DOTALL)with the same hole — 37, 39, 40, 42, 43, 44 — plusphp_template_scope.pyfor 41.🔑 This is why the obvious consolidation is currently the wrong move. Routing those eight into
markup_mask— the library extracted in #184/#249 precisely to stop this drift — would fix their comment half and leave this half armed, while making it look like the class had been closed at one site.Order of operations
source_scope.markup_maskso<!--inside a quoted attribute value or a{{ }}interpolation is not a comment opener, with a mutation-checked arm (flip the branch, the arm goes red).php_template_scope, one site, with each gate's existing suite as the regression.source_scope's own header already records one instance (<template>nests, and a lazy quantifier silently dropped a real unlabelled<NcSelect>at openconnector EditMapping.vue:376). Same trap, same file.