pf_app
A view narrowed with `partd contains 'Keysto'` could be totalled but not written: `SCOPE_OPS` carried the comparison and list operators and refused everything else, so the panel showed a figure the operation answered 400 on. Refusing is the right default -- a scope silently dropped is a write wider than the panel that authorised it -- but `contains` is how a text dimension is actually filtered, the alternative being an IN list of the 180 descriptions that happen to match today. ILIKE, not LIKE, because that is what the engine does: `t_tscalar::contains` lowercases both sides before `std::string::find`, as do `begins_with` and `ends_with`. On `fc_osm_skinny_34` the difference is 16,864 rows against 35, so LIKE would have written a fortieth of what the grid displayed. Narrower is no safer than wider here -- either way it is not the set the ledger counted. The term is literal in the engine, so `%`, `_` and `\` are escaped out of it before it becomes a pattern; `50%` means those three characters. `matches` stays refused. It is RE2 in the engine and Postgres regexes are not RE2 -- `\b` is a word boundary in one and a backspace in the other -- so a pattern both accept could match a different set of rows, which is worse than a 400. Every operator that takes a term now refuses an empty one. An unfinished filter row in the pivot arrives as `[col, '==', null]`, which used to compile to `= ''`; as a substring op it would have been `ILIKE '%%'`, which is every non-null row and therefore no scope at all. Runtime code, not template: nothing to regenerate, only a restart. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
||
|---|---|---|
| lib | ||
| public | ||
| routes | ||
| setup_sql | ||
| ui | ||
| .env.example | ||
| .gitignore | ||
| CLAUDE.md | ||
| install.sh | ||
| package-lock.json | ||
| package.json | ||
| PERSPECTIVE.md | ||
| pf_perspective_options.md | ||
| pf_spec.md | ||
| pf.sh | ||
| server.js | ||