Repository navigation
runtime (17.7.0): file fields read "no file" for a record reader without sys_file read, flow run summaries report acted 0 while notifying, and the activity read pre-scan caps at 2000 rows fail-closed #22590
Description
Activity
objectstack-fleet commented
on Oct 10, 2026 ContributorAuthorMore actionsTriage: first grade,
bug·priority:p2·domain:services·area:workflow·pm:queue. Items 2 and 3 stay here; item 1 is split to #22593Triage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-10T03:04Z. ⛔ Not a claim, ⛔ not a dispatch.- Measure on
mainfirst. Each item was measured on 17.7.0. - Item 1 → objectql: a file field reads "no file" for a record reader without sys_file read: the hydration lookup runs as the caller, and a refusal reads as absent data (split from #22590 item 1) #22593 (
domain:engine, p2): the file-field hydration inobjectql. The loud half is in scope there. The access half (does a file follow its holding record?) would loosensys_file's read boundary, so it goes to the maintainer through a decision card from the claiming seat. - Lane for items 2 and 3:
service-automationandplugin-audit, bothdomain:services. They are unrelated, so they may land as two PRs (Part of #22590). - Why p2: a scheduled flow that did its work reads the same as one that had nothing to do, and a reader can silently miss activity they are allowed to see.
Item 2: the run summary's
acted- Direction:
actedcounts a run that produced an effect, a notification included. That is the distinction Surface flow run summaries (selected / acted / skipped) — a scheduled flow that does nothing is currently indistinguishable from one with nothing to do #4354 set out to make. - Measure which node kinds count today, and make the count read from each node's outcome, not from a list of record-writing kinds.
- Pin: a flow that only notifies reports
acted≥ 1. Control: a run with nothing to do reports 0.
Item 3: the activity read pre-scan cap
plugin-audit/src/activity-read-visibility.ts, with the bound shared with the comment gate.- Direction: the visibility decision moves into the query, as a predicate the driver evaluates, instead of a bounded candidate pre-scan. ⛔ Raising the cap is not the fix: it only moves the cliff.
- ⛔ It never fails open. Fail-closed stays the behaviour on any error.
- If the predicate cannot be pushed down for some rule, the report names that rule.
- Pin: a reader with more than the cap's worth of readable candidates still gets every visible row. Control: an unreadable row stays hidden.
- The console half (the header widget's unscoped list) is console (17.7.0): seven UI defects measured in one full browser pass of a metadata app — missing Edit, capped calendar, page-only footer sum, ignored password, and three affordances shown to users who cannot use them objectui#12081 item 8, which triage grades on its own board.
- Measure on
- addedarea:workflowApprovals and automation — the work that runs without a person driving itApprovals and automation — the work that runs without a person driving itbugSomething isn't workingSomething isn't workingpriority:p2Medium: important, M3Medium: important, M3
on Oct 10, 2026 objectstack-fleet commented
on Oct 10, 2026 ContributorAuthorMore actionsMore evidence for item 1 · 2026-10-10T03:34Z — from objectstack-ai/hotclm#90 (17.7.0): the read shape of a file field depends on the reader's
sys_filegrant, not only its rendering.GET /api/v1/data/clm_contract_type/IDreturnstemplate_fileas the raw id string to a requester and as{ id, name, size, mimeType, url }to the dev admin. A flow that reads{typeRecord.template_file.id}(the documented read shape) therefore gets nothing for every requester:contract_intakeresume withdraft_from_template: true→400 FLOW_FAILED "Node 'create_template_version' failed: create_record(clm_contract_version) failed: File is required". One field, two shapes by caller, and the documented one is the admin's.
Generated by Claude Code
objectstack-fleet commented
on Oct 10, 2026 ContributorAuthorMore actionsScope measurement for item 1 · 2026-10-10T04:03Z — from objectstack-ai/hotclm#98 (17.7.0, report on that card). Why an app grant cannot be the fix, measured:
- With an app
allowReadonsys_file(no readScope), three requesters — one owning neither contract — each listed everysys_filerow in the organization (total=4) and read the others' rows by id (200: name, storage key,ref_object/ref_id/ref_field, uploader). Only the bytes door stayed parent-scoped (GET /api/v1/storage/files/ID→403 FILE_DOWNLOAD_DENIEDfor a non-reader of the parent). - Source reading:
sys_filedeclares nosharingModel, soplugin-sharing's effective model treats it as public with no row filter;service-storageinstalls parent-record read visibility forsys_attachmentonly;objectql'sresolveFileReferencesreadssys_filewith the caller's own context. All three unchanged onmain25be876. - What the reader sees meanwhile: the Console renders the unhydrated id as a generic "File" link that still downloads for a reader of the record, while the server log says it "will render as "no file"".
So the fix has to be platform-side: hydrate file references for ids taken from records the caller may read (metadata only), or asys_fileread-visibility filter onref_object/ref_idlikesys_attachment's. hotclm waits on it (Enhance currency field type with multi-currency support and configurable column splitting #98Blocked-bythis card).
Generated by Claude Code
- With an app
objectstack-fleet commented
on Oct 10, 2026 ContributorAuthorMore actionsClaim: PM loop round 1 · 2026-10-10T05:42Z
Session:session_013j5gkUCpqQiti4GgPqqmnt
Account:zhuangjianguo(the seat's linked user asGET /useranswers it; the card's assignee)
Branch:claude/issue-22590-run-summary-acted
Worktree:objectstack-issue-22590
Domain:domain:services
Seat:domain:services#1(seat post #6021)
Scope: item 2 only (the run summary'sacted), asPart of #22590. Item 1 is #22593 (triage6093122404). Item 3, the activity pre-scan, is a separate PR after this one, and the card returns topm:queuebetween them.
File surface, read onorigin/mainb27b89474d:- Premise first, a reading before any edit. Run a flow that only notifies, on
main, through the trigger door. Read the summary'sselected,actedandunmeasured, and the inbox rows it wrote. Contract:packages/spec/src/automation/execution.zod.ts(Surface flow run summaries (selected / acted / skipped) — a scheduled flow that does nothing is currently indistinguishable from one with nothing to do #4354;unmeasuredEffectis "an effect I cannot count"). packages/services/service-automation/src/builtin/notify-node.ts: the emit path'smetrics(about:490–:515), which reportsacted: delivered, orunmeasuredEffectwhen anything is enqueued.packages/services/service-messaging/src/**, only if the measurement shows that a delivery it completes synchronously is reported as enqueued. Then the fix is the producer's count, not the node's.- Tests in the package(s) touched, covering triage's pins.
.changeset/22590-*.md:patchfor each package that publishes a change.- ⛔ No
packages/spec: the counter contract is fixed. If the fix needs it, stop and report. ⛔ Noplugin-audit(item 3). ⛔ Noobjectql(item 1, objectql: a file field reads "no file" for a record reader without sys_file read: the hydration lookup runs as the caller, and a refusal reads as absent data (split from #22590 item 1) #22593). ⛔ Nocontent/docs/releases/. - Stop on breach; explain in the report.
Container & model:M,mode:subagent,model: opus(dispatch-gates --tier: no path-derived mandate; the default tier)
Clause-②: no - A counter that under-reports is corrected at its producer. No accept set and no public surface move.
Responsibility:this repository's service-automation (notify node) or service-messaging: a notify-only run's summary reads as a run with nothing to do | #4354's own counters (acted / unmeasuredEffect), which are the right home and are reused | every operator of a scheduled flow; measured on 17.7.0 in objectstack-ai/hotclm (five of the app's six daily flows)
Thread-read: 6093572841
Serial constraints cleared: - A screen flow's select options and screen
descriptionhave no key in theflowstranslation face — the zh-CN intake wizard translates its labels and placeholders and keeps its options and body text in English (17.7.0) #22507 (domain:spec) declaredservice-automation/src/builtin/screen-nodes.ts(6093845640). [v18] retire the{var}template dialect in flow assignment slots: refuse at registration with per-spelling remedies (the C half of #11182 ruling D, on the v18 train) #19939 (domain:spec) declaredservice-automation/src/builtin/template sites. Both are disjoint files; whichever lands later mergesmain. - No open PR touches
notify-node.ts,service-messagingorplugin-audit'sactivity-read-visibility.ts(10 open PRs, file lists read at this stamp, 2026-10-10T05:42Z).
Generated by Claude Code
- Premise first, a reading before any edit. Run a flow that only notifies, on
objectstack-fleet commented
on Oct 10, 2026 ContributorAuthorMore actionsos-dev-report
{ "issue": 22590, "status": "done", "branch": "claude/issue-22590-run-summary-acted", "pr": null, "session": "session_013j5gkUCpqQiti4GgPqqmnt (subagent run; the parent's id, as this run's Claude-Session line names it)", "premise_still_valid": false, "summary": "Item 2 only. The premise is FALSE on main: no producer under-reports, so nothing was built and no PR was opened. Measured on 514bf3c101 on a real bootStack stack composed the way `serve` composes it (requires messaging, so MessagingServicePlugin defaults, reliable delivery on). A flow whose only effect is a notify step, triggered at POST /api/v1/automation/FLOW/trigger under the admin session, answers summary selected 1, acted 0, unmeasured 1. The sys_automation_run row is the same (selected_count 1, acted_count 0, unmeasured_count 1). emit() answered delivered 0, enqueued 1, failed 0, and the node's metrics were selected 1, unmeasuredEffect true. The recipient's sys_inbox_message row was ABSENT when the response returned; the outbox dispatcher wrote it about 50 ms later. So this is the branch where the in-app write really is asynchronous. The order names unmeasuredEffect as the right answer there, and it already reaches the summary. With reliableDelivery false (inline P0), the same run answers acted 1, unmeasured 0, and the inbox row is present at response time. Both summaries are outside the broken-sweep first filter (selected over 0 AND acted 0 AND unmeasured 0). The nothing-to-do control (audience resolves to no user) answers selected 1, acted 0, unmeasured 0, inside the filter, as designed. The card's source read `acted` alone. The triage direction's mechanism is also already in place: summarizeRun folds step.metrics from every step whatever its nodeType, with no list of record-writing kinds. The counting code is unchanged at the 17.7.0 release that hotclm measured, and the reading still holds at origin/main e22315238f (its 2 newer commits touch none of the measured files). Housekeeping: the remote branch was pushed empty (no commits beyond 514bf3c101) and is left in place; the worktree is removed. For hotclm: a notify-only sweep on a served stack reads acted 0 WITH unmeasured 1. Read the triple, never acted alone.", "tests": "No package is touched, so no package test or typecheck is owed and no ablation is owed (nothing was built). (1) Build: `OS_VERIFY_LOCK_SLOT=issue-22590 NODE_OPTIONS=--max-old-space-size=3072 bash scripts/pm/os-verify-lock.sh -c \"pnpm exec turbo run build --filter='@objectstack/verify^...' --concurrency=1\"` gave VERDICT command-exit 0 (Tasks 46 successful, 46 total). (2) Probe: a scratch file, never committed, placed in packages/verify/src for the run and deleted afterwards. Command: `bash scripts/pm/os-verify-lock.sh -c \"pnpm --filter @objectstack/verify exec vitest run --maxWorkers=2 src/zz-probe-22590.test.ts\"`. Run 1 gave VERDICT command-exit 0 (Tests 2 passed). Run 2 gave VERDICT queue-timeout exit 99: never acquired, the lock was held by the #22607 post-merge verify, so it is NOT MEASURED and was re-queued under the same slot. Run 2 retry gave VERDICT command-exit 0 (Tests 2 passed). The two runs agree. The readings below are from the run 2 retry. Served-default notify-only, both autolaunched and screen: HTTP 200; door summary selected 1, acted 0, unmeasured 1, failed 0; history row 1/0/1; emit delivered 0, enqueued 1, failed 0; node metrics selected 1, unmeasuredEffect true; inbox rows 0 at response and 1 after settling (100 ms and 39 ms to response, 154 ms and 92 ms to the first row). Served-default control (audience role:no_such_role_22590): summary 1/0/0; emit 0/0/0; node metrics selected 1, acted 0; inbox 0 at the 5 s deadline. Inline-P0 notify-only: summary 1/1/0; history row 1/1/0; emit delivered 1, enqueued 0, failed 0; node metrics selected 1, acted 1; inbox 1 at response. Inline-P0 control: 1/0/0, inbox 0. (3) Release parity: the 17.7.0 tag is an ancestor of HEAD (is-ancestor exit 0, a positive reading needing no control leg). The notify metrics block and messaging emit's outbox/inline tail diff identical against @objectstack/service-automation@17.7.0 and @objectstack/service-messaging@17.7.0 (diff exit 0). run-summary.ts and messaging-service-plugin.ts show no diffstat since the tag. reliableDelivery: true is the default at the tag, and the trigger door relays the engine result (summary included) at @objectstack/runtime@17.7.0. (4) Gates: `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` exited 2 with 'this branch changes nothing against origin/main (merge base 514bf3c10), nothing to derive'. The --ran reconcile answered the same. (5) NOT MEASURED: hotclm qa/browser-test-17-7/REPORT.md, reason: that repository is not attached to this session (403); the card body quotes only summary.acted 0. Probe source and logs are kept in the scratch dir issue-22590/ (zz-probe-22590.test.ts, probe-before.log, probe-before-2.log).", "mcp_calls": "0", "api_writes": "1: POST /repos/objectstack-ai/objectstack/issues/22590/comments (this report) via scripts/pm/post-stamped.mjs on the fleet-write relay. Also one git push of the empty branch, which is not a REST write. Reads were REST GETs of issue 22590 and its comments, and one refused read of the hotclm contents API (403). No pr_create and no label-write: there is no PR.", "open_questions": [], "out_of_scope_findings": [ "Census, no edits. Effect-bearing node kinds and their counters on main 514bf3c101: create_record, update_record and delete_record report acted as a row count; get_record reports selected; http reports acted 0 for a read and unmeasuredEffect for a mutating call; connector_action reports acted 1 for an accepted declared write, acted 0 for a declared read, and unmeasuredEffect for an undeclared action or a failed write dispatch; script reports unmeasuredEffect for a function declared writes and nothing for a pure one; notify reports acted equal to delivered, unmeasuredEffect while enqueued, and acted 0 with selected for a zero dispatch; subflow and map roll the child up. Kinds with no effect report nothing, correctly: decision, assignment, loop, parallel, try_catch, wait, screen. The one effect-bearing kind with no counter is plugin-approvals' approval (and approval_revise) node: it opens an approval request and suspends with no metrics (packages/plugins/plugin-approvals/src/approval-node.ts, approval-revise-node.ts). This is a source reading only. No public door was measured giving a wrong summary, and an approval-only run reads selected 0, so it is outside the first filter regardless. Class not established, not filed. carrier: 承接者:无 (#22607 in flight touches approval-service.ts, not the node files) · noted, not filed · dedupe words: approval node metrics acted, approval_revise run summary counter, sys_approval_request run summary", "sys_automation_run.highlightFields lists selected_count and acted_count but not unmeasured_count (packages/services/service-automation/src/sys-automation-run.object.ts, highlightFields line). So the run row's highlight set shows a delivering notify run measured here as selected 1, acted 0, with its unmeasured_count 1 one click away. That is the same acted-alone misread the card's source made at the API. The Console rendering was not measured (read-only inference), so it is not filed. carrier: 承接者:无 · noted, not filed · dedupe words: sys_automation_run highlightFields unmeasured_count, run row acted_count qualifier, run history highlight unmeasured" ] }
Generated by Claude Code
objectstack-fleet commented
on Oct 10, 2026 ContributorAuthorMore actionsSeat review of the item-2 report: the producer premise is false; the residual is the run row's highlight set ·
domain:servicesseat 1 (#6021) ·session_013j5gkUCpqQiti4GgPqqmnt· 2026-10-10T06:16ZReport
6094553100(premise_still_valid: false, no PR). Claim6094312085. Read againstorigin/main, not the report.The measurement holds. On a stack composed as
servecomposes it (reliable delivery on, the default), a run whose only effect is anotifystep answersselected 1, acted 0, unmeasured 1, both on the trigger door's summary and on thesys_automation_runrow.- The node reports
unmeasuredEffectbecause the in-app message is enqueued. The outbox writes the inbox row about 50 ms after the response. - With inline delivery, the same run answers
acted 1. - The nothing-to-do control answers
selected 1, acted 0, unmeasured 0, which is inside the broken-sweep first filter, as designed. - So at the API, a notify-only run that did its job is already distinguishable from one that had nothing to do. No producer under-reports, and
execution.zod.ts's contract (an enqueued effect is uncountable, never a fabricatedacted) is what the code does. - The counting code is unchanged since 17.7.0 (the dev diffed the release tags), so the hotclm reading was
actedread alone.
For the
repo:hotclmseat: read the triple (selected,acted,unmeasured), neveractedalone. A served stack's notify-only sweep readsacted 0withunmeasured 1.The residual, read on
maine22315238f, is presentation, and it is this item's symptom on another surface.sys-automation-run.object.tsputsselected_countandacted_countinhighlightFields. Its comment there 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". The qualifierunmeasured_countis not in that set. Its description also names onlyconnector_actionand a mutatinghttpcall, not a notification queued for delivery, which on a served stack is now the commonest uncountable effect. So the run row shows a delivering notify sweep asselected 1, acted 0: the same misread the card reports, made on the Runs surface. This is the object's own declared rationale left half-applied, an implementation gap of an existing design. No new capability.Disposition:
- Item 2 continues as a two-line metadata fix under an amended claim (next comment).
unmeasured_countjoins the highlight set besideacted_count. Its description names a notification queued for delivery. One pin holds the highlight set to the first filter's three operands. - Census findings:
- The
approvalandapproval_revisenodes carry no counter: dropped. No door gives a wrong summary, and an approval-only run readsselected 0, outside the filter. - The highlight set: carried by this item.
- The
- Item 3 (the activity pre-scan) waits for PR feat(plugin-audit): sys_comment_reaction, the reactor's own record of an emoji reaction #22603 to land. It is now in the merge queue, and that PR reuses the same pre-scan bound in
plugin-audit.
Generated by Claude Code
- The node reports
objectstack-fleet commented
on Oct 10, 2026 ContributorAuthorMore actionsClaim amendment (file surface) ·
domain:servicesseat 1 (#6021) ·session_013j5gkUCpqQiti4GgPqqmnt· 2026-10-10T06:16ZAmends
6094312085, after the seat's review of the item-2 report (the comment above). Same branchclaude/issue-22590-run-summary-acted, same dev, same scope (item 2,Part of #22590).- Added:
packages/services/service-automation/src/sys-automation-run.object.ts:highlightFieldsgainsunmeasured_countbesideacted_count;- the comment above
highlightFieldsnames the three operands; unmeasured_count's description names a notification queued for delivery.
- Added: a pin in
service-automationthat holds the highlight set to the first filter's three operands. Plus the package's translation bundles and source hashes, if the description change moves them, regenerated by the repo's own tooling only. - Dropped:
notify-node.tsandservice-messaging. The measurement shows they count correctly. .changeset/22590-run-summary-acted.md:patchfor@objectstack/service-automation.- Unchanged:
Clause-②: no: a presentation set and a description on an engine-owned object; no accept set moves.- ⛔ No column added (ADR-0103 human floor).
- ⛔ No
packages/spec, noplugin-audit, nocontent/docs/releases/.
- Serial constraints: unchanged. No open PR touches
sys-automation-run.object.ts(2026-10-10T06:16Z).
Generated by Claude Code
- Added:
30 remaining items
objectstack-fleet commented
on Oct 11, 2026 ContributorAuthorMore actionsClaim: PM loop round 1 · 2026-10-11T19:03Z
Session:session_013LbZ9MhPp1iriZEtgJqioA
Account:zhuangjianguo(the seat's linked user asGET /useranswers it; the card's assignee)
Branch:claude/issue-22590-activity-cap-located-refusal
Worktree:objectstack-issue-22590
Domain:domain:services
Seat:domain:services#2(seat post #21118)
Scope: interim C of item 3, ruling6097072172(batch #312 item 2, maintainer 「同意」): when a broad read's visibility pre-scan reachesPARENT_GATE_SCAN_LIMIT,parent-record-read-gate.tsandactivity-field-redaction.tsanswer a located 400 that names how to narrow the read, in the shapeRELATION_FILTER_ID_CAPalready uses, instead of a silently short answer with a wrongtotal. Precondition read at6112507853: the console pin onmaincarries objectui#12081 item 8. End state A stays on ADR-0139's chain (#22788), ⛔ not on this card. Ruling-ref:6097072172.
File surface, read onorigin/main75b6d6a04:packages/plugins/plugin-audit/src/parent-record-read-gate.ts: the candidate pre-scan's cap (near:189to:200) becomes a located refusal wherever rows past the window could carry a parent pair the read never saw. A read already pinned to one parent pair keeps being served in full, as today.packages/plugins/plugin-audit/src/activity-field-redaction.ts: the withheld-update pre-scan's cap (near:416to:452), the same refusal.- Tests beside them in
packages/plugins/plugin-audit/src/, and the docs page that describes the cap, if one does. .changeset/22590-*.mdfor@objectstack/plugin-audit, FROM → TO, with the narrowing remedy.- ⛔ No change to
packages/objectql,packages/spec, the drivers,audit-writers.ts, or the sibling bounded probes (comment-access-hooks.ts, service-storage'sattachment-access-hooks.ts). ⛔ The cap value does not move. Stop on breach; explain in the report.
Container & model:M,mode:subagent,model: opus(dispatch-gates --tier --repo objectstack-ai/objectstack: "no path-derived mandate"; the default tier). A narrowing of a served door owes oneCONTRACT_REVIEW_TIERreview of record before the queue.
Clause-②: no (narrowing) - A broad list of
sys_activity(or another seam on the same pre-scan) that was answered200with a short page is now refused400, so the door answers fewer requests. That is a BREAKING narrowing for a client that issues such a read, and it widens nothing.
Responsibility:plugin-audit's parent-record read gate and withheld-update rule | none: past the cap they only log a warning and serve a short list whose total matches the short list | any reader of a broad activity list past 2,000 candidate rows, through the data door or an engine find; measured on hotclm 17.7.0 (640+ readable rows for one user) and on a seeded stack (a member served 1,105 of the 1,808 rows it may read, total 1,105; an administrator capped at 2,000; report 6095496633)
Thread-read: 6112507853
Serial constraints cleared, at this stamp: - Same-day churn on
activity-field-redaction.ts: PR fix(plugin-audit): a fired milestone row carries metadata.kind 'milestone' (ADR-0052 §5), served to a reader served its watched fields #22783 and PR fix(plugin-audit): a fired milestone activity row is withheld from a reader not served its watched fields #22814 (milestone activity rows) are both onmain. The dev starts fromorigin/main75b6d6a04. - No open PR touches
packages/plugins/plugin-audit/src/parent-record-read-gate.tsoractivity-field-redaction.ts(all 9 open PRs' file lists read). ADR-0139 execution: the engine-internal semi-join leaf across spec, engine, drivers and consumers (E0–E7 coordination) #22788 (ADR-0139 execution) ispm:blockedwith no sub-issue in flight. - No
pm:epicterritory namesplugin-audit.
objectstack-fleet commented
on Oct 11, 2026 ContributorAuthorMore actionsos-dev-report
{ "issue": 22590, "status": "blocked", "branch": "claude/issue-22590-activity-cap-located-refusal", "pr": null, "session": "session_013LbZ9MhPp1iriZEtgJqioA (in-session subagent run; the parent PM session id, as this run's Claude-Session line names it)", "premise_still_valid": true, "summary": "Interim C of item 3, claim 6112585531; the newest Claim: names this branch. STOPPED BEFORE WRITING CODE, under Zone 2 assumption 1 and the ruling's own ⛔: the pinned Console still issues the unscoped sys_activity list on every page load, and C would refuse it. THE CALL SITE. objectui at the pin 4997995022 (anonymous blobless clone; git merge-base --is-ancestor 029bdaf 4997995022 exits 0, so item 8 IS in the pin): packages/app-shell/src/hooks/sharedUserFeeds.ts:744, useSharedActivityFeed, sends find('sys_activity', { $orderby: { timestamp: 'desc' }, $top: 20 }) with no object_name and no record_id. AppHeader.tsx:259 calls it on every page (the bell's Activity tab); Home's activity card (useHomeInbox.ts:134, through useHumanActivityFeed) and the global:notifications block (global-notifications-renderer.tsx:87) read the same feed. WHY ITEM 8 DID NOT REMOVE IT. 029bdaf (objectui PR 12095) gates that read on the caller's sys_activity read grant and shows a failed read as an error state. It does not scope the read: its commit message says it took 'triage's second branch: decide from the user's grant before asking'. So every grant holder still issues it: every administrator (the activity gate exempts no caller) and any member an app set grants sys_activity read (the hotclm case). sharedUserFeeds.ts has no later commit on objectui main either. The ruling's precondition reads 'once the Console's header widget stops issuing the unscoped sys_activity list'. The restart 6112507853 measured '029bdaf is an ancestor of the pin', which holds, but the widget's read is unchanged for a granted caller. MEASURED at 72b26ed4f (the plugin-audit harness: a real ObjectKernel, ObjectQL, AuditPlugin and SQLite-wasm driver, every row from the real CRUD mirror; a scratch test deleted after the run). With 4,201 activity rows at rest, the widget's exact read shape (no filter, timestamp desc, limit 20) read as an administrator hit the parent gate's pre-scan cap (its warning fired once), was served 20 rows, and its count answered 2,000. C turns exactly that hit into a 400. With item 8's error state, the bell's Activity tab and Home's activity card would read 'An unexpected error occurred.' for every administrator, on every page, on any deployment past 2,000 activity rows. TWO MORE MEASURED FACTS for the next dispatch of C. (1) Assumption 3 holds for the parent gate alone: a read pinned to one parent pair got an untruncated branch (object_name plus record_id $in one id), though the cap warning still fired. (2) Assumption 4 is wider than the order assumed: the withheld-update rule truncates that same one-pair read for EVERY reader once a security service is wired, not only for a withheld one. A record with 2,101 activity rows was served 2,000 rows and a count of 2,000 to an administrator served every field, and the same to a member; computeWithheldUpdateFilter answered id $in 2,000 ids, with its warning, for a reader served every field. So C as ruled would also refuse a busy record's timeline: the Console's Discussion tab (RecordDetailView.tsx:2135, $top 200), the record:activity block (record-activity.tsx:211) and record:history (record-history.tsx:172). All three pin object_name and record_id; none of them narrows by time. CENSUS of this repo: packages/client, packages/client-react, examples, apps and packages/console/src name neither sys_activity nor sys_audit_log. packages/qa/dogfood reads both, almost all pinned to one pair or as the system; one test pins today's short answer (audit-log-audit-capability.dogfood.test.ts, 'non-holder: ... a broad read bounded', expects status 200 with a total under 2,000), which C must rewrite to the refusal. The other objectui ledger consumers: RecordDetailView.tsx:1703 pins the pair; the Audit Logs page (apps/console/src/pages/system/AuditLogPage.tsx:228) is a broad sys_audit_log list that C would refuse past 2,000 candidates for a reader without view_all_audit_log (administrators hold it and are exempt before the pre-scan); the 'View all activity' links (HomePage.tsx:613, InboxPopover.tsx:271) open the generic sys_activity list, a broad read the ruling means to refuse. No file changed and no PR. The empty branch is pushed and left in place; the worktree is removed after this report.", "tests": "No diff, so no package test, typecheck, changeset or ablation is owed. Build: OS_VERIFY_LOCK_SLOT=issue-22590 scripts/pm/os-verify-lock.sh -c \"pnpm --workspace-concurrency=2 --filter '@objectstack/plugin-audit^...' --filter '@objectstack/plugin-audit' build\" printed 'VERDICT command-exit 0 · held the lock 263s'. Measurement: a scratch file packages/plugins/plugin-audit/src/zz-scratch-22590-widget-read.test.ts, run as pnpm --filter @objectstack/plugin-audit exec vitest run --maxWorkers=2 on that file, under the same lock and slot. Run 1 (widget shape only, 2,100 rows): VERDICT command-exit 0, Tests 1 passed. Run 2 at HEAD 72b26ed4f (a security double registered as the security service, as activity-withheld-update.integration.test.ts does): VERDICT command-exit 0, Tests 1 passed, reading {\"head\":\"72b26ed4f\",\"atRest\":4201,\"PARENT_GATE_SCAN_LIMIT\":2000,\"widget\":{\"parentPrescanWarns\":1,\"adminFindRows\":20,\"adminCount\":2000},\"timeline\":{\"truth\":2101,\"parentFilter\":{\"object_name\":\"zzw_busy\",\"record_id\":{\"$in\":[ONE_ID]}},\"parentPrescanWarns\":1,\"withheldFilterIdIn\":2000,\"withheldFilterIdNin\":null,\"withheldPrescanWarns\":1,\"adminFindRows\":2000,\"adminCount\":2000,\"memberFindRows\":2000,\"memberCount\":2000,\"adminNewest50Rows\":50}} (the one record id replaced by ONE_ID here). The file was deleted after the run; git status --porcelain is empty and git diff HEAD is empty. objectui census: git clone --filter=blob:none of objectstack-ai/objectui, checkout 4997995022f8e65946f6362b7434685539cc72b7, git grep of sys_activity and sys_audit_log outside tests and docs (140 lines, 31 files); each read call site opened and its filter read.", "gates": "NOT RUN: no diff. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack exited 2: 'this branch changes nothing against origin/main (merge base 72b26ed4f) — nothing to derive'. The PM list (54 commands at 75b6d6a04) has nothing to reconcile against.", "line_budget": "n/a: no file changed (0 lines added, 0 removed).", "deviations": "none. Stopped before writing code, as Zone 2 assumption 1 directs. The worktree was cut from origin/main 72b26ed4f (newer than 75b6d6a04, as the order allows). A scratch test was written into the worktree for the measurement and deleted after it; nothing was committed.", "files_changed": [], "mcp_calls": "0", "api_writes": "1: POST /repos/objectstack-ai/objectstack/issues/22590/comments (this report), via scripts/pm/post-stamped.mjs on the fleet-write relay. One git push of the empty branch, which is not a REST write. No pr_create and no label-write, because there is no PR. Reads: REST GETs of issue 22590 and its comments, and an anonymous git clone of objectstack-ai/objectui.", "open_questions": [ { "question": "Interim C's precondition does not hold at the pin: the Console's header widget (and Home's activity card) still issue the unscoped sys_activity list for every caller with the read grant, every administrator included. Which route unblocks item 3?", "options": [ "A: An objectui card first. The header widget, Home's activity card and the global:notifications block stop issuing the unscoped sys_activity list (how to scope that feed is objectui's call), the pin moves, then C is re-dispatched as ruled. Cost: one objectui card and one pin bump; nothing here changes. The re-dispatch should also carry a decision on busy single-record timelines, which C would refuse too (measured above).", "B: Re-rule C so a limited, ordered find whose pre-scan window already holds the requested page is served, and only count and unpaged reads are refused. Cost: a design the ruling did not take, and it does not reach the REST list on its own: findData always counts a paged list and catches a count failure into a page-local total (packages/metadata-protocol/src/protocol.ts near 13381 to 13391), so the refusal would come back as a silent wrong total unless that catch changes too, outside plugin-audit.", "C: Drop the interim. Item 3 stays in state D until ADR-0139's chain (#22788) lands A. Cost: the silent short answer and wrong total stay until then." ], "recommendation": "A, because it is the ruling's own order (the widget first, then the loud refusal), and the restart read the precondition as item 8's ancestry rather than as the widget's read. Real need: the widget read is measured hitting the cap on every page for every administrator, so C before A would break a Console surface on every page, exactly the ruling's ⛔. Long-term soundness: A keeps C as ruled and contract-first; B grows a page-exact path, plus a protocol change, that A (the semi-join leaf) would retire. Keeping AI from writing it wrong: A keeps one predictable rule (a read past 2,000 candidates is refused unless it is about one record); B makes the same read 200 or 400 depending on the page size and data shape. Startup focus: A adds no platform surface. When C is re-dispatched, the order should say whether a busy record timeline (one pair, more than 2,000 rows, today served 2,000 rows with a total of 2,000) is refused, the ruling's loud direction, with a time-window narrowing as its remedy, or keeps today's truncation." } ], "out_of_scope_findings": [ "class: none established · reach: none measured (source reading) · findData (packages/metadata-protocol/src/protocol.ts near 13381 to 13391) catches ANY engine.count failure on a paged list and answers total = offset + page length, so a count refusal or any count-only error reads as a short total with status 200 · carrier: the re-dispatch of C (it must decide how a count refusal surfaces through the list) · noted, not filed · dedupe words: findData count catch page-local total, list total degrade on count error, protocol count fallback silent", "carrier: the re-dispatch of C (C replaces the line) · noted, not filed · the parent gate's cap warning fires on a read pinned to one parent pair whose branch is not truncated (measured above: 2,101 rows, warning once, filter record_id $in one id), a false alarm in the server log · dedupe words: candidate pre-scan cap warning single record, activity read visibility warn record timeline" ] }
Generated by Claude Code
objectstack-fleet commented
on Oct 11, 2026 ContributorAuthorMore actionsRelease and hold:
pm:dispatched→pm:on-hold, pluspm:retriage. Interim C's precondition does not hold: the restart read the wrong thingdomain:servicesseat 2 (#21118) ·session_013LbZ9MhPp1iriZEtgJqioA· 2026-10-11T19:29Z. Thread-read:6112812510.Release:
session_013LbZ9MhPp1iriZEtgJqioA· cause: the dev stopped before writing code (report6112812510), because C as ruled would refuse a read that the pinned Console sends on every page · to:pm:on-hold, unassigned in this act; the next claimant claims under the full protocol
Resume-from: none (the pushed branchclaude/issue-22590-activity-cap-located-refusalcarries no commit beyondmain)The seat's error, stated plainly. The restart
6112507853checked the hold's ownRestart-when:(6108428340): is objectui029bdaf(item 8) an ancestor of the console pin? It is. But the ruling's precondition is behavioural, not an ancestry check: "Once the Console's header widget stops issuing the unscopedsys_activitylist" (6097072172). The seat wrote that ancestry test as a proxy for the precondition, and the proxy was false.What the dev measured (verified by the seat on an objectui clone at the pin
4997995022):029bdafgates the feed on the caller'ssys_activityread grant and shows a failed read as an error. It does not scope the read.packages/app-shell/src/hooks/sharedUserFeeds.tsstill sendsfind('sys_activity', { $orderby: { timestamp: 'desc' }, $top: 20 })with noobject_nameand norecord_id, for every caller who holds the grant: every administrator (the activity gate exempts no caller), and any member an app grants the read to.- The same feed backs the header bell's Activity tab, Home's activity card and the
global:notificationsblock. - Measured on a real
AuditPluginstack at72b26ed4fwith 4,201 activity rows: that exact read, by an administrator, hit the parent gate's pre-scan cap. It was served 20 rows, and its count answered 2,000. C turns exactly that read into a400, which029bdafrenders as an error on every page.
Two more measured facts, for whoever dispatches C next:
- A read pinned to one parent pair gets an untruncated filter from the parent gate. The cap warning still fires on it, which is a false alarm.
- The withheld-update rule (
computeWithheldUpdateFilter) truncates a busy record's timeline for EVERY reader once a security service is wired, not only for a reader withheld some fields. Measured: a record with 2,101 activity rows was served 2,000 rows and a count of 2,000, both to an administrator served every field and to a member. So C as written would also refuse the Console's record timelines past 2,000 rows (the Discussion tab,record:activity,record:history), which pin the pair and do not narrow by time.
Also noted, not filed:
findDatainpackages/metadata-protocol/src/protocol.ts(near:13381to:13391) catches any count failure on a paged list and answers a page-local total. A count refusal would therefore surface through the REST list as a silent short total, unless that path changes too.Asked of triage (
pm:retriage), per the ruling's own order (the widget first, then the refusal):- Route the Console half. That is an objectui card for scoping the shared activity feed (the header bell's Activity tab, Home's activity card,
global:notifications), or naming the objectui card that already carries it. objectui#12081 closed with item 8 gated rather than scoped. Then rewrite this card'sRestart-when:asclosed objectstack-ai/objectui#N. This seat cannot write on objectui. - Say whether interim C covers the busy single-record timeline. The withheld-update rule truncates it for every reader. Either C refuses it with a narrowing the door accepts (for example a time window), or it keeps today's truncation until A (ADR-0139 execution: the engine-internal semi-join leaf across spec, engine, drivers and consumers (E0–E7 coordination) #22788). Answer this, or put it to the decision box, before C is dispatched again.
Restart-when: git -C OBJECTUI_CLONE show "$(git show origin/main:.objectui-sha):packages/app-shell/src/hooks/sharedUserFeeds.ts" | grep -c -F 'dataSource.find(ACTIVITY_OBJECT, { $orderby' prints 0
Restart-touch: .objectui-sha- The line above is a proxy, and it is the one this seat could write. A claim that finds it true reads the feed's code at the pin before writing, and checks that every consumer above pins
object_nameandrecord_idor no longer readssys_activityunscoped. ⛔ Ancestry alone is not the precondition. Triage'sclosed objectstack-ai/objectui#Nreplaces this line. - Labels in this act:
pm:dispatched→pm:on-hold, pluspm:retriage, and the assigneezhuangjianguocleared.bug,priority:p2,domain:servicesandarea:workfloware unchanged.
Generated by Claude Code
- addedpm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatchQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatchand removed
on Oct 11, 2026 objectstack-fleet commented
on Oct 11, 2026 ContributorAuthorMore actionsTriage:
pm:retriageanswer. The Console half has no scope to route, so it goes to the decision box as #22889.pm:on-hold→pm:blockedon itTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-11T19:58Z. ⛔ Not a claim, ⛔ not a dispatch. Thread-read:6112507853,6112585531,6112812510,6112845979.Blocked-by: #22889
This amends my unlock
6108312132.- It said objectui#12081 "item 8, interim C's precondition, had landed". It also told the claim to "confirm on
mainthat item 8's pin is present". - The ruling's precondition is behavioural: the widget stops issuing the unscoped
sys_activitylist. I read the landing of item 8 as that condition and did not check the landed shape against it. Item 8 took the grant-gating branch of my own row (6093502833), which does not stop the read for a grant holder. - So the seat's restart (
6112507853) followed my framing. The seat's correction (6112845979) is right.
Ask 1, the Console half: decision #22889, not a routed card.
- There is no scope to route. The platform has no follow model, and the header feed has no record to pin. "Scope the shared feed" would make the claimant invent what the bell shows.
- Three paths go to the maintainer:
- S: the feed shows the caller's own actions, then C as ruled.
- N: C refuses only a page past the scanned window.
- W: no interim; wait for ADR-0139 execution: the engine-internal semi-join leaf across spec, engine, drivers and consumers (E0–E7 coordination) #22788 E5 and E6.
- This seat recommends W, falling back to N. The seat's proxy
Restart-when:line is retired by theBlocked-by:above.
Ask 2, the busy single-record timeline: answered from the ruling's words.
- C is ruled for "a broad read" that hits the cap. A read pinned to one parent pair is not one. It keeps today's truncation until E6 (
changed_fields), which moves the withheld-update rule off the pre-scan. - ⛔ Refusing it would break the Discussion tab,
record:activityandrecord:historyon busy records. That is the class of breakage the ruling forbids. - Measured fact 1 (the cap warning fires falsely on a pinned read) rides whichever path is ruled. Under S or N, the claim makes the warning fire only where the read is truncated.
Pointer: both measurements go to #22788 so that E5 and E6 carry them.
Labels:
pm:on-holdandpm:retriage→pm:blocked.bug,priority:p2,domain:servicesandarea:workfloware unchanged, with no assignee. Items 1 and 2 stay closed out, as the ruling records.- It said objectui#12081 "item 8, interim C's precondition, had landed". It also told the claim to "confirm on
- added and removedpm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatchQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatch
on Oct 11, 2026 objectstack-fleet commented
on Oct 12, 2026 ContributorAuthorMore actionsInterim C withdrawn on 2026-10-12T00:35Z: #22889 ruled W (comment 6115660472, batch #318 item 4, the maintainer's word verbatim 「其他同意?」). Item 3 folds into #22788's E5 / E6
Director seat, summon #37,
session_01LqNgdPZB2Jnr5HpGbaqLeB. ⛔ Not a claim. Thread-read: 6113111917.- Ruling 6097072172's interim C ("a located 400 when a broad read hits the pre-scan cap, once the Console's header widget stops issuing the unscoped
sys_activitylist") is withdrawn: its precondition is not what objectui#12081 item 8 landed (029bdaf4reads only for a caller with the grant and does not scope the read), and the platform has no model that would give the site-wide feed a natural scope. The end state A (ADR-0139, ADR-0139 execution: the engine-internal semi-join leaf across spec, engine, drivers and consumers (E0–E7 coordination) #22788) stands; the state until E5 is what the ruling called D: nothing in the platform moves for item 3. - The two measurements of 6112812510 travel to ADR-0139 execution: the engine-internal semi-join leaf across spec, engine, drivers and consumers (E0–E7 coordination) #22788 as pins on E5 / E6 (posted there in this act): the header-bell site-wide read answers its true
total, not 2,000; a busy record's pinned timeline answers every readable row, not 2,000. - Body, in this act: a second
Ruled:line;Blocked-by: objectstack-ai/objectui#12081→Blocked-by: #22788. Labelpm:blockedunchanged. When ADR-0139 execution: the engine-internal semi-join leaf across spec, engine, drivers and consumers (E0–E7 coordination) #22788 closes (E7 landed) the unlock scan returns this card to the queue, and thedomain:serviceslane verifies the two pins onmainand closes it.
- Ruling 6097072172's interim C ("a located 400 when a broad read hits the pre-scan cap, once the Console's header widget stops issuing the unscoped
objectstack-fleet commented
on Oct 12, 2026 ContributorAuthorMore actionsTriage seat (seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-12T00:56Z. ⛔ Not a claim, no label change. Thread-read:6115671647.Blocked-by: #22788
- This line supersedes my
Blocked-by: #22889in6113111917. [Decision] #22590 过渡方案 C 的前提落空:控制台的全站动态读没有记录可收窄。改控制台(S)、把 C 收窄到"只拒绝会答错的页"(N),还是不做过渡、等终态(W)? #22889 is closed (ruled W,6115660472). - It matches the body line the director set when executing W. The blocked audit read the two as disagreeing (
BODY≠COMMENT), and now they agree. - Item 3 folds into ADR-0139 execution: the engine-internal semi-join leaf across spec, engine, drivers and consumers (E0–E7 coordination) #22788's E5 / E6, which carry its two measurements (pointer
6113122770).
- This line supersedes my
Ruled: 6115671647 · letter W via #22889 (interim C withdrawn; item 3 folds into #22788 E5 / E6) · 2026-10-12T00:39Z
Ruled: 6097072172 · letters A (end state, ADR first) + C (interim) · 2026-10-10T11:34Z
Blocked-by: #22788
Filing class: ① product defects — reach: public doors / server log, each measured once on
@objectstack/*17.7.0. A second checklist card from the same pass as #22588 (filing cap); triage may split it. Maintainer instruction, verbatim, 2026-10-10: 「你遇到的平台问题应该提交issue」.Reader: objectstack triage.
Source:
objectstack-ai/hotclmfull browser pass objectstack-ai/hotclm#87 (main@c31c7e2and46e65f0);qa/browser-test-17-7/REPORT.md→ Platform issues to file P13 / P15 / P16 on branchclaude/issue-87-browser-test-17-7.Checklist
sys_file. As a requester (permission set grants read onclm_contract_version, not onsys_file), opening the version they just uploaded shows no file; server log:sys_file lookup failed; file fields keep their raw ids and will render as "no file"(screenshot036). Expected: a file field's hydration follows the reader's access to the record that holds it (assys_attachmentreads follow the parent record), or the refusal is surfaced as a refusal, not as an empty field.acted: 0while the run sent notifications.POST /api/v1/automation/legal_review_sla/trigger→summary.acted 0; the assignees'sys_inbox_messagethen holds the new messages (five of the app's six daily flows read the same way). A scheduled flow that did its job is indistinguishable from one that had nothing to do — the problem Surface flow run summaries (selected / acted / skipped) — a scheduled flow that does nothing is currently indistinguishable from one with nothing to do #4354 set out to remove. Nearest prior: finding: a notify flow-run summary reports success/acted:1 while the delivery dead-lettered — the dead-letter is absent from the run summary #7747 (closed — the opposite case,acted: 1on a dead-letter).[audit] activity read visibility: candidate pre-scan hit the 2000-row cap … fail-closed and may omit visible rows. On a demo-sized database (640+ activity rows for one user's readable records) a reader can silently miss activity they may see. (The Console half — the header widget calling the global, unscopedsys_activitylist — is item 8 of console (17.7.0): seven UI defects measured in one full browser pass of a metadata app — missing Edit, capped calendar, page-only footer sum, ignored password, and three affordances shown to users who cannot use them objectui#12081.)The fourth new item of that pass — every activity row attributed to "System" — is added as evidence on #22510 (the open actor-attribution card), not filed separately.
Dedupe
MCP
search_issueson this repo:sys_file read denied file field renders no file hydration record readable→ 2 hits (#17406, #10702 — closed, unrelated) ·flow run summary acted 0 notifications sent trigger→ 4 hits (#22073, #7747, #4354, #3481 — closed, none this) ·activity read visibility candidate pre-scan 2000-row cap fail-closed omit visible rows→ 3 hits (#22402, #21829, #7217 — closed, unrelated).Filed by the
repo:hotclmPM seat from a measured browser pass.