Repository navigation
fix(cli)!: refuse a production os serve that composes no organization object, --server or not - #25155
Conversation
…--server Claude-Session: https://claude.ai/code/session_019pgvPFjtU47ULgfY7bZc6Q Co-authored-by: Claude <noreply@anthropic.com>
…n-less production boot refusal Claude-Session: https://claude.ai/code/session_019pgvPFjtU47ULgfY7bZc6Q Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019pgvPFjtU47ULgfY7bZc6Q Co-authored-by: Claude <noreply@anthropic.com>
…gine composes Claude-Session: https://claude.ai/code/session_019pgvPFjtU47ULgfY7bZc6Q Co-authored-by: Claude <noreply@anthropic.com>
…ck:cli-test-child-env reads it Claude-Session: https://claude.ai/code/session_019pgvPFjtU47ULgfY7bZc6Q Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckNothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)), so this run has no opinion about the docs. What this run could not see
Coarse fallback — 30 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
…ts the no-auth refusal now reaches The banner and transport pins boot today's production kernel-only command (auth composed, a store); the lifetime and no-storage pins, whose shape is no store and no auth composed, boot the cloud runtime's host kernel, the one production composition that still composes no auth. Claude-Session: https://claude.ai/code/session_019pgvPFjtU47ULgfY7bZc6Q Co-authored-by: Claude <noreply@anthropic.com>
…r, and this one names --preset minimal The seat widened the file surface to the one pending changeset that would otherwise publish the remedy this fix removes. Claude-Session: https://claude.ai/code/session_019pgvPFjtU47ULgfY7bZc6Q Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Read-only. The head ① Derived judgmentsItem 1 (ruling item 1, letter A). Executed as ruled, for both compositions, with or without
Item 2 (ruling item 2). No "tenant-scoped objects present" qualifier: neither condition reads the object set or any Item 3 (the consequence the dev flags). A production Item 4 (ruling item 3, pins). All present in Item 5 (ruling item 5, counting). Census at the head: 16 cli test files name Item 6 (ruling item 6). The changeset's remedy list states: "A cloud migrate-and-exit job ( Item 7 (ruling item 4, docs). ② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS |
…kipped-with-a-warning sentence; os start takes no --no-server Claude-Session: https://claude.ai/code/session_019pgvPFjtU47ULgfY7bZc6Q Co-authored-by: Claude <noreply@anthropic.com>
|
Seat note: deliberate correction of a pending release note · epic PM
|
…fuse-orgless-production-boot
|
Maintainer confirmation · recorded by epic PM This confirms the DELIBERATE CORRECTION named in 6331173130. |
|
Seat ACCEPT · epic PM Seat check
|
…on no-organization refusal Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UQCyrGmMy6kwav9XCzAuQ9
Fixes #25133
Clause-②: yes (narrowing)
Metadata-protocol: none
Executes the maintainer's ruling 6328987873 (letter A) under the claim 6329345390. Dispatched by the epic seat (
epic:#15194,session_01Y5583bs2RDd53MH6kM1ca3). Ships as an 18.x patch. Review tier:CONTRACT_REVIEW_TIER, because the diff declares a narrowing.What changed
packages/cli/src/commands/serve.ts: a productionos serve/os start(no--dev) that composes no organization object is now refused whether or not--server, before any plugin starts:authtier is off and noAuthPluginis mounted (A1). The condition goes fromflags.server && noAuthto(flags.server || !isDev) && noAuth. A development boot keeps the old shape: it is refused only when it serves.no-secret). The condition dropsflags.server.no-secretis only ever the reason on a production boot, because a development boot resolves the fallback secret, so this needs no posture check of its own.host-kernelreason (the cloud runtime's warn-only path);os devand--dev;Ruling item 5: the count
15 test files name
--no-server. 8 of them carry neither--devnor an auth secret, not 9:serve-datasource-admin-routes,serve-http-server-composition,serve-kernel-only-hold,serve-no-transport-announcements(unit),serve-observability-registration,serve-unknown-hostname-guard,format.server-ready-no-http-server. Each readsserve.tssource or calls a pure function.serve-migrate-exit-schema-parity.integration. It spawns throughbin/run-dev.js, which pinsNODE_ENV=development, so it is a dev boot and is untouched.The one test that really boots production
--no-serverwithout a secret istest/serve-ready-degraded-boot.e2e.test.ts(nightly tier). It carriesOS_AUTH_SECRETin its healthy leg only, which is why the flag/secret count misses it. Its degraded leg is re-fixtured onto the one production composition that still skips auth with a warning: a host-kernel stub, a plugin registered underObjectOSEnvironmentPlugin's name.Missed by that count, found by CI on
5996c531: four cli integration files booted production--no-server --preset minimal.minimalcomposes no auth (tiers['core']), so ruling item 1's no-auth refusal now reaches them:serve-no-server-banner,serve-no-transport-announcements,serve-kernel-only-lifetimeandserve-no-storage-boot-guard(6 tests). They are re-fixtured in6531b332. The banner and transport legs boot today's production kernel-only command (auth composed, a store). The lifetime and no-storage legs, whose shape is no store and no auth, boot the host-kernel stub with tiers['core','auth']and still pass--preset minimal. Consequence: a productionos serve --preset minimalis now refused with or without--server, unless the stack mounts AuthPlugin itself. It was already refused with--server. This is ruling item 1 as written (report 6330837413).Non-test callers: no workflow, Dockerfile, example or script in the repo runs
--no-server.os startalways hands itsservechild a secret.Pins:
packages/cli/test/serve-no-auth-secret-refusal.integration.test.ts(10 legs)All legs spawn
bin/run.jswithNODE_ENVunset.--server;--no-server;--no-serverwithOS_MIGRATE_AND_EXIT=1;--server;--no-server(with a secret).--server;--no-server;--dev --no-serverwith no secret;--no-server(asserts the warn-only line);--dev --no-server.Proof (head
6531b332; CI green: 42 check runs, 0 failing, Test Core 6/6)serve-ready-degraded-boot.e2e(OS_TEST_TIERS=nightly): 2/2.vitest run --project unitreports 321/321 files and 4746/4746 tests. LockVERDICT command-exit 0.Test Core).ablation-replace.mjsturns the no-secret condition back toflags.server && …, the cli is rebuilt, andablation-dist-preflightconfirms the marker is indist/.--no-server("the boot was not refused") and the migrate-and-exit leg (it exited 0).flags.server && ….--no-server.--absentconfirms both markers are gone from all 680 built files and the tree is clean.node scripts/pm/dispatch-gates.mjs --commands: 101 families, every exit code captured before any pipe.check:cli-test-child-env(exit 1, a real finding).5996c531:NODE_ENV: undefinednow sits after each leg's env spread.--ranaccounts for 101 of 101.check-governed-merges --branch: exit 0, NOT governed (0 of 6 paths), +276 / -63.pnpm dev:crm -- --freshreached ready, health 200;os serveofexamples/app-crmwithOS_AUTH_SECRETreached ready, health 200;--no-serverand no secret exits 1 with the new text, and its database is never opened.os serve(OS_TENANCY_POSTURE=group,invite-only,OS_PLATFORM_OWNER_EMAIL, a secret) on a scratch app that declares@objectstack/organizationsreached ready withOrganizationscomposed, health 200.Docs
content/docs/deployment/cli.mdx: the--no-serverexample and the flag description now say a production kernel-only boot is held to the same auth rule.content/docs/upgrading.mdx: the 18.0 auth-secret paragraph now covers no-auth,--no-server, migrate-and-exit, and the unaffected boots.content/docs/releases/**is not touched (ruling item 4). The changeset carries the correction for the v18 page's--no-serversentence.Acceptance notes
tenancy.enabled: falseremedy was left out on purpose. The dispatch asked for it as an operator remedy, but ruling item 2 writes no qualifier, so declaring it does not lift the no-auth refusal. Offering it in the refusal text would advertise a fix that does not work. The changeset says so explicitly..changeset/24583-7b3-organizationless-counter-retired.md(already shipped in 18.0.0'sdist, still pending in.changeset/) contradicts this change twice: "Unchanged:--no-server(kernel only) still boots with the warning" and "or run kernel-only with--no-server". It is outside the claimed file surface and was not edited. This PR's changeset states that it supersedes both sentences. The seat may want that file corrected before the next version cut.test-budget:, because there is no CI reading yet.Generated by Claude Code