Skip to content

Commit 70ff93d

Browse files
committed
chore: i18n for the two activity columns; tenant-audit census for the backfill's write site; system-context page
Claude-Session: https://claude.ai/code/session_01TyhsxCqpTkk1U5GLD1JETy Co-authored-by: Claude <noreply@anthropic.com>
1 parent c53c594 commit 70ff93d

7 files changed

Lines changed: 61 additions & 28 deletions

File tree

‎content/docs/permissions/system-context.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ One of the largest consumers of the flag;
166166
| 42 | Delegation write guard bypassed | plugin-approvals | Get: service / seed / import may write delegation rows naming another delegator | `packages/plugins/plugin-approvals/src/lifecycle-hooks.ts#bindDelegationWriteGuard` |
167167
| 43 | Approval actor / submitter / pending-approver checks bypassed (8 sites) | plugin-approvals | Get: approve, reject, recall, reassign without being a pending approver or the submitter | `packages/plugins/plugin-approvals/src/approval-service.ts#isOverrideActor`, `#resolveActor`, `#sendBack`, `#resubmit`, `#reassign`, `#remind`, `#requestInfo`, `#comment` |
168168
| 44 | Attachment access hooks return early (insert + update + delete, and the read AST) | service-storage | Lose: attachment visibility scoping | `packages/services/service-storage/src/attachment-access-hooks.ts#installAttachmentAccessHooks`, `#installAttachmentReadVisibility` |
169-
| 45 | Comment access hooks return early (insert + update + delete, and the read AST), and so do the activity and audit-log read gates (their read AST), the activity field redaction and the audit-log field redaction (the rows a read serves), and the query guard over both objects' value-bearing columns (the read AST) | plugin-audit | Get: the whole activity row and the whole `sys_audit_log` row, its before/after snapshots included, on `find` / `findOne` — the audit writer, each read gate's own pre-scan and the activity redaction's — and a filter, sort or grouping by those columns. Lose: comment visibility scoping, the narrowing of `sys_activity` and of `sys_audit_log` to rows whose parent record the caller can read, the redaction of a parent field's value from an activity row's text and recorded change and from a ledger row's before/after snapshots, the withholding of an activity row recording an update whose every changed field the reader is withheld (on `count` and `aggregate` too, so a total agrees with the rows), and the refusal of a query over those columns for a reader withheld a field of the objects it can reach | `packages/plugins/plugin-audit/src/comment-access-hooks.ts#installCommentAccessHooks`, `#installCommentReadVisibility`, `packages/plugins/plugin-audit/src/activity-read-visibility.ts#installActivityReadVisibility`, `packages/plugins/plugin-audit/src/audit-log-read-visibility.ts#installAuditLogReadVisibility`, `packages/plugins/plugin-audit/src/activity-field-redaction.ts#installActivityFieldRedaction`, `packages/plugins/plugin-audit/src/audit-log-field-redaction.ts#installAuditLogFieldRedaction`, `packages/plugins/plugin-audit/src/parent-field-query-guard.ts#installParentFieldQueryGuard` |
169+
| 45 | Comment access hooks return early (insert + update + delete, and the read AST), and so do the activity and audit-log read gates (their read AST), the activity field redaction and the audit-log field redaction (the rows a read serves), and the query guard over both objects' value-bearing columns (the read AST) | plugin-audit | Get: the whole activity row and the whole `sys_audit_log` row, its before/after snapshots included, on `find` / `findOne` — the audit writer, each read gate's bounded probe and the activity backfill — and a filter, sort or grouping by those columns. Lose: comment visibility scoping, the narrowing of `sys_activity` and of `sys_audit_log` to rows whose parent record the caller can read, the redaction of a parent field's value from an activity row's text and recorded change and from a ledger row's before/after snapshots, the withholding of an activity row recording an update whose every changed field the reader is withheld, of a fired milestone row whose watched fields it is not all served, and of an unstamped row (on `count` and `aggregate` too, so a total agrees with the rows), and the refusal of a query over those columns for a reader withheld a field of the objects it can reach | `packages/plugins/plugin-audit/src/comment-access-hooks.ts#installCommentAccessHooks`, `#installCommentReadVisibility`, `packages/plugins/plugin-audit/src/activity-read-visibility.ts#installActivityReadVisibility`, `packages/plugins/plugin-audit/src/audit-log-read-visibility.ts#installAuditLogReadVisibility`, `packages/plugins/plugin-audit/src/activity-field-redaction.ts#installActivityFieldRedaction`, `packages/plugins/plugin-audit/src/audit-log-field-redaction.ts#installAuditLogFieldRedaction`, `packages/plugins/plugin-audit/src/parent-field-query-guard.ts#installParentFieldQueryGuard` |
170170
| 46 | Knowledge search returns hits unfiltered | service-knowledge | Lose: the permission filter over search results | `packages/services/service-knowledge/src/knowledge-service.ts#applyPermissionFilter` |
171171
| 47 | The inbound-mail read gates return early (the read AST) | service-mailbox | Get: every `sys_mail_message` and `sys_mail_association` row on `find` / `findOne` — the ingest writer and each gate's own pre-scan, which read the association and mailbox rows a decision needs. Lose: the narrowing of a message to callers who can read a record it is associated with or who own its mailbox (so the unassociated pool is restricted), and of an association to callers who can read the record it names (on `count` and `aggregate` too, so a total agrees with the rows) | `packages/services/service-mailbox/src/mail-read-visibility.ts#gateMiddleware` |
172172
| 47b | The inbound-mail ingest seam: `ingest` admits the system context ALONE, and `rematch` admits it without the organization, owner or `manage_mailboxes` check | service-mailbox | Get: a mail source hands a mailbox its messages, and server code re-matches any mailbox. Lose: nothing a user held — every other caller of `ingest` is refused (`PERMISSION_DENIED`), because an ingested message is shown on every record its correspondents' addresses match and a user who could hand the seam a message could write one in a correspondent's name; every other caller of `rematch` must be in the mailbox's organization and own the mailbox or hold `manage_mailboxes` | `packages/services/service-mailbox/src/ingest/mail-ingest-service.ts#ingest`, `#authorizeRematch` |

