Repository navigation
fix(service-automation): the run row's highlight set carries the first filter's qualifier, unmeasured_count - #22632
Conversation
…t filter's qualifier, unmeasured_count The broken-sweep first filter is `selected_count > 0 AND acted_count = 0 AND unmeasured_count = 0`, and the object's own comment says it has to be visible on the run row. Only two of its three operands were in `highlightFields`. On a served stack a notify step's delivery is enqueued, so a notify sweep that did its job reads `acted 0, unmeasured 1`, and the run row showed it as `selected 1, acted 0`. - highlightFields gains `unmeasured_count` right after `acted_count` - the comment above it names the three operands - `unmeasured_count`'s description names a notification queued for delivery - one pin holds the highlight set to the three operands Claude-Session: https://claude.ai/code/session_013j5gkUCpqQiti4GgPqqmnt Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): 19 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 9 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 6 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 56ee4e5a51efb94df06ce030d26577bc3f7c3df9 && git checkout 56ee4e5a51efb94df06ce030d26577bc3f7c3df9
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin e8c666687030083c0ffff9c172bc273b9ada6f7b 29cfa41a1bfaa3e909bb3360085c8e8dc39e906b && git checkout -B drift-repro e8c666687030083c0ffff9c172bc273b9ada6f7b && git merge --no-ff 29cfa41a1bfaa3e909bb3360085c8e8dc39e906b
node scripts/docs-audit/affected-docs.mjs --json e8c666687030083c0ffff9c172bc273b9ada6f7b
|
Part of #22590
Clause-②: no
Item 2 of #22590 (a flow run's summary reports
acted: 0while the run sent notifications), under the amended claim (seat review6094568255, claim amendment6094572037). Item 3, the activity pre-scan, is not addressed here, and #22590 remains open.What changes
packages/services/service-automation/src/sys-automation-run.object.ts, metadata only:highlightFieldsgainsunmeasured_count, right afteracted_count. The run row now shows all three operands of the broken-sweep first filter:selected_count > 0 AND acted_count = 0 AND unmeasured_count = 0.highlightFieldsnames those three operands, and says why the qualifier has to be visible.unmeasured_count's description lists a notification queued for delivery among the uncountable effects, beside theconnector_actionand the mutatinghttpcall it already named. The rest of the sentence is unchanged.Plus one pin,
src/sys-automation-run-first-filter-highlight.test.ts, and apatchchangeset. No column is added (ADR-0103 engine-owned object). No counter moves, and nothing innotify-node.ts,service-messagingorpackages/specchanges.The premise measurement: no producer under-reports
The dispatched premise was this: a notify-only run triggered at
POST /api/v1/automation/FLOW/triggerreturnsacted: 0ANDunmeasured: 0while the inbox rows are already written. It is false.How it was measured:
bootStackstack (@objectstack/verify), sqlite-wasm, with an org-bound admin session.origin/mainwas514bf3c101.start → notify (recipients: [admin], channels: [inbox]) → end, run once asautolaunchedand once asscreen. Both answered identically.summary(selected / acted / unmeasured)sys_automation_runrowemit()answer (delivered / enqueued / failed)metricssys_inbox_messageat responserequires: ['messaging'], soMessagingServicePlugindefaults, reliable delivery on, asservecomposes itselected 1, unmeasuredEffect truereliableDelivery: falseselected 1, acted 1role:no_such_role_22590resolves to no userselected 1, acted 0selected 1, acted 0On a served stack, the in-app write really is asynchronous: the outbox dispatcher writes the inbox row about 50 ms after the response. The contract's answer for that is
unmeasuredEffect("an effect I cannot count"), never a fabricatedacted, and it already reaches both the door's summary and the run row. The delivering run (1 / 0 / 1) sits outside the first filter. The nothing-to-do control (1 / 0 / 0) sits inside it, as designed.summarizeRunfoldsstep.metricsfrom every step whatever itsnodeType, so the count already reads from each node's outcome; there is no list of record-writing kinds.Release parity.
@objectstack/service-automation@17.7.0tag is an ancestor of HEAD (merge-base --is-ancestor, exit 0).diff, exit 0):MessagingService.emit's outbox and inline tail.run-summary.tsandmessaging-service-plugin.tshave no diffstat since the tag.reliableDelivery: trueis the default at the tag.summaryincluded, at@objectstack/runtime@17.7.0.acted 0beside anunmeasured 1it did not read.Why the fix is the highlight set, not a counter
At the API, the delivering run is already distinguishable. The misread survives on the Runs surface.
highlightFieldscarriedselected_countandacted_countbut not their qualifier, although the object's own comment says the first filter "has to be visible on the run row itself … a signal you must click to find is a signal nobody sees". So a served stack's delivering notify sweep showed asselected 1, acted 0on the run row, which is the card's misread made on another surface.Counting the enqueue as
actedwould bring back the #7747 defect: a summary asserting a delivery thatsys_notification_deliverycould still markdead. Folding it into anything other thanunmeasuredwould break the contract inexecution.zod.ts. The fix that matches the declared design is to show the qualifier the design already has.Node-kind census (no edits)
These are the effect-bearing node kinds and the counter each reports on
main:create_record,update_record,delete_record:actedas a row count.get_record:selected.http:acted 0for a read,unmeasuredEffectfor a mutating call.connector_action:acted 1for an accepted declared write,acted 0for a declared read,unmeasuredEffectfor an undeclared action or a failed write dispatch.script:unmeasuredEffectfor a function declaredwrites, nothing for a pure one.notify:actedequal todelivered;unmeasuredEffectwhile a delivery is enqueued;acted 0plusselectedfor a zero dispatch.subflow,map: the child run rolls up.The kinds without an effect report nothing, which is correct:
decision,assignment,loop,parallel,try_catch,wait,screen.The one effect-bearing kind with no counter is plugin-approvals'
approval/approval_revisenode. See Acceptance notes.The pin and its ablation
sys-automation-run-first-filter-highlight.test.ts:numbercolumn, a positive control so that the containment check cannot pass on a typo;highlightFieldscontains all three operands.Ablation, run on the committed tree with
node scripts/ablation-replace.mjsin WRAP mode under the verify lock, which proves the write landed on disk and restores it under a trap.--replacement "'acted_count', ". The tool refused it before any test ran. The replacement is a substring of the anchor, so its count could not rise, and the tool reads that as a mutation that did not land. The file was restored to the HEAD blob. Recorded as a no-op.--anchor " 'unmeasured_count'," --delete:5a0f59cde967→ec9f7ec995f1;Tests 1 failed | 1 passed (2),first-filter operands missing from highlightFields: unmeasured_count: expected [ 'unmeasured_count' ] to deeply equal [];5a0f59cde967, equal to HEAD,git diff HEADis empty, andgit status --porcelainis empty.dist/sits between the mutation and the run.Verification
Every run below is at HEAD
29cfa41a1b(the branch merged withorigin/main83b8b80728). Heavy runs went throughscripts/pm/os-verify-lock.sh(slotissue-22590, 3 GB heap, turbo--concurrency=1, vitest--maxWorkers=2).turbo run build --filter='@objectstack/service-automation...'gaveVERDICT command-exit 0,Tasks 30 successful, 30 total.packages/specmoved onmain's side of the merge, sopnpm --filter @objectstack/spec check:generatedalso ran:All 15 generated artifacts are up to date.service-automationtest:vitest run --maxWorkers=2gaveTest Files 182 passed (182),Tests 2287 passed (2287). The new pin is included.service-automationtypecheck:tsc --noEmit && pnpm check:test-typecheckgaveVERDICT command-exit 0andcheck:test-typecheck: OK. The new test file is in tsc's program (tsc --listFilescount 1).node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderived 63 commands from the real diff (3 paths). All were run, each exit captured before any pipe. The reconcile with--ran, given exit codes, answered63 derived, 62 run, 1 NOT-MEASURED, 0 UNRUN.check:dual-build-cjs-loads, reason:PREREQUISITE NOT MET, exit 3. It reads the built output of every publishable package, and 37 have nodist/in this worktree, which built onlyservice-automation's closure. This is a declared narrowing: CI'sLint & Repo Gatesruns it on a full build. The diff changes a string array and a description string in one source file, and does not touch any package's emit format or exports.pnpm check:i18ngaveOK (9 package(s) — all bundles in sync), andpnpm check:i18n-coveragegaveOK (13 config(s) … none new). No translation bundle carriessys_automation_run, so nothing was regenerated.packages/cliandobjectqlare not touched; no integration layer is owed.Acceptance notes
6094568255).approval/approval_revise(packages/plugins/plugin-approvals/src/approval-node.ts,approval-revise-node.ts) open an approval request and suspend with nometrics. No door gives a wrong summary, and an approval-only run readsselected 0, outside the filter regardless. This was a source reading only, and it is not filed.selected,acted,unmeasured), neveractedalone. On a served stack, a notify-only sweep that delivered readsacted 0withunmeasured 1.failed_countstays absent (the [finding] service-automation:FlowRunSummary.failedhas no queryable column onsys_automation_run, so "which runs lost rows?" is asummary_jsonscan #15606 verdict). The sibling pinsys-automation-run-failed-count-verdict.test.tsstill holdsfailed_countout ofhighlightFields, and still requiresacted_countin it.Generated by Claude Code