Repository navigation
fix(service): the SLA Violations tile counts open breaches at read time - #2090
Merged
objectstack-fleet[bot] merged 5 commits intoOct 13, 2026
Merged
Conversation
…once All fourteen widgets on service_dashboard bind the same dataset, `case_metrics`, and each spelled it out. Declare it once and pass it by shorthand (`dataset,`), the shape case.view.ts already uses for its one data provider. The registered ServiceDashboard value is unchanged (JSON-identical to the base's), and src/service's authored total drops from ~21,911 to ~21,863 tokens (-192 chars). Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BCDqr1DqnQjEh5jA9gGkUG
The tile counted only `is_sla_violated`, which nothing sets on an open case unless the scheduled case_sla_monitor sweep runs, and scheduled work is off by default. It now counts a flagged case OR a case still open (status not resolved/closed) whose sla_due_date is strictly before the current instant: the selection the sweep itself makes. It counts; it does not flag, escalate or alert, which still need the sweep. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BCDqr1DqnQjEh5jA9gGkUG
case-sla-breach-on-close.test.ts: the tile's filter must be the flag OR
case_sla_monitor's query_breached selection (operator and status set
read off the sweep; only the instant's spelling differs, `{now}` in a
query filter for the flow template's `{NOW()}`). A new section counts
seven cases through the dataset query path with no sweep run (4 counted,
the flag alone would give 2), then runs the sweep and checks it flags
exactly the cases the tile already counted, with the count unchanged.
live-work-predicate-parity.test.ts: the tile joins the by-name roster of
"no longer live work" consumers. dashboard-agent-global-filter.test.ts:
the ground-truth model learns the tile's `$or`, and five fixture rows
carry a due date so the partition check exercises the open-case branch.
Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BCDqr1DqnQjEh5jA9gGkUG
The SLA & Escalation and Cases pages (three locales each) described the tile as counting `is_sla_violated: true`, and Cases said every breach lands in Escalated Cases. The tile now also counts open cases past their due date at read time, and only the breaches the sweep flags are escalated. Changeset for the tile change. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BCDqr1DqnQjEh5jA9gGkUG
… column dashboard-date-range-window.test.ts runs every service widget over a hand-shaped crm_case with no sla_due_date column, and the driver refuses a filter on a column the table lacks. The fixture gains the column and four rows with a deadline (two open and past due, one open and not yet due, one closed and past due), and the ground-truth model learns the tile's `$or`. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BCDqr1DqnQjEh5jA9gGkUG
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
This was referenced Oct 13, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #2070
Clause-②: no
Acceptance 3, ruled C (ruling comment 6142062705). Acceptance 1 and 2 landed in PR #2080.
What changes
The SLA Violations tile on the service dashboard (
sla_violations) now counts at read time. A case counts when it is flaggedis_sla_violated, or when it is still open (status notresolved/closed) and itssla_due_dateis strictly before the current instant. That second branch is exactly the selectioncase_sla_monitorsweeps, so the tile reads true on a deployment that runs no scheduled work (the platform default), where nothing flags an open breach. It counts; it does not act. Escalation and the owner's alert still need the sweep.{now}is the query-filter spelling of the instant the flow template spells{NOW()}. The flow spelling is refused in a query filter: measured400 FILTER_TOKEN_UNKNOWNonPOST /api/v1/analytics/dataset/query. The sweep is unchanged.Token cost, paid first by an equal shrink in
src/service(ceiling unchanged)service_dashboardbindcase_metrics. The dataset is now declared once (const dataset = 'case_metrics') and passed by shorthand, the same shapecase.view.tsuses for its data provider (refactor(service): shrink src/service back under its authored-total token ceiling, behaviour unchanged (#2082) #2085). The registeredServiceDashboardvalue is JSON-identical before and after (cmpexit 0). Saves 192 chars.node scripts/check-source-token-ratchet.mjs,src/serviceauthored total: ~21,911 → ~21,897 tokens (87,645 → 87,588 chars). The ceiling stays at ~22,000, with headroom ~89 → ~103. The interaction layer goes from ~5,616 to ~5,602.One rule, pinned through the existing parity test
test/case-sla-breach-on-close.test.tsalready pinned the sweep's strict$lt. Its section 4 now pins the three readers of one boundary:{ $lt: '{NOW()}' }(kept as before);$orof the flag and the sweep's ownquery_breachedselection, withis_sla_violated: falsefolded away. The status set and the comparison operator are read off the sweep, and only the instant token is mapped. A$lteon either side, a different status set, or a third clause on the sweep turns it red.The new section 5 drives the real engine with no sweep run. It writes seven cases: open and late, parked on the customer and late, flagged and late, flagged with its deadline moved out, open and not yet due, resolved on time with the deadline passing since, and one with no deadline. Through the booted analytics service the tile counts 4, where the flag alone gives 2. It then runs
case_sla_monitorand checks that the sweep flags exactly the cases the tile already counted and that the tile's number does not move. This is the behavioural proof that the two spellings select one set of rows.test/live-work-predicate-parity.test.ts: the tile joins the by-name roster of "no longer live work" consumers. That roster's own rule is that every consumer is pinned there by name.File surface: two test files beyond the claim, both forced
test/dashboard-agent-global-filter.test.tsruns every count widget against a hand-written ground-truth model that throws on any shape it does not understand, and$orwas one. The model now understands the tile's$or($ninon status,$lt '{now}'on the due date). Five fixture rows carry a due date, so the Agent-partition check exercises the open-case branch.test/dashboard-date-range-window.test.tsruns every service widget over a hand-shapedcrm_casethat had nosla_due_datecolumn, and the driver refuses a filter on a missing column. The fixture gains the column and four rows with a deadline, and its ground-truth model learns the same$or.Docs
Only sentences that became false were changed.
content/docs/service/sla-and-escalationandcontent/docs/service/cases(three locales each) described the tile as countingis_sla_violated: true. Cases also said every breach lands in Escalated Cases, which now holds only for the breaches the sweep flags. The changeset is.changeset/2070-sla-tile-read-time.md.Measured at the public door: fresh DB,
objectstack dev, sweep not boundobjectstack dev -p 4172(thepnpm devcommand on another port, to stay clear of sibling servers in this container) logged thatcase_sla_monitoris NOT bound, because scheduled work is off by default. On the fresh demo DB there were 38 cases: 13 flagged, 20 open and past due (16 of them unflagged), and a ground truth of 29 by union.{ is_sla_violated: true }/_console/apps/app.objectstack.hotcrm/dashboard/service_dashboard)The console's own request for the tile was
runtimeFilter: { $and: [ { $or: [ flag, { status $nin, sla_due_date: { $lt: ISO instant } } ] }, { created_at: 90-day window } ] }. The console resolves{now}itself before sending, and the response wascase_count: 29. A REST caller sending{now}is resolved by the server, and the two agree. The hosted runtime is NOT MEASURED, because none is reachable from this container.Ablations (on committed state, through
ablation-replace.mjs; restore proven by blob == HEAD and an emptygit diff HEAD){ is_sla_violated: true }: 4 red. The section 4 pin goes red, the section 5 count reads 2 where 4 is expected, and both roster tests go red. The post-sweep test stays green, because after a sweep the flag alone does count them; the pre-sweep count is the one that discriminates.['closed']: 4 red. The section 4 pin goes red, the count goes 5 against 4 (the resolved-on-time row) before and after the sweep, and the roster goes red.$ltchanged to$lte: 2 red. The section 4 pin and the global-filter truth model both refuse the shape. No row can sit exactly at the instant, so only the structural pin can see this one.Gates on
7647070e, each through the shared verify lockpnpm validate && pnpm typecheck && pnpm lint && pnpm lint:i18n-gate && pnpm hygiene && pnpm hygiene:tokens && pnpm build:VERDICT command-exit 0. The run printedValidation passed,18 suggestion(s)(pre-existing),i18n lint gate: 0,source hygiene clean,source token ratchet cleanandBuild complete.tsc --listFileslists all four touched test files.pnpm testran as two file halves whose union is all 179 test files (diffed equal), split to fit the foreground cap:Test Files 90 passed (90),Tests 1640 passed | 1 skipped,VERDICT command-exit 0;Test Files 89 passed (89),Tests 2054 passed,VERDICT command-exit 0.Acceptance notes (not filed)
@objectstack/*past 17.7.0 once the next release publishes — it carries the platform fixes #2016, #2000, #1583 and #1585 wait on #2031, carrier: chore(deps): upgrade@objectstack/*past 17.7.0 once the next release publishes — it carries the platform fixes #2016, #2000, #1583 and #1585 wait on #2031's dev). On objectstackmain(spec18.0.0-next.0), a dashboard widget's{now}is not refused. The widgetfilterisanalyticsCarrierFilter(), which isFilterConditionSchemaplus the nested-relation walk. The 18 template-dialect refusal covers flow node value slots only. The sweep's{NOW()}inquery_breachedis a flow{…}token at aget_recordfilter operand, which that refusal does cover, and it is not a whole-string query placeholder. So the upgrade has to re-spell it. The section 4 pin will go red on that change by design, so the sweep and the tile move together.src/serviceinteraction layer prints the advisory to re-anchor to ~6,000. Per the ruling, no ceiling moves here.Generated by Claude Code