‎content/docs/permissions/tenant-audit-census.mdx‎

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ are reported as `undecidable` rather than assumed either way.
122122

123123
The same holds twice over for the context. An options argument spelled as a
124124
literal can be read; one spelled `options`, `{ ...opts }`, or handed through a
125-
forwarding shim cannot, and **53 of the 225 sites are spelled that way**. A
125+
forwarding shim cannot, and **53 of the 226 sites are spelled that way**. A
126126
context resolved from an inline literal or a local `const` can be tested for
127127
`isSystem`; one arriving from a helper call cannot.
128128

@@ -187,10 +187,10 @@ reproduce them. Where it disagrees, it disagrees on the page:
187187

188188
| carried figure | where it survives | this census |
189189
| :--- | :--- | ---: |
190-
| 175 write call sites | quoted in the merged changeset | **225** |
190+
| 175 write call sites | quoted in the merged changeset | **226** |
191191
| 24 carrying no tenant context | quoted in the merged changeset | **2** provable and tenancy-enabled; **32** more whose options argument is unreadable |
192-
| 127 of 175 statically decidable, 48 runtime-parameter-name sites | restated on the `isSystem`-scoping card | **154 of 225** decidable, **71** undecidable |
193-
| 135 (77%) silenced by the `isSystem` guard before the posture gate | the lost issue body — **no surviving corroboration** | **not reproduced**: 121 decidably elevated, 0 decidably not, 96 undecidable |
192+
| 127 of 175 statically decidable, 48 runtime-parameter-name sites | restated on the `isSystem`-scoping card | **155 of 226** decidable, **71** undecidable |
193+
| 135 (77%) silenced by the `isSystem` guard before the posture gate | the lost issue body — **no surviving corroboration** | **not reproduced**: 122 decidably elevated, 0 decidably not, 96 undecidable |
194194
| 141 and 132, two independent re-derivations | the card that filed this work | — |
195195

