Repository navigation
Commit b12e16a
feat(service-settings)!: the settings audit trail's global rung moves to a deployment-level sibling, sys_platform_setting_audit (18.x step 2, expand-first) (#24590)
Closes #24530
Clause-②: yes (narrowing)
**18.x line, step 2 (8b-0e′), expand-first. This is PR 2a only.**
- Ruling record 6257510973 (#24377, Q4 A).
- Ruling records 6266750393 and 6267391172 on #15194 (the line).
- The epic seat's binding scope correction, from step 0's measured plan
(os-dev-report 6268559156 on #24522).
The `sys_secret` / `sys_flow_credential` flip (ruling record 6259141885,
#24435 Q1 A, for the latter) is **not in this PR**. It moves to step 6,
with the opened ceremony; see "Measured" below.
⛔ **Merge gate.** This PR stays a draft until #24522's upgrade harness
(#24552) has merged. Then it merges `main`, runs the harness, and lands
only when R1–R4 hold with the harness green.
## What changes
1. **New `sys_platform_setting_audit`**, the settings audit trail's
global rung, created fresh:
- `systemFields: { tenant: false }`: no organization column;
- `requiredPermissions: ['manage_platform_settings']`, as
`sys_platform_setting` has;
- `managedBy: 'engine-owned'`, with reads only on the data API;
- the same row shape as `sys_setting_audit`, with `scope` always
`global`.
It registers with service-settings (the manifest), joins
`SETTINGS_READ_DOOR_OBJECTS` (so each namespace's `readPermission`
applies there too), and joins `PLATFORM_OBJECTS_BY_PACKAGE`.
2. **The writer routes by rung** (`buildSettingAuditWriter`, through one
predicate, `settingAuditObjectFor`):
- `scope === 'global'` goes to the sibling, with no organization;
- tenant and user rows stay in `sys_setting_audit`, stamped as before;
- each insert names its table literally, so the tenant-audit census
keeps every write site statically decidable.
3. **Regenerated with the repo's tooling:**
- `scripts/platform-object-tenancy-census.json`: the sibling is added,
out of reach because `systemFields.tenant` is `false`;
- the tenant-audit census page and counts: one more write call site,
with the hand-written figures the gate holds updated to match.
4. **Inventory:** the sibling's row, placed next to
`sys_setting_audit`'s (`column-drop`, no column).
5. **Platform checklist:** two items that read the global rung's audit
rows from `sys_setting_audit` are re-pointed, each with a revision bump
and a history entry.
## Measured: why `sys_secret` and `sys_flow_credential` are untouched
**Measured:** the 18.0 DDL for the two tables, populated with
18.0-shaped rows, then the engine's schema sync and the v18 boot guard's
own predicate.
| declared shape | boot guard (`findV17ShapeTables`) | drift
(`detectManagedDrift`) |
|:--|:--|:--|
| `systemFields: { tenant: false }`, `organization_id` not declared |
**refuses**: `["sys_flow_credential","sys_secret"]` | `drop_column`,
`destructive`, on both tables |
| `tenancy: { enabled: false }`, `organization_id` kept declared | `[]`
| `[]` |
The first row refuses every 18.0 database at boot, which breaks R2. The
second escapes the guard, and the epic seat rules that escape a gap, not
a route. The kept ceremony class would also block its `column-drops`
step on a column the runtime still declares: measured on
`organization-ownership-boot-refusal-order.dogfood.test.ts` when the two
objects' inventory fates were moved, and reverted. So both objects ship
as 18.0 shipped them, and their flip lands with step 6.
## 18.0 rows
- Nothing is moved, deleted or re-stamped, and no column is dropped.
- An 18.0 install wrote its global-rung audit rows into
`sys_setting_audit`, **stamped with the Default Organization**. They
stay there, readable through the data API as before.
- The global rung's history is the sibling **plus** `sys_setting_audit`
rows **selected by `scope = 'global'`**, never by a NULL organization.
- There is no in-process history reader to union today: the only readers
are the two data-API grids behind the settings read door. Both serve
their rows, as the dogfood pin shows.
## The step-5/6 ceremony owes
| table | owed |
|:--|:--|
| `sys_setting_audit` | move the `scope = 'global'` rows to
`sys_platform_setting_audit`: an inventory `departures` entry, plus a
`MOVES` entry in `organization-ownership-apply.ts` like `sys_setting`'s
`global-rung-move`. Then `scope`'s `global` option can retire, and 8e's
NOT NULL applies to the rest. |
| `sys_secret` | the column-less flip with step 6: the declaration goes
to `systemFields: { tenant: false }`, the inventory fate goes 3 →
`column-drop`, and the `column-drops` step drops `organization_id`. The
settings writer stops stamping it. |
| `sys_flow_credential` | the same flip with step 6, plus
`column-drops`. |
| `sys_platform_setting_audit` | nothing. |
Step 7 removes the dual read: once the move has run, the global rung's
history is the sibling alone.
## Proof
- **R1:** a fresh `pnpm dev -- --fresh` showcase boot.
- `GET /api/v1/discovery` answered 200.
- `PUT /api/settings/ai` (encrypted, global) and `PUT
/api/settings/branding` (tenant) answered 200.
- `/data/sys_platform_setting_audit` lists the global rows with no
organization, and `/data/sys_setting_audit` the tenant rows, stamped.
- **R2:**
- The pins below, including an 18.0-shaped global audit row booted over
with the ownership guard composed.
- #24522's harness at branch head `f6128e3`, run locally on this branch:
5/5 green (its files are not committed here).
- **R3:** the same harness run's v17 case is refused with the guard's
code.
- **R4:** `OS_TENANCY_POSTURE=isolated pnpm dev -- --fresh` exits 1 with
`tenancy posture 'isolated' is not supported in 18.0 (NOT_IMPLEMENTED)`.
**New pins:**
- `service-settings/src/settings-deployment-level-expand.test.ts`: a
real engine under `single` with a live D9-stamp control. The new write
shape, and the dual read on an 18.0-shaped row.
- `service-settings/src/settings-secret-organization.test.ts`: per-rung
routing through the one predicate.
- `qa/dogfood/test/settings-audit-global-rung-sibling.dogfood.test.ts`:
two showcase boots over one file, the second with an 18.0-shaped stamped
global audit row.
- The sibling also joins the D7 family pin, the read-door pin and the
manifest pin.
**Ablation X6:** the routing predicate was pinned to
`sys_setting_audit`, via `scripts/ablation-replace.mjs`, rebuilt, and
checked by `ablation-dist-preflight` in both directions.
- Red: the expand pin 2/4, the routing pin 2/6, the dogfood pin 2/4.
- Restored to the `HEAD` blob.
- The first attempt was a no-op: the build refused the marker spelling,
and no pin ran.
## Acceptance notes
- A global-rung audit row now needs `manage_platform_settings` to be
read through the data API. That is the narrowing the changeset states,
with its remedy.
- `docs/qa` checklist items 7 and 29 were re-pointed because their
clauses stopped being true. Not in the claim's named surface; noted for
the seat.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01HZT9daB4YX41N1SVVxYcek)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 973917c commit b12e16a
22 files changed
Lines changed: 803 additions & 93 deletions
File tree
- .changeset
- content/docs/permissions
- docs
- audits
- qa/platform-checklist/areas
- packages
- cli/src/utils
- platform-objects/src
- system
- qa/dogfood/test
- services/service-settings/src
- spec/src/system/constants
- scripts
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
83 | 83 | | |
84 | 84 | | |
85 | 85 | | |
86 | | - | |
| 86 | + | |
87 | 87 | | |
88 | 88 | | |
89 | 89 | | |
| |||
122 | 122 | | |
123 | 123 | | |
124 | 124 | | |
125 | | - | |
| 125 | + | |
126 | 126 | | |
127 | 127 | | |
128 | 128 | | |
| |||
187 | 187 | | |
188 | 188 | | |
189 | 189 | | |
190 | | - | |
| 190 | + | |
191 | 191 | | |
192 | | - | |
193 | | - | |
| 192 | + | |
| 193 | + | |
194 | 194 | | |
195 | 195 | | |
196 | 196 | | |
| |||
200 | 200 | | |
201 | 201 | | |
202 | 202 | | |
203 | | - | |
| 203 | + | |
204 | 204 | | |
205 | 205 | | |
206 | 206 | | |
207 | 207 | | |
208 | 208 | | |
209 | 209 | | |
210 | | - | |
| 210 | + | |
211 | 211 | | |
212 | 212 | | |
213 | 213 | | |
214 | | - | |
| 214 | + | |
215 | 215 | | |
216 | 216 | | |
217 | 217 | | |
| |||
223 | 223 | | |
224 | 224 | | |
225 | 225 | | |
226 | | - | |
227 | | - | |
| 226 | + | |
| 227 | + | |
228 | 228 | | |
229 | | - | |
| 229 | + | |
230 | 230 | | |
231 | | - | |
| 231 | + | |
232 | 232 | | |
233 | 233 | | |
234 | 234 | | |
235 | 235 | | |
236 | | - | |
| 236 | + | |
237 | 237 | | |
238 | 238 | | |
239 | 239 | | |
240 | 240 | | |
241 | 241 | | |
242 | 242 | | |
243 | | - | |
| 243 | + | |
244 | 244 | | |
245 | 245 | | |
246 | 246 | | |
247 | | - | |
| 247 | + | |
248 | 248 | | |
249 | 249 | | |
250 | 250 | | |
| |||
297 | 297 | | |
298 | 298 | | |
299 | 299 | | |
300 | | - | |
| 300 | + | |
301 | 301 | | |
302 | 302 | | |
303 | 303 | | |
304 | 304 | | |
305 | 305 | | |
306 | | - | |
| 306 | + | |
307 | 307 | | |
308 | 308 | | |
309 | 309 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
33 | 33 | | |
34 | 34 | | |
35 | 35 | | |
36 | | - | |
37 | | - | |
| 36 | + | |
| 37 | + | |
38 | 38 | | |
39 | | - | |
| 39 | + | |
40 | 40 | | |
41 | | - | |
| 41 | + | |
42 | 42 | | |
43 | 43 | | |
44 | 44 | | |
45 | 45 | | |
46 | | - | |
| 46 | + | |
47 | 47 | | |
48 | 48 | | |
49 | 49 | | |
| |||
90 | 90 | | |
91 | 91 | | |
92 | 92 | | |
93 | | - | |
| 93 | + | |
94 | 94 | | |
95 | 95 | | |
96 | 96 | | |
97 | 97 | | |
98 | 98 | | |
99 | | - | |
| 99 | + | |
100 | 100 | | |
101 | 101 | | |
102 | 102 | | |
| |||
229 | 229 | | |
230 | 230 | | |
231 | 231 | | |
| 232 | + | |
232 | 233 | | |
233 | 234 | | |
234 | 235 | | |
| |||
0 commit comments