Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .changeset/20751-services-strings-stage8-state-the-decision.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
'@objectstack/service-analytics': patch
---

The read-scope comparand refusals, the native-SQL cross-field backstop and the two display-SQL echo refusals no longer cite tracker numbers; each one states the decision behind it in words

Clause-②: no

Some strings the analytics service shows to operators and callers pointed at an issue-tracker number for the reason behind them. The number goes; where the sentence did not already say what was decided, it now does.

- The read-scope compiler's undefined-comparand refusal says an undefined comparand is refused rather than read as null, on the SQL drivers and on this door alike. Its refusal of a non-boolean `$null`, `$exists` or `$empty` comparand says a non-boolean comparand for any of the three is refused rather than coerced, on every driver and on this door alike. Both still say they fail closed, and that the producer to fix is whoever built the read scope, never the caller of the query.
- The native-SQL strategy's cross-field backstop and the `/analytics/sql` echo's refusal of a field-reference comparison say the engine path's driver enforces the cross-field rules (declared same-table columns only, never the tenant-isolation column, one comparison class) with metadata it owns, so those rules are enforced in one place, next to the metadata they read.
- That echo refusal and the echo's unmapped-operator refusal say the echo renders every predicate the query runs with, or refuses.

Text only: no status, error code, field, route or control flow moves. A client or log filter that matches the old text (for example a tracker-number suffix) needs the new spelling.
8 changes: 5 additions & 3 deletions packages/services/service-analytics/src/read-scope-sql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1682,8 +1682,9 @@ function undefinedComparandError(field: string, path: string): Error {
`predicate was meant ({ "${field}": null } or { "${field}": { "$null": true } }), or omit the key ` +
`when the value is genuinely absent. The producer to fix is whoever BUILT this read scope — an ` +
`admin-authored sharing rule / permission set, its CEL lowering, or the in-process code that ` +
`assembled the FilterCondition — never the caller of this query, who cannot author it (#6050 ` +
`ruling B, pushed down to this compiler by #6125).`,
`assembled the FilterCondition — never the caller of this query, who cannot author it. An ` +
`undefined comparand is refused rather than read as null, on the SQL drivers and on this door ` +
`alike.`,
);
}

Expand Down Expand Up @@ -1870,7 +1871,8 @@ function nonBooleanFlagComparandError(op: string, field: string, path: string):
`not a string, a number, null or undefined. The producer to fix is whoever BUILT this read ` +
`scope — an admin-authored sharing rule / permission set, its CEL lowering, or the in-process ` +
`code (a getReadScope option) that assembled the FilterCondition — never the caller of this ` +
`query, who cannot author it (#5347 / #5369, pushed down to this compiler by #6387).`,
`query, who cannot author it. A non-boolean comparand for any of the three is refused rather ` +
`than coerced, on every driver and on this door alike.`,
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1034,9 +1034,11 @@ export class NativeSQLStrategy implements AnalyticsStrategy {
`[native-sql-strategy] ${hit.source} carries a field reference ` +
`{ "$field": "${hit.ref}" } under "${hit.op}" on "${hit.field}", which this strategy does not ` +
`compile into a column-to-column comparison — it would BIND the reference object as the ` +
`comparison's value and answer a wrong row set silently (#7598). \`canHandle\` declines such a ` +
`comparison's value and answer a wrong row set silently. \`canHandle\` declines such a ` +
`query so it routes to the ObjectQL/engine path, whose driver compiles it and enforces the ` +
`#5222 rulings with metadata it owns; reaching this throw means the decline and this emitter ` +
`cross-field rules (declared same-table columns only, never the tenant-isolation column, one ` +
`comparison class) with metadata it owns, so those rules are enforced in one place, next to ` +
`the metadata they read; reaching this throw means the decline and this emitter ` +
`stopped agreeing, which is our bug and must never degrade to a silent answer.`,
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -462,11 +462,14 @@ export class ObjectQLStrategy implements AnalyticsStrategy {
`{ "$field": "${crossField.ref}" } under "${crossField.op}" on "${crossField.field}". ` +
`The query itself is SERVED — \`NativeSQLStrategy.canHandle\` declines a cross-field ` +
`comparison so it routes to the ObjectQL engine path, where driver-sql compiles it into a ` +
`column-to-column predicate written TOTAL across NULLs and enforces the #5222 rulings ` +
`(#7598, maintainer ruling 2026-08-12). This renderer has no faithful rendering of that ` +
`column-to-column predicate written TOTAL across NULLs and enforces the cross-field rules ` +
`(declared same-table columns only, never the tenant-isolation column, one comparison class) ` +
`with metadata it owns, so those rules are enforced in one place, next to the metadata they ` +
`read. This renderer has no faithful rendering of that ` +
`predicate: what it can emit is a comparison against the reference object as a bound VALUE, ` +
`which reproduces none of the rows the query returns. Refusing rather than half-rendering — ` +
`an echo that contradicts execution is worse than no echo (#3601 / #3602 / #3650). Run the ` +
`an echo that contradicts execution is worse than no echo, so the echo renders every ` +
`predicate the query runs with, or refuses. Run the ` +
`query itself (/analytics/query) to get its rows.`,
);
}
Expand Down Expand Up @@ -1478,7 +1481,7 @@ export class ObjectQLStrategy implements AnalyticsStrategy {
`filter-normalizer.ts refuses anything it cannot map — so this means a new ` +
`operator reached the normalizer without an arm here. Add one rather than ` +
`dropping the predicate: an echo without it describes a WIDER query than the ` +
`one that ran (#5333).`,
`one that ran, and the echo renders every predicate the query runs with, or refuses.`,
);
}
params.push(values[0]);
Expand Down
Loading
Loading