196196
**The differences are not reconciled, and deliberately so.** The old census's
@@ -201,17 +201,17 @@ at any commit.
201201

202202
Two structural facts do plausibly widen this reading against any hand or regex
203203
one, and both are counted in the generated tables below: the 38 sites reached
204-
through an erased (`any`) receiver, and the 50 that name their object through a
204+
through an erased (`any`) receiver, and the 51 that name their object through a
205205
`const` rather than inline. An instrument that read either the way a person does
206206
would report a smaller number and would not say so.
207207

208208
The fourth row is the one worth flagging to anyone citing it. **The 135 / 77%
209209
figure has no surviving corroboration anywhere in the tree.** This census reads
210-
121 of 225 (54%) as decidably elevated, with 96 more whose elevation is a
210+
122 of 226 (54%) as decidably elevated, with 96 more whose elevation is a
211211
run-time fact — so the claim is neither confirmed nor refuted, and the honest
212212
answer is that a static reading cannot settle it.
213213

214-
⇒ **Cite `2 / 225`, and say what it is**: the sites whose options argument was
214+
⇒ **Cite `2 / 226`, and say what it is**: the sites whose options argument was
215215
READ and holds no tenant context, against a decidably tenancy-enabled object.
216216
That is the control's provable yield surface. ⛔ Do not cite it as "the sites
217217
without tenant context" — **32 further sites** have an options argument this
@@ -223,29 +223,29 @@ cannot read, and they are neither in nor out.
223223

224224
| what | count |
225225
| :--- | ---: |
226-
| write call sites on the application surface | **225** |
227-
| …whose object name is statically decidable | 154 |
226+
| write call sites on the application surface | **226** |
227+
| …whose object name is statically decidable | 155 |
228228
| …whose object name is chosen at run time | 71 |
229-
| …against an object with tenancy ENABLED | 153 |
229+
| …against an object with tenancy ENABLED | 154 |
230230
| …against an object that declares tenancy off | 1 |
231-
| threading a tenant context | 164 |
231+
| threading a tenant context | 165 |
232232
| PROVABLY carrying none (options read, no context key) | **8** |
233233
| …of those, against a decidably tenancy-enabled object | **2** |
234234
| options argument UNREADABLE — may or may not carry one | 53 |
235235
| …of those, against a decidably tenancy-enabled object | 32 |
236-
| threading a decidably ELEVATED (`isSystem`) context | 121 |
236+
| threading a decidably ELEVATED (`isSystem`) context | 122 |
237237
| threading a context that is decidably NOT elevated | 0 |
238238
| threading a context whose elevation is a run-time fact | 96 |
239239

240240
| how the instrument reached the site | count |
241241
| :--- | ---: |
242-
| receiver carried a readable engine type | 187 |
242+
| receiver carried a readable engine type | 188 |
243243
| receiver erased, placed by the object NAME | 28 |
244244
| receiver erased, placed by an `object: string` PARAMETER | 5 |
245245
| receiver erased, placed by an `UNTYPED_RECEIVERS` row | 5 |
246246

247247
| object name spelled inline | 104 |
248-
| object name spelled through a `const` | 50 |
248+
| object name spelled through a `const` | 51 |
249249
| object name is an `object: string` parameter | 7 |
250250
| object name is some other run-time expression | 64 |
251251

@@ -297,13 +297,13 @@ holds still. They are required to be HERE and to say WHEN they were true;
297297
their values are not compared. The reasoning, and the measurement behind it,
298298
are in `scripts/check-tenant-audit-census.mjs`.
299299

300-
Measured on 2026-10-14 at `95898bba1`.
300+
Measured on 2026-10-16 at `c53c594e7`.
301301

