Skip to content

feat(objectql,cli,plugin-webhooks)!: 18.x step 4: the walled lift (8b walled half, the os serve lift, the declared-webhook name guard, declared webhooks deployment-wide) - #24667

Merged
objectstack-fleet[bot] merged 33 commits into
mainfrom
claude/issue-24580-walled-lift
Oct 20, 2026
Merged

objectstack-fleet[bot] merged 33 commits into
mainfrom
claude/issue-24580-walled-lift

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Clause-②: yes (narrowing)

Closes #24580 (18.x step 4, the walled lift). Every item of the card is in: 8b's walled half (with the cited sys_secret / sys_flow_credential bridge, contract review B1), #24489 items 2 and 3 (the os serve lift, R4 restated), the declared-name guard, and item 4 under ruling A.

Base. #24588 (step 1) has merged into main (191e0430e), and origin/main (3a931fca1) is merged into this branch at fdfc84238 with a merge commit. The four plugin-webhooks files conflicted only because #24588 was squash-merged: main's copy of each equals the step-1 head this branch had merged (b4ae8e68), and the resolution keeps this branch's side. That is proven per file: the branch's delta against main equals its own delta against b4ae8e68, byte for byte. No regenerable artifact conflicted. The diff is now this card's own files only.

What changes

  1. 8b's walled half (packages/objectql). The platform-object exemption in Engine.resolveSystemInsertOrganization is gone under group / isolated too. A system insert into a sys_ / cloud_ / ai_ object with an organization column that names no organization is now refused under a wall with ERR_SYSTEM_WRITE_ORGANIZATION_REQUIRED (500, reason walled-posture), instead of landing organization_id NULL. PLATFORM_OBJECT_TENANCY keeps its every-posture reader, the bypassTenantAudit mute; only the walled write-path reader retired. The docblocks in platform-object-tenancy.ts, system-write-organization.ts and engine.ts now say this.
  2. The declared-name guard on sys_webhook (packages/plugins/plugin-webhooks, seat note 6271514301 item 1). bindDeclaredWebhookNameGuard has the shape of bindDeclaredRuleNameGuard. A non-system insert whose name is declared (switched on or off) is refused, and so is an update that changes name onto a declared one. The refusal is DeclaredWebhookNameConflictError: RESOURCE_CONFLICT, 409, naming the webhook and the remedy (clone under another name, switch the declaration off through the activation door). System writes pass. An update that keeps its name passes, so the 18.0 mirror rows and the rows customised there stay editable. WebhookOutboxPlugin binds the guard beside the reader and unbinds it on destroy(). The system-context census page gains the anchor.
  3. Walled test rigs. 164 walled-posture test files, measured by a posture grep. Two failed after the lift, both fixtures, and both are fixed by carrying the organization a walled system write needs. No assertion moved.
    • plugin-security/src/builtin-positions.boot.test.ts: the census positive-control insert.
    • plugin-security/src/grant-holder-membership-refusal.test.ts: the sys_position seed.
  4. The objectql pins of the exemption itself flip. They pinned the behaviour this card retires. They now pin the walled refusal and its discriminating control (an object with no tenant column is still not refused):
    • system-write-organization.test.ts
    • tenancy-by-object-classification.test.ts
  5. Two changesets: 15212-8b-walled-platform-insert-refused.md (objectql, minor, breaking) and 15212-webhook-declared-name-guard.md (plugin-webhooks, minor, breaking).

#24489 items 2 and 3: the os serve lift (R4 restated)

The session owner granted the permission the classifier had held, and these went in at 45898dbf6:

  • walledPostureRefusal and its call in serve.ts are deleted, together with serve-walled-posture-refusal.test.ts. The diff is the exact inverse of ae6bd1a6's serve.ts hunks.

  • The seven walled e2e cases are restored whole from ae6bd1a6^. Neither file had changed since. There are 3 cases in serve-organizations-host-resolution.e2e.test.ts and 4 in serve-organizations-mount-failure.e2e.test.ts.

  • walledPostureFinding (doctor) and WALLED_POSTURE_RELEASE_NOTE (the fix-list note) are removed. Their four test files are restored from 5b14b7f^; none had changed since, and 5b14b7f's hunks in them were all walled.

  • The 18.0 walled docs sentences are removed:

    • tenancy-modes.mdx: the callout now says only that the upgrade path arrives in a later 18.x release;
    • environment-variables.mdx: the OS_TENANCY_POSTURE row;
    • self-hosting.mdx: byte-identical to before ae6bd1a6;
    • cli.mdx, three sites.

    Each sentence's upgrade half stays, because it belongs to steps 5 and 6. Nothing under content/docs/releases/ or docs/adr/ is touched.

  • One changeset: 15212-walled-posture-boots.md (@objectstack/cli, minor, Clause-②: yes (widening)).

Item 4 (seat note 6271514301 item 2): ruling A, implemented

