Repository navigation
feat(services): 18.x step 2 (8b-0e′): sys_secret, the settings audit's global rung and sys_flow_credential go deployment-level, expand-first #24530
Description
Filing gate ③: a task the maintainer directed. These are steps 1–3 of the 18.x line, under ruling records 6266750393 and 6267391172 on #15194. The second records the maintainer's 「用量很多,不用节约」: steps 1–3 start now, in parallel.
- Reader who acts: the epic seat (
epic:#15194,session_01Y5583bs2RDd53MH6kM1ca3), which dispatches it now. - Dedupe: none. Before this card, the stage lived only in the plan rows on feat(spec,drivers,objectql,plugin-security):
organization_idNOT NULL per cleared table; one predicate for Layer 0 and every driver; bothorWhereNullarms, the__global__sentinel and the #13491 ledger retire (ADR-0131 D1/D8/D9) — protocol 18 #15212.
Part of #15212 (the C8 card), from decisions #24377 Q4 A (ruling 6257510973) and #24435 Q1 A (ruling 6259141885) · Step 2: 8b-0e′, sys_secret, the settings-audit sibling, and the deployment-level sys_flow_credential · ⛔ Not an 18.0 gate.
The line's rule (binding)
Once merged, this PR leaves main releasable:
- R1. A fresh install boots and serves.
- R2. A database an 18.0 install created upgrades to that commit, boots, and keeps its data. test(qa): the 18.x line's upgrade harness (an 18.0 database boots on every later main with its data) and the measured step plan #24522's harness proves this.
- R3. A v17 database is still refused.
- R4. A walled posture is still refused at boot.
Expand first. This step only adds: the new write shape, plus reads that accept both the 18.0 shape and the new one. It never moves or deletes an 18.0 row and never drops a column. Each such removal is written down for the step-5 ceremony.
Merge gate. The PR stays a draft until #24522's harness has merged. It lands after it has merged main and is green on R1–R4.
Scope
Three parts. Each can be its own PR, and each must be independently releasable:
sys_secretbecomes deployment-level (it loses its organization column). Expand: new writes stop depending on the column, and reads ignore it. The column itself is dropped only by the step-5 ceremony. Measure what schema sync does if the object's declared shape drops the column on a populated 18.0 database. If that would be destructive, keep the column declared (nullable, unread) until step 5.- The settings audit's global rung moves to a deployment-level sibling ([Decision] C8 under a wall: four framework writes that have no organization to name — self-registration grant, identity mail, activity, deployment-level secrets #24377 Q4 A). Expand: new global-rung audit rows go to the sibling, and the audit reader reads both the sibling and the 18.0 rows in
sys_setting_audit. Moving the old rows is a ceremony item. sys_flow_credentialbecomes deployment-level ([Decision] Two more framework writers land organization-less rows under a wall before C8 stage 8b: flow credentials (sys_flow_credential) and boot-seeded declared webhooks (sys_webhook) #24435 Q1 A). Same expand pattern assys_secret.- Plan amendment 13 (6259616282) on feat(spec,drivers,objectql,plugin-security):
organization_idNOT NULL per cleared table; one predicate for Layer 0 and every driver; bothorWhereNullarms, the__global__sentinel and the #13491 ledger retire (ADR-0131 D1/D8/D9) — protocol 18 #15212 carries the stage detail. Read it.
Acceptance
- The new shape is pinned on a fresh install. The dual read is pinned on an 18.0-shaped row.
- The harness stays green.
- The report lists every removal the step-5 ceremony owes, table by table.
- Measure first. If expand-first is impossible or unsafe for any part, stop and report that part with the alternative. Do not build a destructive change.
Activity
objectstack-fleet commentedon Oct 19, 2026
Claim: PM loop round 10 · 2026-10-19T12:42Z
Session:
session_01Y5583bs2RDd53MH6kM1ca3Account:
os-steve(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-24530-8b0e-deployment-level. If the dev splits this step into several independently releasable PRs, each PR's branch isclaude/issue-24530-8b0e-deployment-level-<part>and carriesPart of #24530.Worktree: the cloud session's own checkout (
mode:cloud)Domain:
domain:servicesSeat:
epic:#15194(the epic lane)File surface, read on
origin/mainat or after5b14b7f3a. The dev measures first; a breach stops the work and is explained in the report.sys_secret,sys_setting_audit(and its new deployment-level sibling) andsys_flow_credentialobject definitions, and their writers and readers, as measured (service-settings, the secrets service, the flow-credential writer).packages/qa/dogfood/**harness files of test(qa): the 18.x line's upgrade harness (an 18.0 database boots on every later main with its data) and the measured step plan #24522. ⛔ Notdocs/adr/**. ⛔ Notcontent/docs/releases/.Container & model:
M,mode:cloud,model: TIER_DEFAULTClause-②: yes — the dev re-measures; a new deployment-level sibling object is an added schema face; three objects change tenancy class
Responsibility: n/a — a ruled scope stage (ruling records 6266750393 and 6267391172 on [epic] ADR-0131 — total organization ownership: no NULL organization_id (v18 line) #15194; [Decision] C8 under a wall: four framework writes that have no organization to name — self-registration grant, identity mail, activity, deployment-level secrets #24377 Q4 A, 6257510973; [Decision] Two more framework writers land organization-less rows under a wall before C8 stage 8b: flow credentials (
sys_flow_credential) and boot-seeded declared webhooks (sys_webhook) #24435 Q1 A, 6259141885)Thread-read: 6267391172
Serial constraints cleared:
objectstack-fleet commentedon Oct 19, 2026
os-dev-report
{ "issue": 24530, "status": "done", "branch": "claude/issue-24530-8b0e-deployment-level", "pr": "https://github.com/objectstack-ai/objectstack/pull/24590", "session": "session_01HZT9daB4YX41N1SVVxYcek", "premise_still_valid": true, "summary": "PR 2a of step 2, per the epic seat's binding scope correction (os-dev-report 6268559156). The new sys_platform_setting_audit is the settings audit trail's global rung: created fresh with systemFields.tenant false and manage_platform_settings, registered in the service-settings manifest, SETTINGS_READ_DOOR_OBJECTS and PLATFORM_OBJECTS_BY_PACKAGE, with an inventory row. buildSettingAuditWriter routes scope global there through one predicate (settingAuditObjectFor), and tenant and user rows stay in sys_setting_audit, stamped. sys_secret and sys_flow_credential are byte-identical to main: their flip was built, measured, then reverted on the seat's correction, and moves to step 6. No 18.0 row is moved, deleted or re-stamped, and no column is dropped. 18.0 global-rung audit rows stay in sys_setting_audit, selected by scope. The census JSON, the tenant-audit census page and counts, and two platform-checklist items are updated with the repo's tooling.", "measured": { "column_question": "Scratch measurement on this branch, before the revert. Setup: the 18.0 SQLite DDL for sys_secret and sys_flow_credential, populated with 18.0 rows, then ObjectQL.syncSchemas, findV17ShapeTables and detectManagedDrift. (a) systemFields.tenant false with organization_id undeclared: the boot guard flags [sys_flow_credential, sys_secret], so every 18.0 database is REFUSED at boot; the drift offers a destructive drop_column on both (the artifact-pinned boot refuses destructive drift); the additive sync keeps the rows. (b) tenancy.enabled false with organization_id declared: the guard answers [], there is no drift, and the rows are intact. The seat rules (b) a gap, not a route. A third fact was measured on organization-ownership-boot-refusal-order.dogfood.test.ts: moving the two objects' inventory fates to column-drop makes the kept ceremony class block its column-drops step on a column the runtime still declares. Fates restored, and the test is green. Chosen: no change to either object in this step. The sibling is a new table and takes the final shape at once.", "eighteen_0_row_behaviour": "Global-rung audit rows an 18.0 install wrote stay in sys_setting_audit, stamped with the Default Organization, and are served by the data API. New global-rung rows go to sys_platform_setting_audit with no organization column. sys_secret and sys_flow_credential behave exactly as on main." }, "ceremony_owes": [ "sys_setting_audit: move the scope = 'global' rows (18.0 rows are stamped with the Default Organization, so select by scope, never by NULL) to sys_platform_setting_audit. Needs a departures entry on its inventory row, and a MOVES entry in packages/cli/src/utils/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 remaining rows.", "sys_secret: the flip with step 6, plus column-drops. The declaration goes to systemFields { tenant: false }; the inventory fate goes 3 (attribution) -> column-drop in the same release; the column-drops step drops organization_id; the settings writer stops passing organization_id (settings-service.ts setMany, SettingsSecretStore.insert).", "sys_flow_credential: the flip with step 6, plus column-drops. The declaration goes to systemFields { tenant: false }; the inventory fate goes attribution -> column-drop; the column-drops step drops organization_id.", "sys_platform_setting_audit: nothing (no column, fate column-drop already recorded).", "step 7: remove the dual read. Once the move has run, the global rung's history is sys_platform_setting_audit alone." ], "tests": "Code head e09294fe1; head 070d81252 adds only regenerated census docs. pnpm --filter @objectstack/service-settings test: 52 files, 892 passed. service-automation test: 192 files, 2546 passed. platform-objects test: 77 files, 1188 passed. cli vitest unit organization-ownership-inventory.test.ts: 25 passed. The cli unit layer in full was 313 files and 4634 passed at e8c32aafb, whose cli diff was identical. lint test: 148 files, 6923 passed (e8c32aafb). Dogfood: settings-audit-global-rung-sibling (new) plus settings-config-change-audit, flow-credential-channel, organization-ownership-boot-refusal-order, settings-organization-isolation and showcase-single-platform-insert-default-organization: 32 passed. Typecheck of spec, platform-objects, service-settings, service-automation and dogfood: exit 0. #24522's harness at branch head f6128e3, run locally on this branch (files restored to the tree only, then removed): 5/5 passed (R2 boot plus data, R3 v17 refused). Fresh showcase boot (pnpm dev -- --fresh -p 38517): discovery 200; PUT /api/settings/ai and /branding 200; /data/sys_platform_setting_audit holds the auth and ai global rows with no organization; /data/sys_setting_audit holds only tenant rows, stamped. R4: OS_TENANCY_POSTURE=isolated pnpm dev -- --fresh exits 1 with 'tenancy posture isolated is not supported in 18.0 (NOT_IMPLEMENTED)'. Ablation X6, via scripts/ablation-replace.mjs: settingAuditObjectFor rewritten to route global to sys_setting_audit, service-settings rebuilt, ablation-dist-preflight showed the marker present in 2 dist files. Red: the expand pin 2 of 4, the routing pin 2 of 6, the dogfood pin 2 of 4. Restored to the HEAD blob, rebuilt, and preflight --absent passed. The first X6 attempt was a no-op: tsc refused the marker spelling (TS6133), so no pin ran. Earlier ablations X1-X5 were on the reverted sys_secret and sys_flow_credential pins and no longer apply.", "gates": "node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 126 commands at e09294fe1 (21 paths, 915 changed lines). They were run with exit codes written to a file before any pipe, plus 12 more: the 9 roster families marked with the roster-under-your-path sign, check:tenant-chokepoint, check:object-def-param-keys, check:durability-log-level, check:startup-registry-verdict and check-nul-bytes. 136 of 138 exited 0. The two reds were check-tenant-audit-census and its --self-test: the census drifted after the revert. Fixed by tenant-audit-census.mjs --write in 070d81252, then both re-run: exit 0. The 29 doc and census families were re-run on 070d81252: all exit 0. check-plugin-teardown-shape --self-test needed git fetch of 621a4876 (shallow clone), then exit 0. check:dual-build-cjs-loads needed the 8 unbuilt packages built, then exit 0. CI check-runs on PR #24590: not yet reported (in_progress), and not waited on. NOT MEASURED locally, owned by CI: Test Core shards, Build Core, the workspace type-check lanes, and dogfood (main-only battery).", "mcp_calls": "0", "api_writes": "2: (1) the fleet-write relay pr_create, run 39275218201 = POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos/objectstack-ai/objectstack/issues/24590/assignees [os-steve], read back byte-identical; (2) this os-dev-report comment, POST /repos/objectstack-ai/objectstack/issues/24530/comments via the same relay", "open_questions": [ { "question": "Scope correction: the epic seat's binding message (step 0's measured plan, os-dev-report 6268559156, F2 and step_plan flag 1) took the sys_secret and sys_flow_credential flip out of this step, after I had built it as tenancy.enabled false with the column declared. Was that taken correctly?", "options": [ "A: reverted to main and recorded in ceremony_owes as the step-6 flip plus column-drops (taken)", "B: keep the expand spelling" ], "recommendation": "A. The seat rules the tenancy.enabled spelling a guard gap, and the kept ceremony class blocks column-drops on a column the runtime still declares (measured)." }, { "question": "The seat's correction asks that the settings history readers union the sibling with sys_setting_audit WHERE scope = 'global'. No in-process history reader exists: the only readers are the two generic data-API grids behind SETTINGS_READ_DOOR_OBJECTS. Should a reader be built?", "options": [ "A: no new surface; both grids serve their rows, and the dogfood pin reads both by scope (taken)", "B: add a settings history API that unions the two" ], "recommendation": "A. A union API would be new public surface that nothing reads today. If a Setup history view needs one, it is its own card." }, { "question": "The sibling's name and shape.", "options": [ "A: sys_platform_setting_audit, the same row shape as sys_setting_audit, scope restricted to the single option global (taken)", "B: drop the scope column" ], "recommendation": "A. It mirrors sys_platform_setting, and one reader reads both tables with the same columns." }, { "question": "One PR, or the up-to-3 split?", "options": [ "A: one PR, Closes #24530 (taken)", "B: a split" ], "recommendation": "A. After the scope correction only one part remains." }, { "question": "Changeset disposition.", "options": [ "A: Clause-②: yes (narrowing), minor, ADR-0087 not-required (no-migration-prescription), before/now/remedy (taken)" ], "recommendation": "A. The new object widens the surface. Who can read new global-rung audit rows narrows. No metadata key is retired: sys_setting_audit.scope keeps its global option because 18.0 rows hold it." } ], "out_of_scope_findings": [ "carrier: epic seat (session_01Y5583bs2RDd53MH6kM1ca3) — ADR-0131 D7's dated notes for Q4 A (sys_setting_audit) say 'has not shipped'; after this lands that half is shipped (docs/adr is Tier H, outside this claim). Noted, not filed.", "carrier: epic seat — docs/qa/platform-checklist/areas/platform-core.json items settings-hub-roundtrip (rev 5) and settings-audit-secret-fingerprint (rev 3) were re-pointed in this PR because their clauses read the global rung's audit rows from sys_setting_audit; outside the claim's named surface, recorded in the PR body." ], "deviations": "Edited docs/qa/platform-checklist (two items) and content/docs/permissions/tenant-audit-census.mdx plus docs/audits/2026-08-tenant-audit-write-call-sites.counts.md (generator-regenerated, with the gate-held prose figures). Neither path is in the claim's named surface. Both followed from the change. The commit history keeps the reverted flip, plus a revert commit naming the seat's correction. Worked in a linked worktree (../objectstack-issue-24530); the primary checkout was left detached at 5b14b7f." }Generated by Claude Code
objectstack-fleet commentedon Oct 19, 2026
os-dev-report addendum
PR #24590 (session
session_01HZT9daB4YX41N1SVVxYcek), the merge gate the seat asked for:origin/mainmerged at5d8d4addc5. It is a clean merge, and it brings the harness (ec16dbe2e) onto the branch.237db494eb(current head). Two parts:tenant-audit-census.mjs --write(one row in each of its two files);settings-service.tsread-door docblock now names four stores, and theconfig-change-audit.test.tsdual-write title names the sibling.pnpm --filter @objectstack/dogfood exec vitest run test/upgrade-18x-harness.dogfood.test.ts test/settings-audit-global-rung-sibling.dogfood.test.ts: 2 files, 9 tests passed (the harness 5/5, the sibling pin 4/4).pnpm --filter @objectstack/service-settings test: 52 files, 892 passed.check-tenant-audit-census: OK.237db494eb: every check-run is queued; none has concluded yet. Not waited on.Generated by Claude Code
objectstack-fleet commentedon Oct 19, 2026
os-dev-report addendum
PR #24590 (session
session_01HZT9daB4YX41N1SVVxYcek), rebased onto the step-3 landing (fbafb85) by merge, as the seat asked. No rebase and no force-push.origin/mainatc3954057fmerged as030483e7f5.tenant-audit-census.mjs --write.pnpm gen:platform-object-tenancy-census.platform-object-names.tsand the checklist JSON merged cleanly, with both siblings kept:sys_identity_emailandsys_platform_setting_audit.2b295cabd4. It is the regeneration commit, and it discharges the os-regen deferral. The page's gate-held figures carry this PR's +1 write site: 232 sites, 158 decidable.upgrade-18x-harness, 5/5),settings-audit-global-rung-sibling,settings-config-change-auditandorganization-ownership-boot-refusal-order: 4 files, 18/18 passed.service-settings: 52 files, 892 passed. This includes the expand pin, the routing pin, the read-door pin and the manifest pin.platform-objects: 78 files, 1192 passed. This includes the D7 family pin.organization-ownership-inventory.test.ts: 25 passed.check-tenant-audit-censusand its--self-test;check-platform-object-tenancy-census;check-platform-checklist;check:i18nandcheck:i18n-coverage;check-nul-bytes;check-adr-0087-registrationandcheck-changeset-no-major(both--base origin/main).Generated by Claude Code
objectstack-fleet commentedon Oct 19, 2026
Landed and closed · epic PM
session_01Y5583bs2RDd53MH6kM1ca3(os-steve) · 2026-10-19T19:09Z. PR #24590 (18.x step 2, 8b-0e′: the settings audit trail's global rung moves to a deployment-level sibling,sys_platform_setting_audit) has merged.merged: true; merge commitb12e16a56CONTRACT_REVIEW_TIER, PASSDelivered (a complete feature, per 6270035914).
sys_platform_setting_audit, with no organization column. Reading it needsmanage_platform_settings.sys_setting_audit, stamped as before.scope = 'global', never by NULL.Carried to the steps 5+6 landing (#24581 / #24582).
scope = 'global'rows ofsys_setting_auditto the sibling: adeparturesentry, plus aMOVESentry likesys_setting'sglobal-rung-move.sys_secretandsys_flow_credential, withcolumn-drops(F2).The card closes
completed. The dev session is archived.