302302
| corpus scale (not enforced) | count |
303303
| :--- | ---: |
304-
| tracked non-test sources scanned | 649 |
305-
| engine-shaped types recognised | 75 |
304+
| tracked non-test sources scanned | 654 |
305+
| engine-shaped types recognised | 77 |
306306
| declared objects in the registry | 116 |
307-
| same-named calls subtracted as non-engine | 164 |
307+
| same-named calls subtracted as non-engine | 165 |
308308

309309
{/* END GENERATED: tenant-audit-census */}

‎docs/audits/2026-08-tenant-audit-write-call-sites.counts.md‎

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,17 +33,17 @@ silent, and `node scripts/tenant-audit-census.mjs --write` is the resolution.
3333

3434
| Measure | Value |
3535
|---|---:|
36-
| Write call sites | 225 |
37-
| Object name statically decidable | 154 |
36+
| Write call sites | 226 |
37+
| Object name statically decidable | 155 |
3838
| Object name chosen at run time | 71 |
39-
| Against a tenancy-enabled object | 153 |
39+
| Against a tenancy-enabled object | 154 |
4040
| Against an object declaring tenancy off | 1 |
41-
| Threading a tenant context | 164 |
41+
| Threading a tenant context | 165 |
4242
| Provably carrying none | 8 |
4343
| …and decidably tenancy-enabled | 2 |
4444
| Options argument unreadable | 53 |
4545
| …and decidably tenancy-enabled | 32 |
46-
| Threading a decidably elevated context | 121 |
46+
| Threading a decidably elevated context | 122 |
4747
| Threading a decidably non-elevated context | 0 |
4848
| Threading a context of undecidable elevation | 96 |
4949

@@ -90,14 +90,14 @@ holds still. They are required to be HERE and to say WHEN they were true;
9090
their values are not compared. The reasoning, and the measurement behind it,
9191
are in `scripts/check-tenant-audit-census.mjs`.
9292

93-
Measured on 2026-10-14 at `95898bba1`.
93+
Measured on 2026-10-16 at `c53c594e7`.
9494

9595
| corpus scale (not enforced) | count |
9696
| :--- | ---: |
97-
| tracked non-test sources scanned | 649 |
98-
| engine-shaped types recognised | 75 |
97+
| tracked non-test sources scanned | 654 |
98+
| engine-shaped types recognised | 77 |
9999
| declared objects in the registry | 116 |
100-
| same-named calls subtracted as non-engine | 164 |
100+
| same-named calls subtracted as non-engine | 165 |
101101

102102
## Every site
103103

@@ -115,6 +115,7 @@ Measured on 2026-10-14 at `95898bba1`.
115115
| `packages/plugins/plugin-approvals/src/approval-service.ts` | `insert` | `sys_approval_token` | enabled | elevated | 1 |
116116
| `packages/plugins/plugin-approvals/src/approval-service.ts` | `update` | `sys_approval_token` | enabled | elevated | 1 |
117117
| `packages/plugins/plugin-approvals/src/backfill-platform-row-organizations.ts` | `update` | `objectPlan.object` | undecidable | context, elevation undecidable | 1 |
118+
| `packages/plugins/plugin-audit/src/activity-changed-fields-backfill.ts` | `update` | `sys_activity` | enabled | elevated | 1 |
118119
| `packages/plugins/plugin-audit/src/auth-event-audit.ts` | `insert` | `sys_audit_log` | enabled | options unreadable | 1 |
119120
| `packages/plugins/plugin-audit/src/read-audit.ts` | `insert` | `sys_audit_log` | enabled | options unreadable | 1 |
120121
| `packages/plugins/plugin-audit/src/stored-metadata-body-migration.ts` | `update` | `object` | undecidable | elevated | 1 |

