Skip to content

fix(cli)!: refuse a production os serve that composes no organization object, --server or not - #25155

Merged
objectstack-fleet[bot] merged 9 commits into
mainfrom
claude/issue-25133-refuse-orgless-production-boot
Oct 22, 2026
Merged

objectstack-fleet[bot] merged 9 commits into
mainfrom
claude/issue-25133-refuse-orgless-production-boot

Conversation

@objectstack-fleet

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

Copy link
Copy Markdown
Contributor

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 production os serve / os start (no --dev) that composes no organization object is now refused whether or not --server, before any plugin starts:

  • The auth tier is off and no AuthPlugin is mounted (A1). The condition goes from flags.server && noAuth to (flags.server || !isDev) && noAuth. A development boot keeps the old shape: it is refused only when it serves.
  • The tier is on but no secret resolved (no-secret). The condition drops flags.server. no-secret is 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.
  • Both refusal texts drop the "run with --no-server" remedy and the "drop the REST/dispatcher plugins" remedy. Both were offered as ways to serve without the data API, and both still write ownerless rows. Each text now says that kernel-only does not avoid the refusal. The no-secret text also names the migrate-and-exit job.
  • Unchanged:
    • the host-kernel reason (the cloud runtime's warn-only path);
    • os dev and --dev;
    • programmatic embedding, which never runs this command.
  • Ruling item 2: no "tenant-scoped objects present" qualifier is written. I measured no composition it would spare (see Acceptance notes).

Ruling item 5: the count

15 test files name --no-server. 8 of them carry neither --dev nor an auth secret, not 9:

  • 7 are unit-level and boot nothing: 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 reads serve.ts source or calls a pure function.
  • 1 spawns, but not in production: serve-migrate-exit-schema-parity.integration. It spawns through bin/run-dev.js, which pins NODE_ENV=development, so it is a dev boot and is untouched.

The one test that really boots production --no-server without a secret is test/serve-ready-degraded-boot.e2e.test.ts (nightly tier). It carries OS_AUTH_SECRET in 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 under ObjectOSEnvironmentPlugin's name.

Missed by that count, found by CI on 5996c531: four cli integration files booted production --no-server --preset minimal. minimal composes 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-lifetime and serve-no-storage-boot-guard (6 tests). They are re-fixtured in 6531b332. 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 production os serve --preset minimal is 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 start always hands its serve child a secret.

Pins: packages/cli/test/serve-no-auth-secret-refusal.integration.test.ts (10 legs)

All legs spawn bin/run.js with NODE_ENV unset.

  • Refused, with the database never opened:
    • no-secret with --server;
    • no-secret with --no-server;
    • no-secret --no-server with OS_MIGRATE_AND_EXIT=1;
    • no-auth with --server;
    • no-auth with --no-server (with a secret).
  • Boots, reaching ready and opening the database:
    • a configured secret with --server;
    • a configured secret with --no-server;
    • --dev --no-server with no secret;
    • the host kernel with no secret, --no-server (asserts the warn-only line);
    • no-auth with --dev --no-server.

Proof (head 6531b332; CI green: 42 check runs, 0 failing, Test Core 6/6)

  • The changed files, locally:
    • the pin file: 10/10;
    • serve-ready-degraded-boot.e2e (OS_TEST_TIERS=nightly): 2/2.
  • cli unit layer: vitest run --project unit reports 321/321 files and 4746/4746 tests. Lock VERDICT command-exit 0.
  • cli typecheck: exit 0.
  • cli integration layer: the rest of it is declared to CI (Test Core).
  • Ablation 1: ablation-replace.mjs turns the no-secret condition back to flags.server && …, the cli is rebuilt, and ablation-dist-preflight confirms the marker is in dist/.
    • Result: 2 failed and 8 passed. The red pins are no-secret --no-server ("the boot was not refused") and the migrate-and-exit leg (it exited 0).
    • Restore: blob == HEAD.
  • Ablation 2: the no-auth condition goes back to flags.server && ….
    • Result: 1 failed and 9 passed. The red pin is no-auth --no-server.
    • Restore: blob == HEAD. After a rebuild, --absent confirms 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.
    • Five were non-zero on the first sweep: four prerequisite refusals (exit 3: a shallow clone, and unbuilt packages) and check:cli-test-child-env (exit 1, a real finding).
    • The finding is fixed in 5996c531: NODE_ENV: undefined now sits after each leg's env spread.
    • All five re-ran at exit 0 after a full build and a fetch of the pinned fixture commit. --ran accounts for 101 of 101.
  • check-governed-merges --branch: exit 0, NOT governed (0 of 6 paths), +276 / -63.
  • R1:
    • pnpm dev:crm -- --fresh reached ready, health 200;
    • a production os serve of examples/app-crm with OS_AUTH_SECRET reached ready, health 200;
    • the same app with --no-server and no secret exits 1 with the new text, and its database is never opened.
  • R4: a walled production os serve (OS_TENANCY_POSTURE=group, invite-only, OS_PLATFORM_OWNER_EMAIL, a secret) on a scratch app that declares @objectstack/organizations reached ready with Organizations composed, health 200.

Docs

  • content/docs/deployment/cli.mdx: the --no-server example 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-server sentence.

Acceptance notes

  • The tenancy.enabled: false remedy 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.
  • The pending changeset .changeset/24583-7b3-organizationless-counter-retired.md (already shipped in 18.0.0's dist, 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.
  • The degraded-boot e2e's healthy and degraded legs now differ in config (bare vs host-kernel stub) as well as secret. Its header says so.
  • The pin file now spawns 10 boots, about 34 s of wall time locally. It declares no test-budget:, because there is no CI reading yet.
  • Edits were first made on the session's primary checkout (the claim's "the cloud session's own checkout"). They moved to a linked worktree once the guard hooks refused; no content was lost.

Generated by Claude Code

@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Oct 22, 2026
@github-actions

github-actions Bot commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing 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
  • 1 anchor(s) matched too much of the corpus to be a work list: os serve (command, 38 pages)
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • 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 — 30 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 23540582f4f7a783ee11337bbda12a8352a70f91 → packageMentionDocs.

…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>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 6531b3324328c3310f23e01f66abe5933129758d
Local-runs: none
Scope: .changeset/25133-refuse-orgless-production-boot.md, content/docs/deployment/cli.mdx, content/docs/upgrading.mdx, packages/cli/src/commands/serve.ts, packages/cli/src/commands/serve-kernel-only-lifetime.integration.test.ts, packages/cli/src/commands/serve-no-server-banner.integration.test.ts, packages/cli/src/commands/serve-no-transport-announcements.integration.test.ts, packages/cli/test/serve-no-auth-secret-refusal.integration.test.ts, packages/cli/test/serve-no-storage-boot-guard.integration.test.ts, packages/cli/test/serve-ready-degraded-boot.e2e.test.ts; read to judge them: packages/core/src/stack-auth.ts, packages/core/src/kernel.ts, packages/runtime/src/runtime.ts, packages/cli/src/commands/start.ts, packages/cli/src/commands/dev.ts, packages/cli/test/helpers/serve-process.ts, packages/cli/test/serve-migrate-exit-schema-parity.integration.test.ts, .changeset/24583-7b3-organizationless-counter-retired.md, content/docs/releases/v18/index.mdx, content/docs/automation/webhooks.mdx, packages/cli/CHANGELOG.md, scripts/check-adr-0087-registration.mjs, scripts/measure-test-shard-timings.mjs, AGENTS.md

Read-only. The head 6531b332 was checked out in a detached worktree from origin/claude/issue-25133-refuse-orgless-production-boot (merge-base with origin/main 23540582f4 is 4c7dcb4519) and diffed against that merge-base: 10 files, +346 / -81. The ruling 6328987873 (seven binding items), the seat's effort note 6328820987, the claim 6329345390, both dev reports (6330211620, superseded by 6330837413), the measurement 6323585273 section 2 and the PR body were read from the GitHub API; the check-runs for the head were read the same way; node scripts/pm/check-governed-merges.mjs --branch ... was run (read-only). Nothing was built, no test was run, nothing was posted. The ablation counts are judged structurally from the pins, not re-run.

① Derived judgments

Item 1 (ruling item 1, letter A). Executed as ruled, for both compositions, with or without --server.

  • No-auth case (auth tier off, no AuthPlugin mounted): packages/cli/src/commands/serve.ts:4674-4675 sets const productionBoot = !isDev; and the condition is (flags.server || productionBoot) && !(tierEnabled('auth') || hasAuthPlugin) — a production boot is refused regardless of the flag; a development boot keeps the old flags.server gate, as the ruling leaves --dev unchanged. isDev is isDevelopmentBoot(flags.dev) (serve.ts:2414; packages/core/src/stack-auth.ts:135-137: --dev or NODE_ENV === 'development').
  • No-secret case: serve.ts:4704 is now !authComposition.composes && authComposition.reason === 'no-secret' with the flags.server guard removed. The dev's claim that no-secret is production-only holds by construction: resolveAuthSecret({ isDev }) returns the fallback on every development boot (stack-auth.ts:145-148), and resolvePlatformAuthComposition returns no-secret only when secret is undefined (stack-auth.ts:184).
  • Before any plugin starts: both throws precede await runtime.start() (serve.ts:5423); runtime.use → ObjectKernel.use only registers (packages/core/src/kernel.ts:347-377, state must be idle, no init or start), and the OS_MIGRATE_AND_EXIT=1 short-circuit sits after runtime.start() (serve.ts:5491), so a migrate-and-exit job meets the same throw before the kernel bootstraps. The pins prove the SQLite file is never created on a refused leg (packages/cli/test/serve-no-auth-secret-refusal.integration.test.ts:159) and is created on every control leg (:169), including the OS_MIGRATE_AND_EXIT=1 leg (:209-219).
  • Both texts drop the "run with --no-server" remedy (serve.ts:4680-4683, :4709-4712) and each now says kernel-only does not avoid the refusal; the pin asserts the old phrase "serve without the data API" is absent (:158).
  • host-kernel stays warn-only: the reason is resolved before no-secret (stack-auth.ts:181-184), so the no-secret refusal cannot fire there; the warn is unchanged at serve.ts:4160-4164, and the control leg asserts it (:238-245). Note the host-kernel reason requires the auth tier on; a host kernel with the tier off is auth-tier-off and is refused under the no-auth condition (see B3).
  • os dev hands --dev to serve (packages/cli/src/commands/dev.ts:781-782); programmatic embedding never runs this command. Unchanged.
  • os start: spawns serve with no --server/--no-server flag at all (packages/cli/src/commands/start.ts:524-534) and always with a secret (start.ts:315-317, AUTH_SECRET at :455), so it reaches only the no-auth refusal, which already fired under --server on main. Unchanged by this PR.
  • Other production shapes: --preset minimal resolves tiers ['core'] (stack-auth.ts:62) → auth-tier-off → refused either way. A config that mounts its own HonoServer with --no-server: the refusal reads no transport, so it is refused iff no auth composes. --no-server with NODE_ENV=development is a development boot and is not refused. A stack that mounts its own AuthPlugin is stack-supplies-auth and composes an organization object; outside the ruling's two cases.
  • Still not refused, outside the ruling's two named compositions: the auth tier on with a secret set, but @objectstack/plugin-auth fails to load — serve.ts:4573-4579 warns and boots on with no AuthPlugin and no organization object (B2). An install failure, not a configuration; pre-existing on main.
  • Nothing legitimate refused: a production boot with a secret reaches ready with and without --server (pins :221, :225), --dev boots (:234, :265), the host kernel boots (:238).

Item 2 (ruling item 2). No "tenant-scoped objects present" qualifier: neither condition reads the object set or any tenancy declaration (serve.ts:4675, :4704). The changeset states that tenancy: { enabled: false } does not lift the refusal, which is true of the code.

Item 3 (the consequence the dev flags). A production os serve --preset minimal is refused with or without --server unless the stack mounts AuthPlugin: this follows item 1 as written, since minimal is ['core'] and the no-auth condition is exactly "tier off, no AuthPlugin". Documentation: the changeset FROM/TO covers it only generically ("when the stack mounted no auth at all") and never names --preset minimal; in cli.mdx every --preset minimal mention is unchanged — :358 ("Skip auto-loaded auth/i18n/ui plugins"), :375, and the tier table :395 (minimal | core | none) — and the sentence at :399-401 still says "The auth tier requires OS_AUTH_SECRET to be set; otherwise AuthPlugin is skipped with a yellow warning", which contradicts the paragraph the PR wrote at :365-371. git grep -n "preset minimal" origin/main -- content/docs packages/cli/src lists cli.mdx:358, :369, webhooks.mdx:130 (messaging only, still true), serve.ts:632, :1476, :2925, security-catalog-overlays.ts:130 and schema-migration-plugins.ts:1934; none is annotated on the branch (B1).

Item 4 (ruling item 3, pins). All present in serve-no-auth-secret-refusal.integration.test.ts: no-secret with --server (:198), no-secret --no-server (:202), no-secret --no-server + OS_MIGRATE_AND_EXIT=1 (:209), no-auth with --server (:249), no-auth --no-server with a secret (:253); controls: a configured secret with and without --server (:221, :225), --dev --no-server (:234), host-kernel --no-server asserting the warn line (:238), no-auth --dev --no-server (:265). Every leg spawns bin/run.js with NODE_ENV: undefined written after the leg's env (:118-122); childEnv spreads overrides last and spawn omits undefined entries (packages/cli/test/helpers/serve-process.ts:881-889), so production posture cannot be overridden. Refused legs prove nothing was written by the absent database file (:159) against controls that create it (:169). Ablation, judged structurally: putting flags.server && back on the no-secret condition leaves :198 green and reds :202 and :209 — 2 red; on the no-auth condition it reds :253 only — 1 red. Matches the dev's stated 2 / 1. Nothing was removed from the pre-PR file: its two legs survive with every assertion (git diff against the merge-base), plus the new absent-remedy assertion. The four re-fixtured files each still test their own purpose: serve-no-server-banner (:302-311, :349-354) and serve-no-transport-announcements (:302-315, :364-369) boot today's production kernel-only command (default tiers, a secret, a store) and keep expectNoServerShape, expectDatasourceAdminDegradeLine, the no-objectstack:listening and no-state-file assertions, and the config-mounted-HonoServer control; serve-kernel-only-lifetime shape 1 (:282-288) keeps the defect's mechanism (no storage driver, so no pool) on a host-kernel stub with tiers: ['core', 'auth'] and still --preset minimal, with the held-then-SIGTERM-exit-0 assertion intact; serve-no-storage-boot-guard case 2 (:274-277) keeps "one line before any start() and the banner still reached" on the same stub. serve-ready-degraded-boot.e2e moves its degraded leg onto the host-kernel stub and keeps the healthy leg on the bare config with a secret; the discriminating assertion ("an always-append implementation passes the degraded leg and fails here") still holds, and the header says the legs now differ in config as well as secret. No test weakened.

Item 5 (ruling item 5, counting). Census at the head: 16 cli test files name --no-server; 7 are unit-level with no spawn (serve-datasource-admin-routes, serve-http-server-composition, serve-kernel-only-hold, serve-no-transport-announcements.test, serve-observability-registration, serve-unknown-hostname-guard, format.server-ready-no-http-server); the two under migrate/ carry --dev and a secret; serve-migrate-exit-schema-parity.integration spawns CLI from helpers/serve-process.js, which is bin/run-dev.js (serve-process.ts:21-22, pins NODE_ENV=development), so it is a development boot and is rightly untouched. The dev counted first (PR body, report 6330211620); the flag-times-secret method missed the four --preset minimal files because each carries --dev or OS_AUTH_SECRET in other legs; CI on 5996c531 caught them and 6531b332 re-fixtured exactly those four plus the nightly e2e. Only real production boots were re-fixtured. The PR body still carries the first count (B5).

Item 6 (ruling item 6). The changeset's remedy list states: "A cloud migrate-and-exit job (OS_MIGRATE_AND_EXIT=1) must carry OS_AUTH_SECRET in production; without it the job is now refused, naming the variable." The no-secret text names it (serve.ts:4709-4710: set OS_AUTH_SECRET ... "A migrate-and-exit job (OS_MIGRATE_AND_EXIT=1) needs it too"), and the pin :209-219 proves the job is refused with that text. upgrading.mdx:421-422 and cli.mdx:369-371 repeat it. The cloud seat's verification itself is outside this repo.

Item 7 (ruling item 4, docs). cli.mdx:357 (the --no-server example) and :365-371 (the flag description) are edited; upgrading.mdx:413-426 is edited. No commit on the branch touches content/docs/releases/** (git log over the range is empty); content/docs/releases/v18/index.mdx:61 still offers --no-server, as the ruling requires it to be corrected through the release-notes process. Every sentence the PR wrote is true against the code: the --no-server paragraph (refused before any plugin starts, migrate-and-exit included), the upgrade paragraph (no-auth refused the same way, earlier 18 releases let --no-server through and offered it as a remedy, os dev/--dev/embedded/host kernel unaffected, os start unaffected). One over-generalisation: cli.mdx:357 "production still needs OS_AUTH_SECRET" does not hold for a stack that mounts its own AuthPlugin (cosmetic). Other places on the branch that still offer --no-server as the remedy for a missing secret: only the two the dev named, .changeset/24583-7b3-organizationless-counter-retired.md:18 and :24, plus the release page the ruling reserves. packages/cli/CHANGELOG.md:1131, :1733 and serve.ts:632 narrate shipped history and offer no remedy (B6, B8).

② Semver level

'@objectstack/cli': patch with Clause-②: yes (narrowing) and Metadata-protocol: none on line-initial declarations in both the changeset and the PR body; a BREAKING banner; FROM and TO; a remedy list; and exactly one ADR-0087 marker, not-required (no-migration-prescription), a category the gate lists (scripts/check-adr-0087-registration.mjs:147, :287). That is the level AGENTS.md:1087-1100 gives a non-protocol narrowing: patch, keeping its BREAKING banner and ADR-0087 disposition. Check Changeset is green on the head. Truth of the body: the TO, the Unchanged paragraph (including "os start always hands its serve child an auth secret, so it reaches the no-auth case only"), the tenancy: { enabled: false } sentence and the release-notes correction are all true against the code and the two files it cites. The FROM's "a production os serve / os start (no --dev) with --no-server booted the kernel" is imprecise for os start, which has no --no-server flag and always serves (B4). The remedy list matches the refusal texts: no-secret → set OS_AUTH_SECRET with the openssl rand -hex 32 example (serve.ts:4709-4710); no-auth → enable auth (the auth tier or AuthPlugin in plugins) and set OS_AUTH_SECRET (:4680-4681); migrate-and-exit → :4710. Level and declarations correct.

③ Boundary flags

  • B1 — not BLOCKING. cli.mdx:399-401 still says a missing OS_AUTH_SECRET makes AuthPlugin "skipped with a yellow warning", which is false for every production boot except the host kernel and contradicts the paragraph the PR wrote at :365-371; and no --preset minimal mention (:358, :375, :395) says a production boot on it is refused. The sentence was already false on main for a --server boot since 18.0.0, and the --preset minimal consequence is item 1 as written, so it does not fail the ruling; the fix is one or two lines inside the claimed file surface and should be folded in before merge, with --preset minimal named in the changeset's FROM/TO.
  • B2 — not BLOCKING. A production boot with the auth tier on and a secret set, whose @objectstack/plugin-auth fails to load, warns at serve.ts:4573-4579 and boots with no AuthPlugin and no organization object. Outside the ruling's two named compositions and pre-existing on main; a card for the cli lane.
  • B3 — not BLOCKING. A host kernel whose stack leaves the auth tier off is auth-tier-off, not host-kernel (stack-auth.ts:181-184), so in production it is now refused under --no-server too (it was already refused under --server). This is within the ruling, which keeps the warn-only path for the host-kernel reason, and the pins fixture the host kernel with the tier on; the cloud seat's item-6 check should also confirm its host compositions enable the auth tier. ObjectOSEnvironmentPlugin is not in this repo, so it is not measurable here.
  • B4 — not BLOCKING. Changeset FROM names os start "with --no-server"; os start has no such flag (start.ts:524-534). Wording only; the Unchanged paragraph says the right thing.
  • B5 — not BLOCKING. The PR body is stale: it cites head 5996c531, omits the four --preset minimal re-fixtures and the green CI on 6531b332. The dev listed the corrections in 6330837413; the seat should apply them before landing.
  • B6 — not BLOCKING. .changeset/24583-7b3-organizationless-counter-retired.md:18 and :24 still offer --no-server (outside the claim's surface; the seat has asked for the fold-in). No other doc or code comment on the branch offers it as the remedy. content/docs/releases/** is untouched, as ruled.
  • B7 — not BLOCKING. The pin file spawns ten boots (about 34 s locally) and declares no test-budget: line; the reader is scripts/measure-test-shard-timings.mjs, a report-only gate (AGENTS.md: a duration gate never reds a required job).
  • B8 — not BLOCKING. The serve.ts:632 header comment and CHANGELOG.md:1733 describe a production --no-server --preset minimal boot as a past defect's command; historical, now unreachable in production. Cosmetic.
  • CI on 6531b3324328c3310f23e01f66abe5933129758d: 42 check runs, all completed, 36 success, 6 skipped (Console Pin Gate, the Dogfood Regression Gate matrix template, Dogfood Verify CLI, Temporal Conformance, Packed-tarball smoke, 18.x upgrade harness), 0 failure, 0 pending. Test Core 1/6 through 6/6, Build Core, TypeScript Type Check, Lint and Repo Gates, Governed Surface Queue Guard and Check Changeset are green.
  • Size and governance: check-governed-merges --branch exit 0, NOT governed (0 of 10 paths hit the register), +346 / -81 over 10 files, under the 3000-line threshold.

Implemented-by: claude/issue-25133-refuse-orgless-production-boot
Reviewed-by: session_01Y5583bs2RDd53MH6kM1ca3

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>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Seat note: deliberate correction of a pending release note · epic PM session_01Y5583bs2RDd53MH6kM1ca3 (os-steve) · 2026-10-22T02:24Z.

Check Changeset is red. That is by design: check-empty-changeset's DELIBERATE CORRECTION class.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Maintainer confirmation · recorded by epic PM session_01Y5583bs2RDd53MH6kM1ca3 (os-steve) · 2026-10-22T04:44Z. The maintainer, verbatim: 「确认 25155 修正 7b-3 发布说明」.

This confirms the DELIBERATE CORRECTION named in 6331173130. .changeset/24583-7b3-organizationless-counter-retired.md is rewritten so that it no longer says a production --no-server boot without a secret "still boots", and no longer offers --no-server as a remedy. Check Changeset stays red on that one file by the gate's own design, and this comment is the human confirmation the gate asks for. The seat will land the PR once Test Core is green on the head.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Seat ACCEPT · epic PM session_01Y5583bs2RDd53MH6kM1ca3 (os-steve) · 2026-10-22T04:55Z. PR #25155 at 25b3a6c4bff905d462d4ff44030e5465c342254a (#25133, ruling 6328987873 letter A; claim 6329345390; os-dev-reports 6330211620, 6330837413). ACCEPT.

Seat check

  • Shape: cli serve.ts, refusal pins, cli.mdx, upgrading.mdx and two changesets. Not governed and under 3,000 lines, so this is an ordinary queue landing.
  • What it does: a production os serve / os start that composes no organization object is refused before any plugin starts, whether or not --server. That covers both cases the ruling names: the auth tier off with no AuthPlugin, and the tier on with no secret. OS_MIGRATE_AND_EXIT boots are refused the same way. Neither refusal text offers --no-server as a remedy any more. host-kernel, --dev, os dev, programmatic embedding and a boot with a secret are unchanged.
  • Review: CONTRACT_REVIEW_TIER (narrowing face). PASS 6331012625 at 6531b332: all seven binding items of the ruling are executed, the pins discriminate (both ablations go red), and no test is weakened.
  • Delta since the review (seat read):
    • 4746b166 and 9c2f301c: B1 and B4.
      • --preset minimal is named in the changeset's FROM/TO and at the three cli.mdx mentions.
      • cli.mdx's stale "skipped with a yellow warning" paragraph is replaced with the true refusal.
      • FROM no longer gives os start a --no-server flag.
      • 7b-3's pending note is corrected.
    • 25b3a6c4: a clean merge of main.
  • Pending release note corrected: .changeset/24583-7b3-organizationless-counter-retired.md (DELIBERATE CORRECTION, seat note 6331173130). The maintainer confirmed it verbatim: 「确认 25155 修正 7b-3 发布说明」 (6332920738). Check Changeset stays red on that one file by the gate's design, and it is not a required check.
  • CI:
    • Test Core (3/6) failed twice in @objectstack/create-objectstack (02:30Z, 02:52Z). There was no vitest assertion annotation, an npm 429 hit the same window, and the exact merge ref passes locally.
    • The maintainer re-ran the failed jobs of run 39713789453, and it passed (04:54Z). It is recorded as a registry or network flake.
    • The required checks (TypeScript Type Check, Test Core, Build Core, Lint & Repo Gates, Governed Surface Queue Guard) all succeeded on the head.
  • Out of scope, and where it goes:
  • Landing: the seat flips the PR ready and enables auto-merge. Fixes #25133 closes the card.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 22, 2026 04:56
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 22, 2026 04:56
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 22, 2026
Merged via the queue into main with commit ab488d9 Oct 22, 2026
55 of 58 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-25133-refuse-orgless-production-boot branch October 22, 2026 05:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling

Projects

None yet

2 participants