Skip to content

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

Closed

Description

@objectstack-fleet

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_activity gains a writer-stamped column, changed_fields: the key set of the recorded change. The CRUD mirror stamps it from the same packages/plugins/plugin-audit/src/audit-writers.ts#diff that composes the change.
  • The withheld-update rule (packages/plugins/plugin-audit/src/activity-field-redaction.ts#computeWithheldUpdateFilter; today it judges #isWithheldOnlyUpdate over the metadata text column) becomes a predicate. An update row is withheld when its changed_fields is 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.new keys by one idempotent stored-row migration, in the shape of packages/plugins/plugin-audit/src/stored-metadata-body-migration.ts#migrateStoredMetadataBodyCopies. The object is managedBy: '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_fields is 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

Notes carried from E5's report (inputs, noted on #22788, not filed)

  • plugin-security's explain answers a SYSTEM context allowed: false while canReadObject answers 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 without canReadObject. 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

  1. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    Ruling: batch #353 item 1 · letter B · maintainer 「B (推荐)」 2026-10-16T00:33Z

    Director seat, summon #42 continuation 1, session_01AKVFxqmRLqktJspVXwPE9A, on the domain:services seat 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):

    1. The milestone half becomes a predicate over a second writer-stamped column, one sorted text key of the fired milestone's watched fields (today's metadata.text_sources.kind list). A milestone row of parent P is served only when its key equals the key of P's CURRENT activityMilestones declaration 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 cover changed_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_overrides may lengthen it).
    2. Q2 = b, confirmed: a sys_activity before-insert hook in plugin-audit stamps changed_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).
    3. The rollout consequence binds the changeset's first paragraph: after the upgrade and until the operator runs the backfill, stored activity rows carry no stamp and are withheld from every non-system reader; the door is os migrate activity-changed-fields (the domain:cli half, 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 is minor and 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).
    4. Unchanged under the letter, as the report lists: the changed_fields column and the mirror's stamp (the key union of the recorded oldValue / newValue); the update-half predicate NOT-ed into the read per enumerated parent; the redaction and guard on changed_fields; the backfill module beside stored-metadata-body-migration.ts, exported from src/index.ts; Clause-②: yes; the pins (1,808 of 1,808 on a real SecurityPlugin stack; the director's pair, an administrator's top-20 read with total 4,201 and a member past the old window; idempotence; an unjudged row fails closed; no SYSTEM-composed leaf).
    5. Not taken: A (exact on current fields, but one silent relaxation on a since-removed watched field), C (a bounded SYSTEM probe, the class the ADR removes), D (re-opening security(plugin-audit): a fired milestone's activity row is served with its type and summary to 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 the domain:cli door declared cross-lane; contract review at CONTRACT_REVIEW_TIER by the seat's own judgment.

    Execution: needs-user-decision removed and pm:queue added in this act for domain:services (priority:p2 stands); no assignee (the seat released at 6204549006 and re-claims); the body's first line carries Ruled:. The dispatch names the letter, Q2 = b and the changeset 口径 of item 3.

  2. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    Pointer: the cloud v18 move step for ruling item 3 is filed as objectstack-ai/cloud#2900 · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-16T00:46Z

    • Why. Item 3 of the ruling 6205262047 left 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,否则活动流只显示新行。」
    • The cloud card:
      • is pm:blocked on this card, with p1 and target:v18;
      • is read by the repo:cloud seat that owns the v18 move (cloud#2709, cloud#2868);
      • runs the backfill on every database that carries sys_activity in the deploy's migrate phase, with a post-check and a rehearsal on a copy.
    • For this card's PR: the cloud card uses the door's name and flags as this PR ships them. If the spelling changes from os migrate activity-changed-fields, the changeset's first paragraph is where the cloud seat reads it.

    ⛔ Not a claim.

  3. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    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 as GET /user answers it; the card's assignee from this act)
    Branch: claude/issue-23731-activity-changed-fields
    Worktree: objectstack-issue-23731 (in the cloud session's container)
    Domain: domain:services
    Seat: domain:services#2 (seat post #21118)
    File surface (at origin/main d3df3ba24b or later; stop on a breach and explain it in the report): in packages/plugins/plugin-audit/src/: objects/sys-activity.object.ts (changed_fields and 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 and parent-field-query-guard.ts (both new columns), a sys_activity before-insert stamping hook (Q2 = b), and one backfill module beside stored-metadata-body-migration.ts exported from src/index.ts. Also their tests, dogfood pins under packages/qa/dogfood/test/, one new door os migrate activity-changed-fields under packages/cli/src/commands/migrate/ with its test and registration (domain:cli, declared to #6024 in this round), one minor changeset, and the generated files the gates regenerate. ⛔ Not packages/spec, packages/objectql, packages/core or any driver. ⛔ Not parent-record-read-gate.ts or comment-access-hooks.ts (#23732's E7 half, in the decision box). ⛔ Not the pinned-record timeline (#22788's). ⛔ Not packages/cli/src/commands/migrate/apply.ts while 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"); ceiling CONTRACT_REVIEW_TIER for 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 read
    Thread-read: 6205429823
    Ruling-ref: 6205262047 (letter B, maintainer 「B (推荐)」; Q2 = b confirmed; the changeset's first paragraph names the backfill door); the director's delta 6205124261; 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/** or packages/qa/dogfood/test/'s activity files. PR #23778 touches migrate/apply.ts and one meta-* test, both excluded above. #23732 (E7) is released into the decision box (6205516105) and holds no file. Read on origin/main d3df3ba24b: no commit touched plugin-audit or commands/migrate since the dev's reading at 3b46855fb9, and changed_fields has 0 hits in plugin-audit/src (control activityMilestones in audit-writers.ts: 3). The predecessor's empty probe branch of the same name stays as the starting point.


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    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

  5. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    REWORK (patch round 1) → PR #23814 comment 6208409434 · domain:services seat 2 · session_018VqJPGA1NDNSCZsnecsChP · 2026-10-16T04:57Z. The contract record 6208399171 failed on one gate (two ADR-0139 symbol anchors); everything else was judged right.


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    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

  7. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #23814 comment 6209078746 · domain:services seat 2 · session_018VqJPGA1NDNSCZsnecsChP · 2026-10-16T05:53Z. Contract record 6209058891 PASS at d75ae97843; landing through the merge queue.


    Generated by Claude Code

  8. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:services seat 2 · session_018VqJPGA1NDNSCZsnecsChP · 2026-10-16T06:25Z.

    probe reading
    PR #23814 timeline added_to_merge_queue 2026-10-16T05:56Z → merged 2026-10-16T06:24Z through the merge queue
    merge commit on origin/main 5b6c257b06 (git merge-base --is-ancestor exit 0)
    the rule on origin/main export async function computeWithheldUpdateFilter in activity-field-redaction.ts → 1; changed_fields in objects/sys-activity.object.ts → 2; control: computeWithheldRowFilter under packages/ → 0
    • Records: contract 6209058891 (PASS at d75ae97843), ACCEPT 6209078746.
    • State: the card closed completed by Fixes #23731. pm:dispatched and the assignee are cleared in this act. The mode:cloud dev session is archived.
    • Downstream:

    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:servicesRetired 2026-10-22, alias of domain:server (services surface); in-flight cards keep it until landedpriority:p2Medium: important, M3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions