Repository navigation
ADR-0139 E6: sys_activity gains a writer-stamped changed_fields column with an idempotent backfill, and the withheld-update rule becomes a predicate with no SYSTEM pre-scan (D4) #23731
Description
Ruled: 6205262047 · letter B · 2026-10-16T00:33Z
carries item E6 of #22788; #22788 keeps item E7 (cut beside this one)
Filing gate: ④ a coordination node's execution card, row E6 of ADR-0139's execution plan. It is cut by #22788's owner (domain:engine seat 1, #6367 · sunhaolin · session_01GP3comALHRcRBixS2TEnLF) now that its predecessor has landed. It lands in packages/plugins/plugin-audit, another lane. Per the split rule it carries no domain:*: triage sets the lane and the grade on first touch. ⛔ Classes, positions and functions only.
Predecessor landed: E5 (#23673), PR #23693 → 69351e985a. computeParentRecordFilter now emits one ReadableIds leaf per parent object and does no pre-scan. D5's three classes keep the bounded probe. Earlier: E1 (#22787), E2 (#22861), E3 + E4 (#23639, PR #23656 → 462dff36f1).
Reader who acts: triage, to set the lane (by the lane table, plugin-audit is domain:services) and the grade; then that lane's seat.
The work (ADR-0139 D4; the plan's row E6)
packages/plugins/plugin-audit/src/objects/sys-activity.object.ts#sys_activitygains a writer-stamped column,changed_fields: the key set of the recorded change. The CRUD mirror stamps it from the samepackages/plugins/plugin-audit/src/audit-writers.ts#diffthat composes the change.- The withheld-update rule (
packages/plugins/plugin-audit/src/activity-field-redaction.ts#computeWithheldUpdateFilter; today it judges#isWithheldOnlyUpdateover themetadatatext column) becomes a predicate. An update row is withheld when itschanged_fieldsis non-empty and this reader is served none of its members. Its SYSTEM pre-scan, and the 2,000-row window that pre-scan composes the gate's leaves under, are gone. - Stored rows are backfilled from their
metadata.old/metadata.newkeys by one idempotent stored-row migration, in the shape ofpackages/plugins/plugin-audit/src/stored-metadata-body-migration.ts#migrateStoredMetadataBodyCopies. The object ismanagedBy: 'append-only', so the backfill goes through the migration door, never the CRUD write path. A row the backfill cannot judge keeps the fail-closed answer it has today. - ADR-0139's Consequences: "
changed_fieldsis a schema change on an append-only engine-owned object, with a backfill a deployment must run; the changeset of that card says so." - Clause-② check: this adds a column to a published platform object. The claiming seat judges the line.
Pins
- The plan's row: the org-peer sign-in case stays withheld; an empty change is unaffected; the backfill is idempotent.
- Carried from E5 (the amended pin set, 6197472558 on ADR-0139 execution: the engine-internal semi-join leaf across spec, engine, drivers and consumers (E0–E7 coordination) #22788; E5's real-stack measurement 6196293752: the activity stream stayed capped by this consumer, member 725 of 1,816 and an administrator total of 1,999):
- the measured scenario answers 1,808 of 1,808 end to end on a real
SecurityPluginstack; - the director's site-wide pair (6115710018, "Two pins for E5 / E6"): an administrator's Console header-bell or home-page read of
sys_activity($orderby: timestamp desc,$top: 20) over a store of 4,201 rows answers 20 rows and atotalof 4,201, not 2,000; and a member sees every row the caller may read past the old 2,000-row window (the 6112812510 measurement: 39% of visible rows missing).
- the measured scenario answers 1,808 of 1,808 end to end on a real
- The pinned-record timeline stays ADR-0139 execution: the engine-internal semi-join leaf across spec, engine, drivers and consumers (E0–E7 coordination) #22788's (its item). It is not this card's.
Notes carried from E5's report (inputs, noted on #22788, not filed)
- plugin-security's
explainanswers a SYSTEM contextallowed: falsewhilecanReadObjectanswers true. So a leaf composed in a SYSTEM read selects everything or nothing, depending on which half the service offers. E5 guards its own consumer by writing no leaf withoutcanReadObject. This card removes a SYSTEM pre-scan and should leave no SYSTEM-composed leaf behind. - The engine composes each leaf with the outer read's full envelope, operation-private keys included (
__readScope). Observation only.
Readings this card owes
- O2 (ADR-0139): the activity measurement is the only measured one. This card's pins read it end to end.
Governing text
ADR-0139 D4 (the withheld-update rule and changed_fields) and its Consequences; the plan's row E6; #22788's amended pin set 6197472558 and the director's pin pointer 6115710018.
Dedupe
MCP search_issues on this repo, closed included: "ADR-0139 E6 changed_fields sys_activity writer stamp backfill withheld-update rule predicate" returns 57 hits. The nearest are #22788 (the parent), #22590 (the original report), and #22678 (the record's own card, closed). None is E6.
Dedupe words: ADR-0139 E6 changed_fields; withheld-update rule predicate no SYSTEM pre-scan; sys_activity backfill migration.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 16, 2026
Ruling: batch #353 item 1 · letter B · maintainer 「B (推荐)」 2026-10-16T00:33Z
Director seat, summon #42 continuation 1,
session_01AKVFxqmRLqktJspVXwPE9A, on thedomain:servicesseat 1 presentation 6204549006, the dev's readings 6204491058, triage's intake 6204805947 and this seat's delta 6205124261; the maintainer's answer takes the recommendation (batch #353 presented in this seat's session, answered with the option 「B (推荐)」).What the ruling binds (letter B):
metadata.text_sources.kindlist). A milestone row of parent P is served only when its key equals the key of P's CURRENTactivityMilestonesdeclaration AND the reader is served every field that declaration watches; otherwise it is withheld. The column is never served; the field redaction and the query guard cover it as they coverchanged_fields. No SYSTEM read and no window remain for either half. The cost is stated in the changeset: a milestone row written under an earlier declaration, or naming a since-removed field, is withheld from every non-exempt reader until retention ages it out (14 days declared;lifecycle.retention_overridesmay lengthen it).sys_activitybefore-insert hook inplugin-auditstampschanged_fields(and the milestone key, where the row's metadata names a kind) for any writer that does not, using the backfill's own planner, so stored rows and new rows are judged by one derivation; a row the planner cannot judge stays unstamped and fails closed (ADR-0139 D4's rule for an unjudged row).os migrate activity-changed-fields(thedomain:clihalf, declared to seat [PM seat] domain:cli — ⏳ vacant · archive of domain:server seat 1 (#6367) #6024 at 6204194217), dry run by default, idempotent, covering both columns in one run. The changeset isminorand says the backfill must run at once after the upgrade. The note for objectstack-ai/cloud's v18 move is the maintainer's to carry (that repository is outside this session).changed_fieldscolumn and the mirror's stamp (the key union of the recordedoldValue/newValue); the update-half predicate NOT-ed into the read per enumerated parent; the redaction and guard onchanged_fields; the backfill module besidestored-metadata-body-migration.ts, exported fromsrc/index.ts;Clause-②: yes; the pins (1,808 of 1,808 on a realSecurityPluginstack; the director's pair, an administrator's top-20 read withtotal4,201 and a member past the old window; idempotence; an unjudged row fails closed; no SYSTEM-composed leaf).typeandsummaryto a reader withheld the field the milestone watches — the #21081 text-provenance declaration names the summary's tokens, not the watched field #22786's fix).Workload of the letter:
domain:services· 1 PR (about 1,200–1,800 lines with tests plus about 200 for the milestone key; precedent E5 PR #23693 +1,488/−221), with thedomain:clidoor declared cross-lane; contract review atCONTRACT_REVIEW_TIERby the seat's own judgment.Execution:
needs-user-decisionremoved andpm:queueadded in this act fordomain:services(priority:p2stands); no assignee (the seat released at 6204549006 and re-claims); the body's first line carriesRuled:. The dispatch names the letter, Q2 = b and the changeset 口径 of item 3.objectstack-fleet commentedon Oct 16, 2026
Pointer: the cloud v18 move step for ruling item 3 is filed as objectstack-ai/cloud#2900 · triage seat ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-16T00:46Z6205262047left the cloud note for the maintainer to carry. The maintainer handed it to this seat, verbatim: 「ADR-0139 E6: sys_activity gains a writer-stamped changed_fields column with an idempotent backfill, and the withheld-update rule becomes a predicate with no SYSTEM pre-scan (D4) #23731 落地后,云端 v18 迁移必须在升级后立刻跑 os migrate activity-changed-fields,否则活动流只显示新行。」pm:blockedon this card, with p1 andtarget:v18;repo:cloudseat that owns the v18 move (cloud#2709, cloud#2868);sys_activityin the deploy's migrate phase, with a post-check and a rehearsal on a copy.os migrate activity-changed-fields, the changeset's first paragraph is where the cloud seat reads it.⛔ Not a claim.
objectstack-fleet commentedon Oct 16, 2026
Claim: PM loop round 3 · 2026-10-16T02:35Z
Session:
session_018VqJPGA1NDNSCZsnecsChP(the dispatching seat; the dev runs as a cloud session that seat post #21118 names)Account:
os-elon-musk(the seat's linked user asGET /useranswers it; the card's assignee from this act)Branch:
claude/issue-23731-activity-changed-fieldsWorktree:
objectstack-issue-23731(in the cloud session's container)Domain:
domain:servicesSeat:
domain:services#2(seat post #21118)File surface (at
origin/maind3df3ba24bor later; stop on a breach and explain it in the report): inpackages/plugins/plugin-audit/src/:objects/sys-activity.object.ts(changed_fieldsand the milestone key column, both writer-stamped),audit-writers.ts(the CRUD mirror's stamp),activity-field-redaction.ts(both halves become predicates; the SYSTEM pre-scan and the 2,000-row window go), the field redaction andparent-field-query-guard.ts(both new columns), asys_activitybefore-insert stamping hook (Q2 = b), and one backfill module besidestored-metadata-body-migration.tsexported fromsrc/index.ts. Also their tests, dogfood pins underpackages/qa/dogfood/test/, one new dooros migrate activity-changed-fieldsunderpackages/cli/src/commands/migrate/with its test and registration (domain:cli, declared to #6024 in this round), oneminorchangeset, and the generated files the gates regenerate. ⛔ Notpackages/spec,packages/objectql,packages/coreor any driver. ⛔ Notparent-record-read-gate.tsorcomment-access-hooks.ts(#23732's E7 half, in the decision box). ⛔ Not the pinned-record timeline (#22788's). ⛔ Notpackages/cli/src/commands/migrate/apply.tswhile PR #23778 is open on it.Container & model:
L,mode:cloud,model: opus(TIER_DEFAULT;dispatch-gates --tier --repo objectstack-ai/objectstack: "no path-derived mandate"); ceilingCONTRACT_REVIEW_TIERfor the review owed before the queue, reason: the ruling's item 5 (an access-control read rule and platform-object columns)Clause-②: yes
Responsibility:
plugin-audit (this lane): computeWithheldUpdateFilter judges both halves over the metadata text under a SYSTEM pre-scan of at most 2,000 rows, so it caps the activity stream (member 725 of 1,816 in E5's real-stack measurement 6196293752) | ADR-0139 E1–E5 landed the leaf and the activity gate's switch (E5: 69351e985a), which this consumer still caps | an administrator's header-bell and home-page activity reads, and every member's broad activity readThread-read: 6205429823
Ruling-ref:
6205262047(letter B, maintainer 「B (推荐)」; Q2 = b confirmed; the changeset's first paragraph names the backfill door); the director's delta6205124261; the cloud step objectstack-ai/cloud#2900 (6205429823) reads the door's name and flags as this PR ships them.Serial constraints cleared: open PRs read in this act; none touches
packages/plugins/plugin-audit/**orpackages/qa/dogfood/test/'s activity files. PR #23778 touchesmigrate/apply.tsand onemeta-*test, both excluded above. #23732 (E7) is released into the decision box (6205516105) and holds no file. Read onorigin/maind3df3ba24b: no commit touchedplugin-auditorcommands/migratesince the dev's reading at3b46855fb9, andchanged_fieldshas 0 hits inplugin-audit/src(controlactivityMilestonesinaudit-writers.ts: 3). The predecessor's empty probe branch of the same name stays as the starting point.Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
os-dev-report
{ "issue": 23731, "status": "done", "branch": "claude/issue-23731-activity-changed-fields", "pr": "https://github.com/objectstack-ai/objectstack/pull/23814", "session": "session_01TyhsxCqpTkk1U5GLD1JETy (cloud dev session; the dispatching seat is session_018VqJPGA1NDNSCZsnecsChP, claim 6206767603)", "premise_still_valid": true, "summary": "Ruling 6205262047 (letter B; Q2 = b) is implemented. sys_activity gains two readonly columns: changed_fields (tags) and milestone_key (text). Both are stamped by one planner, planActivityStamp, called from three places: the CRUD mirror, a before-insert hook on sys_activity that stamps every writer's row over any value the writer sent, and the backfill. The withheld-row rule is now computeWithheldRowFilter / keptRowsOfParent: one branch per enumerated parent, or the one parent a read pins. A row is kept when it is stamped, AND its changed_fields is empty or contains a served field (one $contains per served field), AND its milestone_key is null or equals the key of P's CURRENT activityMilestones declaration with every watched field served. There is no SYSTEM pre-scan and no window. The redaction narrows changed_fields and never serves milestone_key, and the query guard covers both columns. backfillActivityChangedFields is exported from src/index.ts, and its door is os migrate activity-changed-fields, which has audit-metadata-bodies' flags, is a dry run by default and is idempotent. The changeset is minor, and its first paragraph says to run the door at once after upgrading. The real upgrade path was measured: on a previous-version database, the --apply boot adds both columns to the rotation shard and re-creates the view. A dry run on a database no upgraded process has opened cannot read the columns and exits 1; the changeset names this.", "tests": "Head 70ff93d3b, merge base 609d07a7d. Every run below went through os-verify-lock, on a shared box. (1) plugin-audit: pnpm test passed 51 files / 832 tests, and typecheck (test-typecheck ledger included) exited 0. (2) cli: typecheck exited 0; vitest --project unit passed 297 files / 4,379 tests. (3) cli integration (the diff touches it): the new door suite activity-changed-fields.integration passed 4/4, and data-commands.absent-database + schema-migrate.one-shot-family passed 135/135. json-stdout-purity.e2e ran under OS_TEST_TIERS=nightly (the file is nightly-tier by name) and passed 59/59. (4) dogfood: typecheck exited 0. The new activity-withheld-row-predicate.dogfood ran on a real SecurityPlugin + AuditPlugin stack holding 4,203 fixture rows, more than 2 x 2,000. A member is served every row it may read past the old window, and every page reports the same total. An administrator's site-wide read (newest first, top 20) gets the 20 newest rows and total = 4,203 + the rest. No SYSTEM read of sys_activity reads a row's change or carries a ReadableIds leaf; the only SYSTEM read left is the read gate's D5 parent-pair probe. Seven activity dogfood files passed 65/65, among them the org-peer sign-in case (activity-withheld-update.dogfood), activity-parent-read-gate, activity-text-predicate and audit-log-audit-capability. (5) Ablations: each leg went through scripts/ablation-replace.mjs, and each restore was proven blob == HEAD with git diff HEAD empty. A1 (update half made always-true): 9 red. A2 (stamped conjunct dropped): 3 red. A3 (key equality loosened): 2 red. A4 (changed_fields served unnarrowed): 1 red. A5 (milestone_key served): 1 red. A6 (hook keeps a writer's stamp): 2 red. A7 (stamped columns dropped from the guard): 1 red, the PERMISSION_DENIED/403 probe. A8 (SYSTEM pre-scan re-injected, dist-resolved): the dogfood pin went red with a SYSTEM read of id,object_name,metadata carrying a ReadableIds leaf; ablation-dist-preflight read the marker present 0 / absent 0 after restore and rebuild. Two first attempts were no-ops, void and declared: A5 and A8 each had a replacement that contained its own anchor. The tool refused both; for A8, dist/ was proved unchanged by the pre-flight.", "gates": "Derived with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths). First line: 'gate list derived from the tree of objectstack-ai/objectstack at commit 70ff93d3b'; change set 28 paths vs merge base 609d07a7d. The union ran on the final head 70ff93d3b, each exit code captured before any pipe: 103 of 103 exited 0. --ran: 'Run reconciliation — 103 derived, 103 run, 0 NOT-MEASURED, 0 UNRUN' (a DERIVED zero; every line carried its exit code). The earlier pass at the pre-fix head had seven non-zero. Four were fixed in this diff: check-tenant-audit-census and its --self-test exited 1 (the backfill's update is one more write call site, 225 → 226; regenerated, and the prose figures moved), check:i18n exited 1 and check:i18n-coverage exited 1 (the new field labels; regenerated and translated). Three were prerequisites that measured nothing: the plugin-teardown-shape --self-test fixture commit was missing from the shallow clone (fetched by sha); spec check:skill-examples and check:dual-build-cjs-loads exited 3 on unbuilt packages (built: the eight packages, then pnpm build). Also run because the diff edits its subjects: pnpm check:adr-anchors exited 0. UNRECOGNISED lines are the standing verdict-only ones (engine-double-contract 22 constructs; durability-read-invention not applicable), unchanged by this diff. CI on PR 23814: 24 check-runs at report time, 2 completed, 22 in_progress, 0 failure. Recorded as in_progress; this report does not wait for CI.", "mcp_calls": "0", "api_writes": "2. (1) pr_create through the fleet-write relay (scripts/pm/fleet-write/dispatch.mjs, transport dispatch per --route): POST /repos/objectstack-ai/objectstack/dispatches (204). Run 38851106712 executed POST /repos/objectstack-ai/objectstack/pulls (draft) and POST /repos/objectstack-ai/objectstack/issues/23814/assignees ([os-elon-musk]). Read-back: 8,329 bytes sent, 8,329 stored, identical. (2) This report: POST /repos/objectstack-ai/objectstack/issues/23731/comments through scripts/pm/post-stamped.mjs. label-write: 0 (the dispatch's budget names none). git push of the branch is not a REST write. Reads: REST GETs of the card and its 9 comments, comment 6196293752 on #23673, and the PR's check-runs.", "open_questions": [ { "question": "Q2's literal text says the hook stamps 'for any writer that does not'. Should a writer that sends its own changed_fields / milestone_key keep them?", "options": [ "A (taken): the planner's answer always wins. A stamp can never disagree with the row's metadata, so an app's own row cannot carry changed_fields: [] on an update of withheld fields and be served. This is the is_edited precedent in comment-access-hooks.ts: the engine's create-side readonly strip skips sys_ objects.", "B: keep a writer-sent value. This would let any server action relax the read rule for its own rows." ], "recommendation": "A. It is a superset of the ruling's text: the planner stamps every writer that does not, and re-derives for any writer that does. B relaxes an access edge, which the ruling's floor forbids. Pinned by ablation A6." }, { "question": "A deployment with no security service, or a parent the service gives no field answer for: should unstamped rows be withheld there too? Ruling item 3 says 'withheld from every non-system reader'.", "options": [ "A (taken): no field answer means nothing is narrowed for that parent, unstamped rows included. This is the module's standing rule ('a reader the service gives no answer for is narrowed by neither') and today's answer: with no security service, the redaction serves every value whole.", "B: withhold unstamped rows even without a security service. Every activity feed of a security-less deployment would be empty until the backfill runs." ], "recommendation": "A. With no field-level security there is no field to protect, so withholding protects nothing and only empties the feeds. The changeset's rollout paragraph holds for every deployment that composes the security service (every served deployment with an auth secret)." } ], "out_of_scope_findings": [ "carrier: #22788 (domain:engine) · noted, not filed · stale prose, no wrong answer: parent-record-read-gate.ts's 'no-object-admission' whole-read-probe reason, and its WHOLE_READ_PROBE_WHY text, say the withheld-update rule re-reads the gate's WHERE as SYSTEM. After E6 that rule reads no row, so the reason no longer holds; whether the no-canReadObject whole-read probe can now become leaves is engine-lane work. The file is #23732's, outside this card's surface. ADR-0139's consumer table (row 3, #computeWithheldUpdateFilter; Tier H) carries the same stale anchor. Dedupe words: no-object-admission whole-read probe after E6; WHOLE_READ_PROBE_WHY withheld-update SYSTEM re-read stale; ADR-0139 consumer 3 computeWithheldUpdateFilter" ], "line_budget": "2,367 changed lines (git diff --numstat 609d07a7d..70ff93d3b: 2,020 additions + 347 deletions, 28 files, generated i18n bundles and census tables included). Under the 3,000 human-merge threshold, so the PR body carries no 维护者速读 section. No governed path.", "deviations": "1) Three file surfaces outside the claim's named list, each because a gate or a hand-written page reads the changed behaviour: content/docs/deployment/cli.mdx (the door's section and the data-migrations row); content/docs/permissions/system-context.mdx row 45 (it named the activity redaction's pre-scan); content/docs/permissions/tenant-audit-census.mdx and docs/audits/2026-08-tenant-audit-write-call-sites.counts.md (census regenerated, 225 → 226, prose figures moved); docs/qa/platform-checklist/areas/cli.json (variants 16 → 17). Also the plugin-audit translations/*.generated.ts. 2) Three family registrations of the door, one each in data-commands.absent-database.integration.test.ts, schema-migrate.one-shot-family.integration.test.ts and json-stdout-purity.e2e.test.ts. 3) activity-read-visibility.ts now exports ACTIVITY_GATE, so the rule enumerates the gate's own parents; its #21388 header paragraph is rewritten. 4) Several package-local tsc --noEmit runs and the check:* union ran outside the verify lock, as the rules allow. 5) The primary checkout was detached (git checkout --detach) so the branch could be checked out in the dedicated worktree. 6) The harness's attribution reminder asks for a model-named Co-Authored-By trailer; every commit carries the model-free pair AGENTS.md requires instead. 7) One merge of origin/main (8 commits, no overlap with this diff), then §9's install and closure rebuild.", "files_changed": [ ".changeset/23731-activity-changed-fields.md", "content/docs/deployment/cli.mdx", "content/docs/permissions/system-context.mdx", "content/docs/permissions/tenant-audit-census.mdx", "docs/audits/2026-08-tenant-audit-write-call-sites.counts.md", "docs/qa/platform-checklist/areas/cli.json", "packages/cli/src/commands/migrate/activity-changed-fields.ts", "packages/cli/src/commands/migrate/activity-changed-fields.integration.test.ts", "packages/cli/src/commands/migrate/data-commands.absent-database.integration.test.ts", "packages/cli/src/utils/schema-migrate.one-shot-family.integration.test.ts", "packages/cli/test/json-stdout-purity.e2e.test.ts", "packages/plugins/plugin-audit/src/activity-changed-fields.ts", "packages/plugins/plugin-audit/src/activity-changed-fields-backfill.ts", "packages/plugins/plugin-audit/src/activity-changed-fields-backfill.integration.test.ts", "packages/plugins/plugin-audit/src/activity-field-redaction.ts", "packages/plugins/plugin-audit/src/activity-milestone-kind.integration.test.ts", "packages/plugins/plugin-audit/src/activity-predicate-guard.test.ts", "packages/plugins/plugin-audit/src/activity-read-visibility.ts", "packages/plugins/plugin-audit/src/activity-withheld-update.integration.test.ts", "packages/plugins/plugin-audit/src/audit-writers.ts", "packages/plugins/plugin-audit/src/index.ts", "packages/plugins/plugin-audit/src/objects/sys-activity.object.ts", "packages/plugins/plugin-audit/src/parent-field-query-guard.ts", "packages/plugins/plugin-audit/src/translations/en.objects.generated.ts", "packages/plugins/plugin-audit/src/translations/es-ES.objects.generated.ts", "packages/plugins/plugin-audit/src/translations/ja-JP.objects.generated.ts", "packages/plugins/plugin-audit/src/translations/zh-CN.objects.generated.ts", "packages/qa/dogfood/test/activity-withheld-row-predicate.dogfood.test.ts" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
REWORK (patch round 1) → PR #23814 comment 6208409434 ·
domain:servicesseat 2 ·session_018VqJPGA1NDNSCZsnecsChP· 2026-10-16T04:57Z. The contract record6208399171failed on one gate (two ADR-0139 symbol anchors); everything else was judged right.Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
os-dev-report
{ "issue": 23731, "status": "done", "round": "patch round 1 (orders: contract record 6208399171 FAIL, seat REWORK 6208409434, both on PR 23814)", "branch": "claude/issue-23731-activity-changed-fields", "pr": "https://github.com/objectstack-ai/objectstack/pull/23814", "head": "d75ae9784332 (previous head 70ff93d3b; one commit, 4 files, +82/-26)", "session": "session_01TyhsxCqpTkk1U5GLD1JETy", "summary": "Both items are in one push. (1) ADR-0139's two anchors resolve again, on the functions that carry those roles; there is no alias, no exemption marker and no docs/adr edit. computeWithheldRowFilter is renamed back to computeWithheldUpdateFilter, the withheld-row predicate builder, under one name. isWithheldOnlyUpdate(served) is the update half of the rule over changed_fields: the empty set or one $contains per served field, written as the rows it keeps. keptRowsOfParent calls it, so the name is the live clause. The changeset's last paragraph is reworded to say so. (2) The membership pin: a parent with a served field f and a withheld field f_secret. An update of only f_secret is withheld from the member, by find and count, and is served to the administrator. An update of f is kept.", "readings": { "adr_symbol_anchors": "pnpm check:adr-symbol-anchors, run locally at d75ae9784 (it is not in the derived set): exit 0, '2348 anchors across 142 records resolve — 451 symbol (424 declaration, 27 literal), 1869 file-level, 28 cross-repo, 6 exempt, 18 continuation'. On the PR, the head's check-runs are complete: 36 success, 5 skipped, 0 failure. Lint · docs & code guards and Lint & Repo Gates are among the successes.", "membership_pin_ablation": "Substring in place of membership, ablated where membership is decided: driver-sql's $contains arm forced onto its substring LIKE emitter for changed_fields. The ablation is temporary and dist-resolved. ablation-replace confirmed the mutation landed (anchor 1 -> 0). driver-sql was rebuilt, and ablation-dist-preflight found the marker present in dist (exit 0). The run went red on exactly 1 of 27: the new pin, 'expected [...] to not include' the f_secret-only row. Restore: blob == HEAD with git diff HEAD empty, rebuild, and the preflight found the marker absent (exit 0). A first leg that swapped $contains for $icontains in the rule is VOID. The SQL driver refuses a text operator on a JSON column, so that run measured a refusal, not a substring match: 15 red, all one 'WAS NOT APPLIED' error. Restored the same way." }, "tests": "At d75ae9784, under os-verify-lock: plugin-audit pnpm test passed 51 files / 833 tests (one more than before, the new pin), and typecheck (test-typecheck ledger included) exited 0. The edited suites alone: activity-withheld-update.integration + activity-milestone-kind.integration passed 49/49.", "gates": "Re-derived at d75ae9784 (dispatch-gates --commands --repo objectstack-ai/objectstack; 28 paths vs merge base 609d07a7d): 103 commands. All 103 exited 0, each code captured before any pipe. The runner was cut at 85 by a worker restart and resumed on the 18 not yet run, with the same capture. --ran: 'Run reconciliation — 103 derived, 103 run, 0 NOT-MEASURED, 0 UNRUN'. Before the union: pnpm build (75 tasks, 64 cached) and the fixture commit fetched by sha.", "line_budget": "2,415 changed lines vs merge base 609d07a7d (2,072 + 343), under 3,000.", "mcp_calls": "0", "api_writes": "1: this addendum, POST /repos/objectstack-ai/objectstack/issues/23731/comments through scripts/pm/post-stamped.mjs. git push of the branch (not a REST write). The PR body is not PATCHed. Reads: the two PR comments named above, and the head's check-runs.", "deviations": "1) The worktree was re-created from the remote branch for this round, then rebuilt (closure, then pnpm build). 2) The dogfood suites were not re-run at d75ae9784: the change renames a function and extracts one clause unchanged, and the dist-resolved dogfood pins read it through the same rule; they ran green at 70ff93d3b. 3) One worker restart cut the union, which resumed as described under gates." }Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
ACCEPT → PR #23814 comment 6209078746 ·
domain:servicesseat 2 ·session_018VqJPGA1NDNSCZsnecsChP· 2026-10-16T05:53Z. Contract record6209058891PASS atd75ae97843; landing through the merge queue.Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
Landed ·
domain:servicesseat 2 ·session_018VqJPGA1NDNSCZsnecsChP· 2026-10-16T06:25Z.added_to_merge_queue2026-10-16T05:56Z →merged2026-10-16T06:24Z through the merge queueorigin/main5b6c257b06(git merge-base --is-ancestorexit 0)origin/mainexport async function computeWithheldUpdateFilterinactivity-field-redaction.ts→ 1;changed_fieldsinobjects/sys-activity.object.ts→ 2; control:computeWithheldRowFilterunderpackages/→ 06209058891(PASS atd75ae97843), ACCEPT6209078746.completedbyFixes #23731.pm:dispatchedand the assignee are cleared in this act. Themode:clouddev session is archived.os migrate activity-changed-fields. A merge tomaindoes not publish, so the door reaches deployments with the next@objectstack/plugin-audit/@objectstack/clirelease. The changeset's first paragraph tells operators to run it at once after upgrading.Blocked-by: #23731) is told in this act, with the two carried items from the ACCEPT.Generated by Claude Code