‎packages/plugins/plugin-audit/src/translations/en.objects.generated.ts‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,14 @@ export const enObjects: NonNullable<TranslationData['objects']> = {
181181
metadata: {
182182
label: "Metadata",
183183
help: "JSON-serialized additional context"
184+
},
185+
changed_fields: {
186+
label: "Changed Fields",
187+
help: "The key set of the recorded change: for an update, every field its recorded old/new values name; empty for every other row. Stamped by the platform at write time; served narrowed to the fields the reader is served. NULL means the row is not stamped yet (run `os migrate activity-changed-fields`)."
188+
},
189+
milestone_key: {
190+
label: "Milestone Key",
191+
help: "For a fired activity milestone, one sorted, comma-joined key of every field the object's activityMilestones declaration watched when it fired; NULL for every other row. Stamped by the platform at write time and never served: the read rule compares it with the current declaration."
184192
}
185193
}
186194
},

‎packages/plugins/plugin-audit/src/translations/es-ES.objects.generated.ts‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,14 @@ export const esESObjects: NonNullable<TranslationData['objects']> = {
181181
metadata: {
182182
label: "Metadatos",
183183
help: "Contexto adicional serializado en JSON."
184+
},
185+
changed_fields: {
186+
label: "Campos modificados",
187+
help: "El conjunto de claves del cambio registrado: para una actualización, cada campo que nombran sus valores antiguos/nuevos registrados; vacío para cualquier otra fila. La plataforma lo marca al escribir; se sirve reducido a los campos que se sirven al lector. NULL significa que la fila aún no está marcada (ejecute `os migrate activity-changed-fields`)."
188+
},
189+
milestone_key: {
190+
label: "Clave de hito",
191+
help: "Para un hito de actividad disparado, una clave ordenada y separada por comas de todos los campos que la declaración activityMilestones del objeto vigilaba al dispararse; NULL para cualquier otra fila. La plataforma la marca al escribir y nunca se sirve: la regla de lectura la compara con la declaración actual."
184192
}
185193
}
186194
},

‎packages/plugins/plugin-audit/src/translations/ja-JP.objects.generated.ts‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,14 @@ export const jaJPObjects: NonNullable<TranslationData['objects']> = {
181181
metadata: {
182182
label: "メタデータ",
183183
help: "JSON シリアライズされた追加コンテキスト"
184+
},
185+
changed_fields: {
186+
label: "変更フィールド",
187+
help: "記録された変更のキー集合:更新の場合は、記録された旧値/新値が含むすべてのフィールド。その他の行では空。書き込み時にプラットフォームが付与し、閲覧者に提供されるフィールドに絞り込んで返されます。NULL はその行がまだ付与されていないことを示します(`os migrate activity-changed-fields` を実行してください)。"
188+
},
189+
milestone_key: {
190+
label: "マイルストーンキー",
191+
help: "発火したアクティビティマイルストーンについて、発火時にオブジェクトの activityMilestones 宣言が監視していた全フィールドをソートしてカンマで連結したキー。その他の行では NULL。書き込み時にプラットフォームが付与し、決して返されません。読み取りルールが現在の宣言と比較します。"
184192
}
185193
}
186194
},

‎packages/plugins/plugin-audit/src/translations/zh-CN.objects.generated.ts‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,14 @@ export const zhCNObjects: NonNullable<TranslationData['objects']> = {
181181
metadata: {
182182
label: "元数据",
183183
help: "附加上下文的 JSON 序列化内容"
184+
},
185+
changed_fields: {
186+
label: "变更字段",
187+
help: "所记录变更的字段键集合:对于更新,为其记录的旧值/新值涉及的每个字段;其他行为空。由平台在写入时标记;仅返回读者有权查看的字段。NULL 表示该行尚未标记(请运行 `os migrate activity-changed-fields`)。"
188+
},
189+
milestone_key: {
190+
label: "里程碑键",
191+
help: "对于已触发的活动里程碑,为该对象 activityMilestones 声明在触发时所监视的全部字段的排序、逗号连接的键;其他行为 NULL。由平台在写入时标记且从不返回:读取规则会将其与当前声明进行比较。"
184192
}
185193
}
186194
},

0 commit comments

Comments
 (0)