The maintainer ruled A (ruling record 6278609968 on #24580). Under group and isolated a declared webhook is deployment-wide, exactly as under single since step 1:

  • It receives every organization's events, and each sys_http_delivery row is stamped with its event's organization.
  • The switch stays the deployment-level activation-ledger row, operator-gated under a wall.
  • Today's organization-less refusal under a wall is removed.
  • No ADR change.

What changed:

The pins:

  • New: packages/qa/dogfood/test/wall-declared-webhook-deployment-wide.dogfood.test.ts. It runs a real walled boot, under both isolated and group, with two organizations created through the auth API and the engine's own record events reaching the plugin's dispatcher. It pins three things:
    1. an organization administrator holding manage_metadata in its own organization is refused 403 PERMISSION_DENIED at the activation door, nothing is written, and the platform operator switches the webhook on;
    2. one delivery per event, each stamped with that event's organization;
    3. each organization reads only its own deliveries, through GET /data/sys_http_delivery with an organization-scoped read grant.
  • Flipped (unit): in declared-webhooks.test.ts, the walled-refusal case becomes the two-organization delivery case, and the posture-read case becomes "deployment-wide under single / group / isolated / no tenancy service".

Reverse-verified, each through ablation-replace.mjs, with the package rebuilt so the mutation reached dist and the source restored to git diff HEAD empty:

  • sub.deploymentWide = true changed to false: 3 unit cases and 4 dogfood cases red (stamping and reading, under both postures).
  • The event-organization stamp replaced by the subscription's: the same 3 + 4 red.
  • The door's posture refusal removed in catalog-activation.ts (runtime dist confirmed mutated, 2 → 1 occurrences): the dogfood door case red under both postures, and the runtime door unit test 12 red.
    • A first run of this pin had org A without manage_metadata, so the door refused it on authority, not on posture, and the ablation stayed green. The fixture now grants manage_metadata inside org A, so only the posture rule can refuse it.

Contract review B1 (comment 6280853341): the walled bridge for sys_secret and sys_flow_credential

The walled lift reached three production writers that are organization-less by ruling until step 6:

  • a global-scope encrypted setting (settings-service.ts, inserted by settings-service-plugin.ts);
  • a datasource credential bind (datasource-secret-binder.ts);
  • a flow credential save (flow-credential-channel.ts).

Under a wall each became a 500 ERR_SYSTEM_WRITE_ORGANIZATION_REQUIRED. The fix is the seat's choice (i):

  1. A cited bridge. WALLED_ORGANIZATION_LESS_BRIDGE (objectql/src/tenancy/system-write-organization.ts) names exactly sys_secret and sys_flow_credential. Engine.resolveSystemInsertOrganization lets their organization-less system inserts land under group / isolated, as before the lift; under single they take the Default Organization as before. It cites ruling 6270035914 item 2 and feat(cli,runtime)!: 18.x step 6: open the upgrade ceremony for v17 and 18.0 databases (and the deferred flips) #24582.

  2. A tripwire. service-automation/src/walled-organization-less-bridge.test.ts asserts the list is exactly those two tables, and that each still carries its tenant column. Note for the feat(cli,runtime)!: the upgrade ceremony opens — v17 and 18.0 databases take it, and a server serves only a database it created or one the ceremony certified (18.x steps 5 and 6) #24681 dev: the PR that flips either table to systemFields: { tenant: false } turns this red. Its message says to delete the list, its reader in Engine.resolveSystemInsertOrganization and the tripwire itself.

  3. Three real-engine walled pins, each under isolated and group:

    • service-settings/src/settings-global-secret-walled.test.ts: the real SettingsService plus the plugin's real secret store;
    • service-datasource/src/datasource-secret-binder.walled.test.ts: the real binder on SQLite. The package gains @objectstack/objectql and @objectstack/platform-objects as devDependencies (6 lockfile lines, no build-graph cycle), aliased to source;
    • service-automation/src/walled-organization-less-bridge.test.ts: the real FlowCredentialChannel on SQLite, which writes both tables.

    Reverse-verified through ablation-replace.mjs:

    • bridge disabled, objectql rebuilt: all 6 walled cases red across the three packages;
    • systemFields: { tenant: false } added to sys_flow_credential, then to sys_secret (platform-objects rebuilt): the tripwire red, each time with its remedy message.

    Each restored to git diff HEAD empty.

  4. The changeset 15212-8b-walled-platform-insert-refused.md:

    • its "Unchanged" list names the two bridged tables;
    • the remedy spells systemFields: { tenant: false };
    • the ADR-0087 marker records the two added exports.
  5. The QA checklist. origin/main (with docs(qa): 18.0 sweep anchor round 5 — the walled-posture and second-organization themes, runner environment facts, and two carried notes #24687) is merged again, at 3f839a98c, cleanly. Then:

    • RUNNER.md's two environment facts are rewritten: "A walled boot is a fixture of its own" (the three prerequisites, measured), and the one-organization entry, which now names the walled boot's organization create;
    • seven items re-pointed, each with a revision bump and a history entry: the five in access-security.json, platform-core.settings-organization-isolation-and-platform-rung, and attachments-storage.chunked-upload-integrity. An unprovisioned walled leg is now blocked(fixture);
    • check-platform-checklist, full and --changed-from: OK.

Re-verified on f73d8891a (after a fresh install and a full pnpm build):

reading result
R1, fresh single boot discovery 200, 0 refusals
R2 + R3, the harness with webhook-materialization and the walled declared-webhook pin 3 files / 15 passed
R4, isolated os serve with the real Organizations runtime discovery 200, Organizations mounted, 0 refusals
service-settings 53 files / 894 passed
service-datasource 46 files / 840 passed
service-automation 196 files / 2600 passed
plugin-security 189 files / 3786 passed, 45 skipped
plugin-webhooks 221 passed
plugin-sharing 1163 passed
plugin-auth 3168 passed, 10 skipped
objectql walled pins 68 passed
CLI unit + the 7 walled e2e cases 4639 passed; 7/7
typecheck (all nine packages) exit 0
dispatch-gates --commands 124 / 124 exit 0, after one fix: check:test-source-alias asked for the new service-datasource imports to be aliased, and they are
CI on f73d8891a 37 success, 5 skipped, 0 failures

Re-verified after merging main (head fdfc84238)

The install was refreshed and pnpm build rerun in full (exit 0). Then:

reading result
R1: pnpm dev -- --fresh (showcase, single) ready, discovery 200, 0 refusals
R2 + R3: upgrade-18x-harness (with webhook-materialization and the walled declared-webhook pin) 3 files / 15 passed
plugin-webhooks, whole package 20 files / 221 passed
objectql walled pins + plugin-security walled fixtures 68 + 76 passed
CLI --project unit + the 7 restored e2e cases 314 files / 4639 passed; 7/7
typecheck: objectql, plugin-webhooks, plugin-security, cli exit 0
dispatch-gates --commands: every derived command, run in full 110 / 110 exit 0

CI red after the merge, fixed at 1412586b4 (fixtures only, no assertion changed). fdfc84238 was red in Test Core 2/6 (plugin-sharing) and 3/6 (plugin-auth). The 164-file walled grep had missed both files, because they set the posture another way. Both fixtures wrote organization-less legacy rows under a wall through the engine, which the walled lift now refuses:

  • plugin-sharing/src/reconcile-refused-grant-continues.test.ts (9 cases): the "platform-global rule" (organization_id null) is now seeded through the driver, as the fixture already seeds its organization-less records. That is the row an older database holds.
  • plugin-auth/src/last-admin-guard.config-anchor.test.ts (3 cases): the legacy unscoped admin_full_access grant and its catalog row are seeded through the driver in the L5 block. The fixture's sys_metadata_activation now declares systemFields: { tenant: false }, as the platform object does (no organization column).
  • Full suites after the fix: plugin-sharing 49 files / 1161 passed; plugin-auth 165 files / 3168 passed, 10 skipped; typecheck exit 0 for both.
  • CI on 1412586b4: 36 success, 5 skipped, 0 failures, all six Test Core shards included.

The readings below were taken before the merge.

Proof (head 45898dbf6)

reading result
objectql, whole package 449 files / 8454 tests passed
plugin-webhooks build closure + typecheck + whole package 20 files / 221 tests passed
plugin-security, whole package 188 files / 3754 passed, 45 skipped
walled rigs (164 files over 17 packages that depend on objectql) all green after the two fixture fixes; dogfood 12 passed, 1 skipped
R2: upgrade-18x-harness.dogfood.test.ts 6/6, rerun after ruling A together with webhook-materialization and the new walled pin: 3 files / 15 passed
plugin-webhooks after ruling A: typecheck + whole package exit 0; 20 files / 221 passed
R3 covered by the harness's v17 case (the ceremony still refuses)
R1: pnpm dev -- --fresh (showcase, single) after a full pnpm build ready, GET /api/v1/discovery 200, no system-write refusal in the log
R4 as restated: a walled os serve boots MET. The built CLI (bin/run-dev.js serve) under OS_TENANCY_POSTURE=isolated (plus the walled prerequisites OS_PLATFORM_OWNER_EMAIL and OS_AUTH_MEMBERSHIP_POLICY=invite-only), on a throwaway app that declares the real @objectstack/organizations, reached Press Ctrl+C to stop. Organizations was mounted, GET /api/v1/discovery answered 200, and the log holds 0 system-write refusals
CLI after the lift: build + typecheck + --project unit exit 0; 313 files / 4633 passed
the seven restored e2e cases (OS_TEST_TIERS=nightly) 7/7

Ablations, each through scripts/ablation-replace.mjs, with mutation landed and restored to git diff HEAD empty:

  • The walled exemption re-inserted in the engine: 6 walled cases red (sys_audit_entry and sys_permission_set, × isolated / group, in both objectql files).
  • The guard's throw replaced by return: 4 cases red (insert, switched-off, rename, plugin wiring).
  • The guard's system pass and rename check removed: 3 control cases red.

Ablation of the lift: a walled refusal re-inserted at serve's posture gate turns all 7 restored e2e cases red; restored to git diff HEAD empty.

Local gates for the os serve half, each exit 0: check-platform-checklist --changed-from origin/main (for the removed exports), adr-0087-registration, changeset-no-major, empty-changeset, closing-keyword-parity, check-affected-docs, check-drift-comment, check-doc-frontmatter, doc-anchors, doc-authoring, docs-spec-enumerations, docs-transcript-drift, doc-security-posture, cli-command-ids, issue-citations, nul-bytes, cross-package-test-inputs, tier-file-adoption, pm-prior-rulings, platform-checklist. Targeted eslint exit 0.

Local gates (dispatch-gates --commands, narrowed to the families reading this card's files; CI runs the farm), all exit 0 at the end:

  • the three census gates (system-context, tenant-audit, platform-object-tenancy);
  • adr-0087-registration, changeset-no-major, empty-changeset, closing-keyword-parity, issue-citations, doc-authoring;
  • engine-double-contract, objectql-double-limit, query-options-erasure, where-matcher;
  • nul-bytes, cross-package-test-inputs, test-source-alias, durability-log-level, error-status-conformance, route-envelope, platform-checklist, engine-split-ratio.

check-system-context-census and where-matcher were red on first run, on this card's own new code; both are fixed in the branch.

Acceptance notes

  • Size. git diff --shortstat origin/main...HEAD on head f73d8891a: 45 files, +1431 / -602 (2033 changed lines), under the 3000 human-merge line, with 0 governed paths.
  • Walled 18.0 databases from embedded hosts. These were never refused. Their NULL rows are step 5's (ceremony attribution and report fates), as the card records. Nothing here moves or rewrites stored rows.

Generated by Claude Code

claude added 21 commits October 19, 2026 13:06
…r retired; webhook activation door

Claude-Session: https://claude.ai/code/session_01PDSxBZE561TFe1N2iEjKXZ
Co-authored-by: Claude <noreply@anthropic.com>
…he 18.0 dual read; door pins for webhook

Claude-Session: https://claude.ai/code/session_01PDSxBZE561TFe1N2iEjKXZ
Co-authored-by: Claude <noreply@anthropic.com>
…asserts no row and a dispatch

Claude-Session: https://claude.ai/code/session_01PDSxBZE561TFe1N2iEjKXZ
Co-authored-by: Claude <noreply@anthropic.com>
…the checklist, the webhooks guide and the showcase; changeset

Claude-Session: https://claude.ai/code/session_01PDSxBZE561TFe1N2iEjKXZ
Co-authored-by: Claude <noreply@anthropic.com>
…tcher's switch read

Claude-Session: https://claude.ai/code/session_01PDSxBZE561TFe1N2iEjKXZ
Co-authored-by: Claude <noreply@anthropic.com>
…posture builds no wall, as its 18.0 seeded copy stamped with the Default Organization was

Claude-Session: https://claude.ai/code/session_01PDSxBZE561TFe1N2iEjKXZ
Co-authored-by: Claude <noreply@anthropic.com>
…red writes; the declared-webhook double is drivable by the limit gate

Claude-Session: https://claude.ai/code/session_01PDSxBZE561TFe1N2iEjKXZ
Co-authored-by: Claude <noreply@anthropic.com>
…does not implement

Claude-Session: https://claude.ai/code/session_01PDSxBZE561TFe1N2iEjKXZ
Co-authored-by: Claude <noreply@anthropic.com>
…authors; checklist and admitsOrganization docblock say a declared webhook is deployment-wide under single

Claude-Session: https://claude.ai/code/session_01PDSxBZE561TFe1N2iEjKXZ
Co-authored-by: Claude <noreply@anthropic.com>
…the fixture's customized declared-webhook mirror

Claude-Session: https://claude.ai/code/session_01PDSxBZE561TFe1N2iEjKXZ
Co-authored-by: Claude <noreply@anthropic.com>
…n fail on a double delivery

Claude-Session: https://claude.ai/code/session_01PDSxBZE561TFe1N2iEjKXZ
Co-authored-by: Claude <noreply@anthropic.com>
…0g-webhooks-registry

# Conflicts:
#	content/docs/permissions/tenant-audit-census.mdx
#	docs/audits/2026-08-tenant-audit-write-call-sites.counts.md
…0g-webhooks-registry

# Conflicts:
#	content/docs/permissions/tenant-audit-census.mdx
#	docs/audits/2026-08-tenant-audit-write-call-sites.counts.md
…-registry' into claude/issue-24580-walled-lift
…er the walled postures (ADR-0131 C8 stage 8b, walled half)

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XA1XHtvWd8Q1Q1gyWvhZZz
…declared webhook's name with 409 RESOURCE_CONFLICT

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XA1XHtvWd8Q1Q1gyWvhZZz
… webhook name guard

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XA1XHtvWd8Q1Q1gyWvhZZz
…d system insert needs

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XA1XHtvWd8Q1Q1gyWvhZZz
… the guard test's fake refuses combinators

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XA1XHtvWd8Q1Q1gyWvhZZz
@github-actions github-actions Bot added size/xl documentation Improvements or additions to documentation tests tooling labels Oct 19, 2026
@github-actions

github-actions Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 4 package(s): @objectstack/cli, @objectstack/objectql, @objectstack/plugin-webhooks, @objectstack/service-datasource, touching 30 documentable anchor(s). ⚠️ 3 changed file(s) yielded no anchor (packages/objectql/src/index.ts, packages/objectql/src/tenancy/platform-object-tenancy.ts, packages/services/service-datasource/vitest.config.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

46 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json f1298b3487fa8a93fdc63fe715e3d1d9f977f33a.

⛔ 9 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 3 changed file(s) yielded no anchor (packages/objectql/src/index.ts, packages/objectql/src/tenancy/platform-object-tenancy.ts, packages/services/service-datasource/vitest.config.ts) — pages documenting those are invisible to this run
  • 1 anchor(s) matched too much of the corpus to be a work list: os serve (command, 34 pages)
  • 7 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 42 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json f1298b3487fa8a93fdc63fe715e3d1d9f977f33a → packageMentionDocs.

Which tree this was computed on

This run read content/docs from c88cfe0a952b16db210bc37f276fc8f6f03bf4c0 — the merge of head 1e8338d3b669027350d477b694a2ae7c1b42623c into base f1298b3487fa8a93fdc63fe715e3d1d9f977f33a, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin c88cfe0a952b16db210bc37f276fc8f6f03bf4c0 && git checkout c88cfe0a952b16db210bc37f276fc8f6f03bf4c0
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin f1298b3487fa8a93fdc63fe715e3d1d9f977f33a 1e8338d3b669027350d477b694a2ae7c1b42623c && git checkout -B drift-repro f1298b3487fa8a93fdc63fe715e3d1d9f977f33a && git merge --no-ff 1e8338d3b669027350d477b694a2ae7c1b42623c

node scripts/docs-audit/affected-docs.mjs --json f1298b3487fa8a93fdc63fe715e3d1d9f977f33a

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs f1298b3487fa8a93fdc63fe715e3d1d9f977f33a → pass the list as
args.docs, on the commit named under Which tree this was computed on.

…very posture (ruling A on the walled shape)

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XA1XHtvWd8Q1Q1gyWvhZZz
… posture rule refuses it

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XA1XHtvWd8Q1Q1gyWvhZZz
…ent facts to the restated R4 — a walled os serve boots once provisioned

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XA1XHtvWd8Q1Q1gyWvhZZz
…to source for the walled credential-bind pin

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XA1XHtvWd8Q1Q1gyWvhZZz
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review (re-review after FAIL at 1412586b4)

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: f73d8891aa2154925f9e96de035e2415c731758e
Local-runs: none
Scope: packages/cli/src/commands/doctor-env-provenance.test.ts, packages/cli/src/commands/doctor-organizations-message-spelling.test.ts, packages/cli/src/commands/doctor-tenancy-posture-report.test.ts, packages/cli/src/commands/doctor.ts, packages/cli/src/commands/serve-organizations-message-spelling.test.ts, packages/cli/src/commands/serve-walled-posture-refusal.test.ts (deleted), packages/cli/src/commands/serve.ts, packages/cli/src/utils/tenancy-posture-hints.ts, packages/cli/test/serve-organizations-host-resolution.e2e.test.ts, packages/cli/test/serve-organizations-mount-failure.e2e.test.ts, packages/objectql/src/engine.ts, packages/objectql/src/system-write-organization.test.ts, packages/objectql/src/tenancy-by-object-classification.test.ts, packages/objectql/src/tenancy/platform-object-tenancy.ts, packages/objectql/src/tenancy/system-write-organization.ts, packages/plugins/plugin-auth/src/last-admin-guard.config-anchor.test.ts, packages/plugins/plugin-security/src/builtin-positions.boot.test.ts, packages/plugins/plugin-security/src/grant-holder-membership-refusal.test.ts, packages/plugins/plugin-sharing/src/reconcile-refused-grant-continues.test.ts, packages/plugins/plugin-webhooks/src/auto-enqueuer.ts, packages/plugins/plugin-webhooks/src/declared-webhook-name-guard.test.ts, packages/plugins/plugin-webhooks/src/declared-webhooks.test.ts, packages/plugins/plugin-webhooks/src/declared-webhooks.ts, packages/plugins/plugin-webhooks/src/webhook-outbox-plugin.ts, packages/qa/dogfood/test/wall-declared-webhook-deployment-wide.dogfood.test.ts + packages/services/service-settings/src/settings-service.ts, packages/services/service-settings/src/settings-service-plugin.ts, packages/services/service-settings/src/settings-secret-organization.test.ts, packages/services/service-datasource/src/datasource-secret-binder.ts, packages/services/service-datasource/src/datasource-admin-plugin.ts, packages/services/service-automation/src/flow-credential-channel.ts, packages/platform-objects/src/system/sys-secret.object.ts, packages/services/service-automation/src/sys-flow-credential.object.ts, packages/platform-objects/src/system/sys-platform-setting.object.ts, packages/core/src/security/system-write-organization.ts, packages/runtime/src/domains/catalog-activation.ts, packages/runtime/src/organization-ownership-boot-guard.ts, packages/plugins/plugin-sharing/src/sharing-rule-service.ts, packages/plugins/plugin-security/src/bootstrap-platform-admin.ts, packages/plugins/plugin-security/src/auto-org-admin-grant.ts, packages/plugins/plugin-audit/src/audit-writers.ts, packages/services/service-messaging/src/sql-http-outbox.ts, packages/services/service-messaging/src/messaging-service.ts, packages/plugins/plugin-approvals/src/approval-service.ts, packages/services/service-mailbox/src/ingest/mail-ingest-service.ts, packages/plugins/plugin-auth/src/scim-connection-service.ts, packages/plugins/plugin-security/src/suggested-audience-bindings.ts, packages/cli/src/utils/comment-reactions-conversion.ts, scripts/platform-object-tenancy-census.json, packages/qa/dogfood/test/upgrade-18x-harness.dogfood.test.ts, docs/qa/platform-checklist/areas/access-security.json, .github/workflows/ci.yml, scripts/check-changeset-no-major.mjs, scripts/check-adr-0087-registration.mjs + (new on this head) packages/objectql/src/index.ts, packages/services/service-automation/src/walled-organization-less-bridge.test.ts, packages/services/service-settings/src/settings-global-secret-walled.test.ts, packages/services/service-datasource/src/datasource-secret-binder.walled.test.ts, packages/services/service-datasource/package.json, packages/services/service-datasource/vitest.config.ts, packages/services/service-settings/vitest.config.ts, packages/services/service-automation/vitest.config.ts, pnpm-lock.yaml, .changeset/15212-8b-walled-platform-insert-refused.md, docs/qa/platform-checklist/RUNNER.md, docs/qa/platform-checklist/areas/attachments-storage.json, docs/qa/platform-checklist/areas/platform-core.json, packages/services/service-settings/src/manifests/mail.manifest.ts, packages/plugins/organizations/src/membership-policy-gate.ts, packages/plugins/organizations/src/organizations-plugin.ts, packages/plugins/plugin-auth/src/auth-manager.ts, packages/spec/src/security/tenancy-posture.ts, scripts/check-test-source-alias.mjs, content/docs/deployment/environment-variables.mdx

Read-only re-review of PR #24667 (card #24580, 18.x step 4), head f73d8891aa2154925f9e96de035e2415c731758e on origin/claude/issue-24580-walled-lift, base origin/main at d20bf9b09, merge-base 1c1144fcd (45 files, +1431 / -602). Nothing was built, run or re-run locally. The delta since the FAIL head 1412586b4 is five commits: the dev's 00191e7ca (bridge, tripwire, three pins, devDependencies, lock), 98b45bd8b (changeset), the merge 3f839a98c of origin/main (1c1144fcd, carrying #24687's checklist round, #24682, #24694 and others), ca7eca0f1 (RUNNER.md and the seven checklist items) and f73d8891a (the service-datasource vitest aliases). The merge is pure: its tree is byte-identical to git merge-tree --write-tree of its two parents (empty diff against 414844695ab), so nothing in it is the dev's. Records read: the prior review (B1, R1-R4), the dev's addendum 4 (6281802782 on #24580), the PR body (patched for addendum 4), ruling 6270035914 item 2 as cited by the bridge, the #24681 branch claude/issue-24581-ceremony-open at edb2db1e8.

① Derived judgments

1. The bridge: implemented as fix (i) specified. B1 closed.

packages/objectql/src/tenancy/system-write-organization.ts:195 declares WALLED_ORGANIZATION_LESS_BRIDGE: readonly string[] = Object.freeze(['sys_flow_credential', 'sys_secret']), exactly the two tables; :198-200 isWalledOrganizationLessBridgeObject is a plain includes over it. The engine's one reader is engine.ts:7277-7279, inside resolveSystemInsertOrganization (:7255): if (isWalledOrganizationLessBridgeObject(object) && this.resolveEnginePosture() !== 'single') return undefined;. Under single the condition is false and the method falls through to the same derive-or-refuse path 1412586b4 had (:7280-7311), so nothing changes there. TenancyPosture is the closed enum single | group | isolated (spec/src/security/tenancy-posture.ts:40), and resolveEnginePosture (:7127-7135) degrades an unreadable posture to isolated, so the bridge applies wherever the refusal it bridges would have. Cannot be widened by configuration: the list is a frozen module constant with no option, environment variable, provider or setter feeding it; its only readers in the tree are engine.ts:217, :7277, index.ts:548-549 (the two exports) and the tripwire (grep over the branch). System inserts only: the bridge sits after the context exit at :7263 (carriesOrganization(execCtx?.tenantId)), so a session write or a system write that threads an organization never reaches it; it is called once per insert from :16154-16158 and only decides whether a DERIVED organization is stamped, so a row that carries its own organization_id keeps it as before. For these two tables under a wall it returns exactly what the pre-lift exemption returned (undefined), so their behaviour on this head equals 18.0's. Citations: engine.ts:7273 "[BRIDGE, epic ruling record 6270035914 on #15194 item 2, #24582]"; system-write-organization.ts:182-184 the ruling, item 2, and #24582, with the measured reason ("declaring them column-less any earlier refuses every 18.0 database at boot", :184-185) and the expiry ("until the steps 5+6 landing flips both tables column-less", :89, :183-184, :189-193). The docblock's "⛔ Never re-introduce a per-table or per-posture cut here" is amended at engine.ts:7270-7276 to "— with ONE cited, temporary exception", naming the bridge and "delete this with the flips"; the module header lists the bridge as a population outside the rule at :87-89. One wording inaccuracy, not behavioural: system-write-organization.ts:189-191 says that once the flips land "the engine exits on that [no tenant column] before this list is read", but in the engine the bridge test (:7277) precedes the schema read (:7280) and the tenant-field exit (:7285-7286); the list is read first. The result is the same undefined either way (③ R3).

2. The tripwire: red on the flip, names what to delete.

packages/services/service-automation/src/walled-organization-less-bridge.test.ts:92-104. It asserts the sorted list equals ['sys_flow_credential', 'sys_secret'] (:94), so adding a table reddens it as well ("⛔ Never extend the list to green it", :17), and for each of SysSecret and SysFlowCredential (:95) it asserts object.systemFields?.tenant is not false (:96-100) and object.tenancy?.enabled is not false (:101). SysSecret is imported from @objectstack/platform-objects, which service-automation's vitest config aliases to source (vitest.config.ts:45-48), and SysFlowCredential is the local object file (:32), so it reads the declarations #24681 edits. On #24681's head edb2db1e8, sys-secret.object.ts:49 and sys-flow-credential.object.ts:54 each declare systemFields: { tenant: false }; .not.toBe(false) fails on each, so the pin turns red. The message (:98-99) reads "NAME now declares systemFields.tenant: false — the steps 5+6 flip has landed, so DELETE WALLED_ORGANIZATION_LESS_BRIDGE, its reader in Engine.resolveSystemInsertOrganization and this tripwire". The dev's ablation (6281802782) flipped each table in turn with platform-objects rebuilt and read the red with that message. On this head both object files still declare no systemFields and no tenancy (branch sys-secret.object.ts, sys-flow-credential.object.ts), so it is green. One shape hazard: the tripwire shares its file with the flow-credential walled pin (:106-121), and the remedy says "this tripwire" while system-write-organization.ts:191-193 says "that pin are deleted" naming the file; a #24681 dev who deletes the file loses a pin that stays valid after the flip (③ R2).

3. The three real-engine walled pins: present, each under isolated and group, each discriminating.

  • Settings, service-settings/src/settings-global-secret-walled.test.ts. A real ObjectQL (:89) with setTenancyPostureProvider(() => posture) (:93) over it.each(['isolated', 'group']) (:106); SysSecret registered from source (:94; the package aliases platform-objects/system to source, vitest.config.ts:72-75, and objectql to source, :56-59); the production pair, SettingsServicePlugin.buildSecretStore(engine) (:95; settings-service-plugin.ts:487, inserting with { bypassTenantAudit: true, context: { isSystem: true } } at :491) and a real SettingsService with LocalCryptoProvider (:96-100); the real mailSettingsManifest whose scope is 'global' (mail.manifest.ts:51) with api_key encrypted (:103). setMany('mail', { ..., api_key }) (:110), then one sys_secret row with namespace: 'mail', key: 'api_key', organization_id null, and the value reads back (:112-116). The driver is an in-memory fake (:33-86) rather than SQLite, which is fine for this seam: the refusal is the engine's (engine.ts:7293-7311), reached before any driver call, so without the bridge the global value's organization-less system insert hits resolveSystemWriteOrganization under a wall and throws. The fake refuses $ operators (:43) so it cannot mis-match silently.
  • Datasource, service-datasource/src/datasource-secret-binder.walled.test.ts. A real ObjectQL and a real SqlDriver on better-sqlite3 :memory: with syncSchemas (:50-60), SysSecret registered from source, createDatasourceSecretBinder({ engine, cryptoProvider }) (:68), bind (:70), then exactly one sys_secret row { namespace: 'datasource', key: 'warehouse' } with organization_id null and resolve(ref) round-tripping (:72-76). Under both postures (:64).
  • Flow credential, service-automation/src/walled-organization-less-bridge.test.ts:106-121. A real ObjectQL and SqlDriver on SQLite with SysSecret and SysFlowCredential registered and setCryptoProvider(fakeCrypto()) (:63-76), new FlowCredentialChannel(() => ql).store(...) (:111) of an inbound flow whose start node carries a secret (:85); then one sys_flow_credential row with null organization and at least one sys_secret row, all null (:113-118). This write reaches both bridged tables in one save.

Red without the bridge: by construction for all six cases (the pre-bridge path under a wall is the walled-posture refusal, core/src/security/system-write-organization.ts:116, thrown at engine.ts:7306-7311), and the dev's ablation with objectql rebuilt reports the six red. Dependencies and gates: service-datasource gains @objectstack/objectql and @objectstack/platform-objects as devDependencies (package.json:66-67; pnpm-lock.yaml +6 lines in that importer only); neither objectql nor platform-objects depends on service-datasource (their package.json), so no cycle; better-sqlite3 and @objectstack/driver-sql were already declared (:30, :41, :51). scripts/check-test-source-alias.mjs is unchanged on the branch, and its baseline for service-datasource (:557-560) lists neither objectql nor platform-objects, so the gate requires both aliased to source: vitest.config.ts:66-67 adds anchored entries (/^@objectstack\/objectql$/, /^@objectstack\/platform-objects\/system$/, the subpath shape service-settings uses), with the comment at :55-62. CI's Validate Package Dependencies, Lint and Type Check are green on the head. service-automation needed no new dependency (objectql and driver-sql are devDependencies at :37, :39; platform-objects a dependency at :32); its baseline entry (:546-550) lists @objectstack/objectql as a KNOWN unaliased import, so the tripwire's WALLED_ORGANIZATION_LESS_BRIDGE and the flow pin's engine come from packages/objectql/dist, not source (③ R4).

4. The changeset: as specified, R2 resolved.

.changeset/15212-8b-walled-platform-insert-refused.md: frontmatter unchanged (minor, Clause-②: yes (narrowing), **BREAKING**); :14 "Apart from the two tables named below, the posture no longer changes which objects the rule covers"; :15 "Unchanged" names sys_secret and sys_flow_credential, the three deployment-level writers, the later-release column drop and the bridge by name; :17 the remedy now reads systemFields: { tenant: false }; :9 the adr-0087 marker records the two added exports. Check Changeset is green on the head, twice (the edited rerun included). The engine's refusal prose at system-write-organization.ts:308-310 still spells the platform-global declaration tenancy: { enabled: false }; that text predates this PR and is not in the delta (③ R5).

5. The checklist: seven items re-pointed, RUNNER.md rewritten, accurate against the restated R4.

The seven, each with a revision bump and a dated history entry (2026-10-20, ref: #24580) that says what moved and that no clause changed: access-security.no-active-org-session-semantics (:2346; revision 3→4; knownGaps[2]; history :2125-2129 of the diff), access-security.activation-write-operator-gate (:2474; 4→5; knownGaps[0], steps[8], acceptance[4] clause and verify, variants[1]-[2]), access-security.walled-environment-metadata-operator-only (:3366; 2→3; knownGaps[0]), access-security.grant-table-write-contract (:3689; 3→4; knownGaps[0], posture-independence note kept), access-security.parent-and-organization-write-guards (:3786; 2→3; knownGaps[0]), platform-core.settings-organization-isolation-and-platform-rung (:3209; 2→3; knownGaps[0]) and attachments-storage.chunked-upload-integrity (:1085; 4→5; knownGaps[0]'s second-organization leg). Each now says: since step 4 os serve (and os dev / os start) boots group|isolated; the 18.0 refusal is deleted; the walled boot is provisioned per RUNNER.md's "A walled boot is a fixture of its own"; an unprovisioned leg is blocked(fixture) naming the entry, never fail and never a faked posture; the pins stay the oracle. RUNNER.md:452-493 replaces the "18.0 boots the single tenancy posture only" entry (which cited walledPostureRefusal(), ruling 6259698357 and the deleted test) with "A walled boot is a fixture of its own", three prerequisites each said to refuse the boot with a FATAL naming itself when absent, and renames the one-organization entry "A stock single boot holds exactly one organization, and no door mints a second; a walled boot mints them through the auth API". Checked against the tree: (1) the multi-org runtime declared by the app: serve's stage-1 formatOrganizationsAbsentFatal (serve.ts:6415-6425), the hoisted-store refusal (:1326-1340 docblock, ADR-0093 D5), pinned live by serve-organizations-host-resolution.e2e.test.ts:185; (2) OS_PLATFORM_OWNER_EMAIL: environment-variables.mdx:104 "Required under the walled postures: unset or blank there refuses to boot", bootstrap-platform-admin.ts:914 "walled + undeclared ⇒ REFUSE BOOT"; (3) a declared membership policy: plugins/organizations/src/membership-policy-gate.ts refuses a walled boot whose auth.membership_policy is the manifest default (:57-66, WALLED_MEMBERSHIP_POLICY_UNDECLARED :92), run at kernel:bootstrapped by organizations-plugin.ts:306-312; an explicit auto is accepted (:57-59), which RUNNER.md's "a declared membership policy (for example invite-only)" states correctly. The second entry: beforeCreateOrganization refuses only when !this.multiOrgPostureEffective() (auth-manager.ts:3570-3577), so a walled boot admits the create. One loose sentence: RUNNER.md:54-55 says the seven walled e2e cases "spawn that boot"; they spawn a walled os serve with a stand-in organizations package (host-resolution.e2e.test.ts:60-67, :96-101) and set only the posture and the owner email (mount-failure.e2e.test.ts:139-144; host-resolution:150-155), so they do not exercise the membership gate (③ R6). Grep on the branch for any claim that 18.x refuses a walled posture, and for walledPostureRefusal / walledPostureFinding / WALLED_POSTURE_RELEASE_NOTE: production source, content/docs, RUNNER.md 0 hits; the remaining walledPostureRefusal hits are all under docs/qa/platform-checklist/areas/, each either a history entry (dated 2026-10-19 or earlier, correct as history) or the re-pointed sentence "the 18.0 refusal (serve.ts walledPostureRefusal) is deleted"; .changeset/pre/15212-180-guards-fresh-single-only.md:5 is the consumed 18.0 changeset (history); access-security.json:2825 ("a walled posture refuses to boot with it unset") is the owner-email prerequisite, true. On main, RUNNER.md:461 and the old knownGaps at access-security.json:2367, :2490, :3374, :3689, :3781, platform-core.json:3229 still carry the refusal; this branch replaces each. The checklist gate (Lint, docs and code guards) is green on the head; the dev reports check-platform-checklist full and --changed-from OK.

6. Nothing else regressed in the delta; the #24681 interaction is as designed.

The dev's own changes touch no serve.ts, doctor, webhooks, dogfood or docs file: engine.ts +12 lines inside one method, the tenancy module, the barrel, three tests, one package.json, one vitest config, the lock, one changeset, RUNNER.md and three checklist JSONs. The merge brought main's own work unaltered (pure merge, above). R1-R4 re-read on this head by the dev (6281802782, after a fresh install and full build): R1 fresh single boot, discovery 200, 0 refusals; R2+R3 harness with webhook-materialization and the walled declared-webhook pin, 3 files / 15; R4 as restated, isolated os serve with the real Organizations runtime, discovery 200, Organizations mounted, 0 refusals, plus the 7 walled e2e cases 7/7. The same caveat as before: the dogfood shards and the harness do not run on pull_request (skipped in the head's check runs), so R2, R3 and the real-runtime R4 rest on the dev's local runs; the e2e-tier and unit suites are on the farm. #24681 (edb2db1e8, merge-base with main 7c509c286): it flips both tables (systemFields: { tenant: false }) and touches none of engine.ts, system-write-organization.ts or the tripwire (diff empty), so landing it after this PR reddens the tripwire as designed and obliges it to delete the list, the engine reader and the tripwire block. Its merge keeps the three pins green: with no tenant column resolveTenantFieldName answers null (:234-243) and the organization-less rows land, which is what each pin asserts (organization_id ?? null is null). Its settings-service.ts change drops the organization_id spread from the ciphertext row (:2121-2124) and the organization_id? member from SettingsSecretStore.insert (settings-service.types.ts:245-251); the settings pin reads organization_id off the stored row only (:115), never off the insert type, so it compiles and stays green. Merge-tree of this branch with #24681 conflicts on content/docs/deployment/cli.mdx (as the prior record said) and on packages/runtime/src/organization-ownership-boot-guard.{ts,test.ts}; the latter two are #24681 against main's #24682 (9e2722a92), since merge-tree origin/main with #24681 shows the same two and this PR does not touch the boot guard (diff empty). declared-webhooks.ts auto-merges. Neither this PR's body nor addendum 4 is a note on #24581/#24681 itself (the PR body carries "Note for the #24681 dev"); the seat should relay (③ merge order).

7. Semver, Clause-② and the changesets. Unchanged from the prior record: four changesets, every one minor; the objectql one Clause-②: yes (narrowing), **BREAKING**, now also recording two added exports (additive); check-changeset-no-major's level axis met; no major, no governed path (the dev's check-governed-merges --branch: 0 governed paths, 2033 changed lines).

8. CI on f73d8891a. 49 check runs, all completed, none failed: 42 success, 7 skipped. Success: Test Core 1/6 through 6/6 and the aggregate, Build Core, Build Docs, Type Check (workspace, source gates, consumer gates, debt ledger) and the aggregate, Lint (ESLint, repo and tooling gates, docs and code guards, CI/release/engine gates, PM dispatch-gates, the scoped lanes) and Lint & Repo Gates, Check Changeset (twice), Check PR Size, Validate Package Dependencies, Governed Surface Queue Guard, Spec property liveness, Check Documentation Links, Flag docs affected, the four PR-claim guards (each twice, once on the edited event), Dogfood Regression Gate (aggregate), filter, Auto Label. Skipped by design: the Dogfood Regression Gate shards (not on pull_request), Dogfood Verify CLI, Temporal Conformance, Console Pin Gate, Packed-tarball smoke (opt-in), and the edited-event reruns of Auto Label and Check PR Size. mergeable_state: clean, base 1c1144fcd equals the merge-base, still a draft.

② Semver level

minor on @objectstack/objectql, @objectstack/plugin-webhooks (two changesets) and @objectstack/cli, under the launch-window convention for breaking changes. The bridge adds two exports to objectql and narrows nothing further; the adr-0087 marker records them. Agreed, as before.

③ Boundary flags

Implemented-by: claude/issue-24580-walled-lift
Reviewed-by: session_01Y5583bs2RDd53MH6kM1ca3

VERDICT: PASS

No blocking items. Residuals R1-R7 and the merge-order note above; R2 (keep the flow-credential pin when #24681 deletes the tripwire) is the one the seat should relay to #24581/#24681 before that PR lands.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 20, 2026 02:30
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 20, 2026 02:30
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 20, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Oct 20, 2026
…lled-lift

# Conflicts:
#	docs/qa/platform-checklist/areas/access-security.json
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 20, 2026 03:52
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 20, 2026
Merged via the queue into main with commit fc34d00 Oct 20, 2026
42 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-24580-walled-lift branch October 20, 2026 04:30
os-steve pushed a commit that referenced this pull request Oct 20, 2026
Conflicts and restatements, the ceremony being open on this branch:

- runtime boot guard, its unit, kernel and dogfood pins: #24682's 18.0
  "fresh installs only" refusal gives way to the marker-based refusal that
  names the ceremony's plan and apply.
- cli.mdx: the open-ceremony text kept; #24678 and step 4's "until then the
  ceremony refuses" sentences restated; #24722's audit-mail-bodies row and
  section kept beside it.
- B2: #24661's seven 18.0 upgrade-guide entries prescribe the ceremony again
  (the ups entry keeps the full apply), pinned in
  organization-ownership-upgrade-entries.test.ts.
- metadata-lifecycle, metadata-service, capabilities, permission-sets: the
  18.0 deferral restated.
- platform-core.v18-boot-refusals revision 5.
- objectql: WALLED_ORGANIZATION_LESS_BRIDGE, isWalledOrganizationLessBridgeObject
  and the engine reader are deleted (sys_secret and sys_flow_credential are
  column-less here); the refusal names systemFields: { tenant: false }. The
  tripwire is deleted; the flow-credential walled pin is kept, renamed
  walled-flow-credential-write.test.ts.
- #24688's principal x door matrix restated over a ceremonied fixture.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014UATXWqAXqkaZkbG92ox6V
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation size/xl tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(objectql,cli)!: 18.x step 4: the walled lift (8b walled half plus the walled part of #24489)

1 participant