Skip to content

Commit 24d622b

Browse files
os-billclaude
andauthored
feat(spec,cli): let an app contribute its own first-run credentials to the boot banner (devHint / devLogins[]) (#19139)
Fixes #17556 Clause-②: yes (widening) Suggestion 1 of #17081, filed onto its own card so PR #17555's `Closes` would not take it down with the parent. #17555 took the platform's half — the `🔑 Dev admin` line now says what that account will and will not see. This is the half `packages/cli` structurally could not write: the platform can describe the account it seeds, and it cannot know that an application's `Hiring` group needs a position or which of five personas a demo should open with. The application knows, and now has a channel to say so. ## What an operator sees Nothing changes unless the application declares something, and nothing changes outside a development boot. With both keys declared: ``` 🔑 Dev admin: admin@objectos.ai / admin123 seeded on empty DB · dev only — do not use in production platform admin — Setup, Studio and every record, but NO app-declared capability, so an app that gates navigation on requiredPermissions may show it an empty menu; grant it a permission set under Setup → Users, or sign in as an account your app seeds 👥 App logins: 2 declared by this app Hiring admin — admin@quillstone.example / demo1234 Job seeker — candidate01@mail.example / demo1234 declared in this app's `devLogins` · dev only — the platform seeded none of them 💡 App hint: run `pnpm seed:demo` first, then sign in as the Hiring admin ``` ## The accept-set delta, stated so a contract review can check it The top-level stack door has been strict since #8687, so both spellings were an `unrecognized_keys` refusal before this change. What is writable after that was not writable before, and nothing else: | surface | before | after | |:--|:--|:--| | `ObjectStackDefinitionSchema.devHint` | refused (`unrecognized_keys`, path `[]`) | `string`, optional | | `ObjectStackDefinitionSchema.devLogins` | refused (`unrecognized_keys`, path `[]`) | array of `DevLogin`, optional | | `DevLogin.email` | did not exist | `string`, required, email-validated | | `DevLogin.password` | did not exist | `string`, optional | | `DevLogin.label` | did not exist | `string`, optional | | any other key inside a `devLogins` entry | did not exist | refused — the entry is `strictObject` from birth | | any other new top-level key | refused | still refused (pinned as the dark control) | Nothing is renamed, nothing is retired, no value that parsed before is refused now, and the delta is recorded in two tracked artifacts a review can read rather than take on trust: `packages/spec/authorable-surface/system.json` gains exactly `system/DevLogin:email`, `:label`, `:password`, and `packages/spec/api-surface-declarations/root.txt` gains the two keys on `ObjectStackDefinitionSchema`. New exports, from `@objectstack/spec/system`: `DevLoginSchema`, `DevLogin`, `DevLoginParsed`. ## Where the declaration belongs, measured The card said its landing point was measured as `packages/spec` yet named only two `packages/cli` files. Both readings are right and they are two different halves: - **Declaration: `packages/spec`.** The banner prints once per boot, for the whole booted stack, so the key is stack-level — not per-`App` (N apps, one banner) and not per-package (the reader looks at the top level). `devPlugins` is the standing precedent for a development-only, CLI-consumed, stack-level key. - **Consumption: `packages/cli`.** `serve.ts` already holds the parsed definition in the same scope as the banner call (it reads `config.flows` two lines away, and `config.devPlugins` in the same function), so the values reach `printServerReady` with no new plumbing. `DevLoginSchema` itself lives in `packages/spec/src/system/dev-login.zod.ts`, mounted into `stack.zod.ts` — the same shape `StackServerConfigSchema` already has. Two reasons, one architectural and one mechanical: element schemas live in their domain file rather than in the 4000-line envelope, and declaring it inside `stack.zod.ts` displaced `ObjectStackDefinitionSchema` off the five-export line of `skills/objectstack-platform/references/_index.md`, dragging a PD-14 governed surface into this diff for a generated one-line churn. Measured both ways: with the schema in `stack.zod.ts`, `check:skill-refs` went red and regenerating produced that churn; with it in `system/`, `check:skill-refs` is green and the file is untouched. **This PR touches no governed surface** (`docs/adr`, `.claude`, `skills`, `AGENTS.md`, `CLAUDE.md` — the `docs/` path here is `docs/audits/`, a regenerated ratchet count). ## Suggestion 1 was not partly landed by #17555 — read first-hand PR #17555's file list is three files: its changeset, `packages/cli/src/utils/format.ts`, and one new test. No `packages/spec` file. Its body says so in its own words: the card's suggestion 1 "is the right long-term shape and is **not** taken here: it needs a new key on a published schema in `packages/spec`, another seat's lane". Independently re-derived on `origin/main` at `b7eaf6a61`: `git grep -nI` for `devLogins|devHint` printed **no lines at all**; the lit control (`seededAdmin`, the same subject) printed 3 files; the dark control (an impossible identifier) printed none. A second probe over `packages/spec/src` for any pre-existing dev-credential declaration (`devAdmin|devLogin|devCredential|firstRun|devPersona|seedAdmin`) printed exactly one line, an unrelated `"u_devadmin"` group key inside an approval-service docblock; lit control (`dev`-prefixed keys) printed 4 lines, dark control none. ## Three decisions the card left to the taker The card listed three, marked "⛔ not prescribed here". Taken, with reasons: 1. **Where the key lives, and one hint or a list — both, and stack-level.** They answer different needs and each has its own composition rule: `devLogins` is `'concat'`, so composing two applications keeps both publishers' personas (dropping one would hide an audience the composed artifact still serves — the `packages` argument, one layer over); `devHint` is `'single'`, so two stacks with contradicting first-run instructions is a composition error naming the key, never a silent last-wins. Both are artifact ENVELOPE keys: refused inside `packages[].manifest`, exactly as `devPlugins` is, because the banner's only reader looks at the top level and a folded copy would be parsed, stored and never printed. 2. **Is printing application-supplied credentials safe — yes, with two constructions.** The *content* is not an escalation: anyone who can write `devLogins` can already write `onEnable`, a function the dev process executes, so these values carry the trust level of the code being booted. Two things genuinely are new, and both are closed by construction rather than by assurance: - **Author-controlled text now reaches a TTY.** A terminal obeys control bytes, so an escape sequence in a hint could erase the rows above it or repaint a forged `🔑 Dev admin` row — the platform's own banner lying on the application's behalf. Every C0/C1 byte is replaced with U+FFFD before printing, so an entry occupies exactly the rows it was given. Visible characters pass through unchanged: this is neutralisation, not redaction, so an author who wrote one can see that they did. - **A production boot must not print them.** The block is gated on `isDev` (`--dev` or `NODE_ENV=development` — the same condition family that lets `maybeSeedDevAdmin` fire at all). Any other boot renders byte-identically to one declaring nothing, so ADR-0115's attention-budget paragraph is untouched on the path it was written about. The residual, stated rather than hidden: these values are committed to the application's repository already. The schema says so where an author reads it — development fixture, never a real secret, never a value that also opens a deployed environment. 3. **What the banner does when an application declares nothing — exactly what it does today.** Byte-identical, asserted for absent, empty-array and blank-string declarations, with a positive control proving the same comparison does move when something is declared. One decision the card did not list, taken the same way: the block is **additive**, beneath the seeded-admin lines and never instead of them. An application-controlled key able to suppress a platform disclosure would let an app hide a live credential the operator was just handed. And **declaring is not seeding**: an entry creates no account, which is why the banner says the application declared it — an entry naming an unseeded account then reads as the app's claim rather than as a broken platform. ## Red / green / dark New pins: `packages/spec/src/stack-dev-logins.test.ts` (13) and `packages/cli/src/utils/format.server-ready-app-logins.test.ts` (13). Rejections assert the Zod issue `code` and `path`, never a bare throw. - **Green** — both keys parse, carry their values through `safeParse` *and* through `defineStack` (the #8687 defect was a key that parsed green and was then stripped); `devLogins` concatenates across a compose; an identical `devHint` passes through. - **Red** — an undeclared key inside an entry raises `unrecognized_keys` at path `['devLogins', '0']`; `username` and `note` are routed to `email` and `label` by the curated aliases; a missing address raises `invalid_type` at `devLogins.0.email`; a non-address is refused at the same path; a non-string `devHint` raises `invalid_type`; two differing hints throw naming `top-level key 'devHint'`. - **Dark** — `devLoginz`, `devHints` and `devCredentials` still raise `unrecognized_keys` at path `[]`, so the green above reads as "two keys were declared" and not as "the strict close regressed". On the banner side, control-byte-free text passes through with no U+FFFD anywhere, so "the bytes were replaced" is a measurement. **Ablations** (`scripts/ablation-replace.mjs`, on-disk proof and restore verified by blob hash plus an empty `git diff HEAD` each time): | mutation | anchor | result | |:--|:--|:--| | drop `.email()` from `DevLogin.email` | 1 to 0, blob `d6b5d27f8e38` to `b0c0abd3c80c` | exactly **1 of 13** red — the address pin. The other 12 stay green, so the ablation is attributable rather than blunt | | delete the `devLogins` key from the stack shape | 1 to 0, blob `11f3c284217a` to `b2611a37d33f` | **8 of 13** red — every accept, refuse and compose pin. The accept leg is not vacuous | **Cross-package reverse verification** — `packages/cli` types against a schema built in `packages/spec`, so the green had to be shown to be a reading of the rebuilt `.d.ts` and not a cache. Replacing `entry.email` with `entry.nope` in `format.ts` (anchor 1 to 0, blob `8143c65513c9` to `1375d1655de6`) turned `pnpm --filter @objectstack/cli typecheck` red with `TS2339: Property 'nope' does not exist on type '{ email: string; password?: string | undefined; label?: string | undefined; }'` — the message quotes the exact shape declared in `system/dev-login.zod.ts`. Restored, blob back to `8143c65513c9`, `git diff HEAD` empty, typecheck green again. ## Verification Measured at `53f78a5796`, this branch. - `pnpm --filter @objectstack/spec test` — **495 files / 14534 tests pass**; `typecheck` green. - `pnpm --filter @objectstack/cli exec vitest run --project unit` — **218 files / 3077 tests pass**; `typecheck` green. The `integration` tier is declared to CI (`pnpm test` runs both); the diff adds no spawn entry point and changes no boot behaviour beyond two option values. - `pnpm --filter @objectstack/spec check:generated` — **16 of 16 artifacts up to date**. - `pnpm lint` (`eslint . --no-inline-config`, the whole repository, not a narrowed run) — **green in 78s**. - `node scripts/pm/dispatch-gates.mjs --ran` — **131 derived families accounted for: 131 run, 0 NOT MEASURED.** ⭐ Re-derived and re-run at head `9b0cba8b19` in the patch round, every family exit 0; the reconciliation reads it back as a DERIVED zero (all 131 recorded an exit code and none of them is 3). ⚠️ This **supersedes** the earlier reading of 「129 run green, 2 NOT MEASURED」: `check:dual-build-cjs-loads` and `check:type-check-debt` both exited 3 — PREREQUISITE NOT MET — on the first pass for want of a whole-workspace build, and both read **exit 0** once that build existed. The build was not bought for this line: five families in the re-derived set refused on the same prerequisite and running them is owed, and the build itself cost 4.9 s because 72 of its 73 tasks were turbo cache hits. Two gates asked for a hand edit and got one rather than a regeneration, because neither has a generator: `packages/spec/llms.txt` (the system domain is 34 schemas, the tree 202) and `content/docs/getting-started/quick-reference.mdx` (System Protocol is 16 of 34; the table is a curated subset, so N is left alone — adding a row is a decision, not a fix). ## Acceptance notes Found while measuring, deliberately **not** fixed here: - `packages/spec/src/stack.zod.ts`, the `STACK_DEFINITION_COLLECTIONS_SHAPE` docblock says the envelope keys "stay on the schema below and are deliberately NOT in here: `manifest`, `packages`, `api`, `server`, `i18n`, `runtimeModule` and `onEnable`". Five of those seven are in fact declared inside that shape (`api`, `server`, `i18n`, `onEnable`, `runtimeModule`); only `manifest` and `packages` are outside it. Comment only — no runtime or accept-set effect. The paragraph this PR does edit is the separate one about `plugins` / `devPlugins`, which was accurate and is kept accurate; the stale sentence is left untouched rather than corrected as a rider. - `packages/cli/src/commands/serve.ts`, in `resolveStorageCapabilityArg`'s header: the reason `config.storage` is not read says "the schema is not `.strict()`, so `defineStack` ... strips it before `serve` could ever see it". #8687 closed that door — the top level is `strictObject` and an undeclared `storage` is now refused at parse, not stripped. The conclusion still holds; the stated mechanism is one release stale. Comment only. Neither is a reproducible defect, a contract violation, or a trap that makes an author write metadata the runtime rejects or drops, so neither is filed as a card. ## What this PR does not do - It does not change what the first run **creates**. That is a product-shape decision and stays exactly as it was; only what the banner can be told to print moves. - It does not implement suggestion 3 of #17081 (an empty-navigation empty state), which is `objectui`. - It does not touch `packages/spec/src/ui/app.zod.ts`: an `App`-level field was considered and rejected on the measurement above (one banner per boot, N apps per stack), and that file is in flight on another card. --- _Generated by [Claude Code](https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3)_ --- ⚠️ **Seat correction to this body, applied by the `domain:spec` seat 2 PM (`session_01JbZnqu8bt6YqfJsr9vaFb3`), ⛔ not by the implementer** — whose standing clauses write a PR body once and hand later corrections to the seat. One edit: the `dispatch-gates --ran` bullet above now carries the patch round's **re-measurement** instead of the first round's reading. ⭐ The earlier number was not wrong about what it measured — the two families genuinely refused their own prerequisite — but the seat had recorded the claim that they later cleared as **unresolved until re-run**, and this round re-ran them. It is now a seat-verified reading rather than a dev assertion inherited across a worktree that no longer exists. ⛔ Nothing else in this body is rewritten, and the diff is untouched by this edit. --- _Generated by [Claude Code](https://claude.ai/code)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 7e40d1d commit 24d622b

28 files changed

Lines changed: 912 additions & 30 deletions
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
---
2+
'@objectstack/spec': minor
3+
'@objectstack/cli': minor
4+
---
5+
6+
feat(spec, cli): an application contributes its own first-run credentials to the development boot banner — `devHint` / `devLogins[]` (#17556)
7+
8+
Clause-②: yes (widening)
9+
10+
## What an operator sees
11+
12+
`os dev` seeds a platform admin on an empty DB, and the banner prints it as the only
13+
credential a first-run operator is handed. #17081 made that line honest about what the
14+
account *cannot* see; it could not name an account that *can*, because the platform does
15+
not know an application's audiences. Measured on a downstream app, of five personas the
16+
four it seeds each rendered their navigation group and the one the banner printed
17+
rendered none — and the operator read the empty shell as a broken product.
18+
19+
Two new top-level keys on the stack definition close that. Declaring either adds a block
20+
BENEATH the seeded-admin lines, on a development boot only:
21+
22+
```
23+
🔑 Dev admin: admin@objectos.ai / admin123
24+
seeded on empty DB · dev only — do not use in production
25+
platform admin — Setup, Studio and every record, but NO app-declared capability, so
26+
an app that gates navigation on requiredPermissions may show it an empty menu; grant
27+
it a permission set under Setup → Users, or sign in as an account your app seeds
28+
29+
👥 App logins: 2 declared by this app
30+
Hiring admin — admin@quillstone.example / demo1234
31+
Job seeker — candidate01@mail.example / demo1234
32+
declared in this app's `devLogins` · dev only — the platform seeded none of them
33+
34+
💡 App hint: run `pnpm seed:demo` first, then sign in as the Hiring admin
35+
```
36+
37+
## What is writable that was not
38+
39+
The top-level stack door has been strict since #8687, so before this both spellings were
40+
an `unrecognized_keys` refusal. The accept set gains exactly:
41+
42+
- **`devHint?: string`** — one sentence printed under the credential block. Composes as
43+
`'single'`: two stacks declaring different hints is a composition error naming the key,
44+
never a silent last-wins.
45+
- **`devLogins?: DevLogin[]`**, where `DevLogin` is `{ email: string; password?: string;
46+
label?: string }`, closed against unknown keys from birth. Composes as `'concat'`, so
47+
composing two applications keeps both publishers' personas. An artifact ENVELOPE key
48+
like `plugins` / `devPlugins`: it stays at the top level and is refused inside
49+
`packages[].manifest`, because the banner's only reader looks at the top level.
50+
51+
`DevLoginSchema` / `DevLogin` / `DevLoginParsed` are exported from
52+
`@objectstack/spec/system`. Nothing is renamed, nothing is retired, and no value that
53+
parsed before is refused now.
54+
55+
## Three properties worth knowing before you author one
56+
57+
- **Declaring is not seeding.** An entry CREATES NOTHING: it names an account the
58+
application seeds by other means (`data` fixtures, `onEnable`, its own script) so the
59+
banner can point at one that shows something. An entry naming an unseeded account
60+
prints a credential that will not work, exactly as a README line would — which is why
61+
the banner says the application declared it.
62+
- **Additive, never a replacement.** The seeded-admin block still prints, unchanged and
63+
first. An application-controlled key able to suppress a platform disclosure would let
64+
an app hide a live credential the operator was just handed.
65+
- **Development only, and scrubbed.** The block renders only under `os dev`,
66+
`objectstack serve --dev` or `NODE_ENV=development`; any other boot is byte-identical
67+
to one declaring nothing. The values are author-controlled text reaching a terminal, so
68+
every C0/C1 control byte is replaced with U+FFFD before printing — an escape sequence
69+
in a hint cannot erase the rows above it or repaint a forged `🔑 Dev admin` row. ⚠️
70+
Whatever is written here is committed to the application's repository and printed to a
71+
terminal: it is a development fixture, never a real secret.

‎content/docs/getting-started/quick-reference.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ Plugin architecture, manifests, and kernel runtime.
8585
| **[Metadata Loader](/docs/references/kernel/metadata-loader)** | `metadata-loader.zod.ts` | MetadataLoaderContract | Metadata loading |
8686
| **[Package Registry](/docs/references/kernel/package-registry)** | `package-registry.zod.ts` | InstalledPackage, InstallPackageRequest | Package resolution |
8787

88-
## System Protocol (16 of 33 schemas)
88+
## System Protocol (16 of 34 schemas)
8989

9090
Runtime environment, logging, jobs, caching, and observability.
9191

‎content/docs/references/index.mdx‎

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
title: Protocol Reference
3-
description: Every schema published by @objectstack/spec — 1530 schemas across 14 protocol modules
3+
description: Every schema published by @objectstack/spec — 1531 schemas across 14 protocol modules
44
---
55

66
{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}
@@ -31,9 +31,9 @@ counts are sums of the rows they head. Regenerate with
3131
| [Security Protocol](/docs/references/security) | 5 | 30 | Permission sets, row-level security, sharing rules, tenancy posture. |
3232
| [Shared Protocol](/docs/references/shared) | 10 | 31 | Primitives used across every protocol — identifiers, HTTP, expressions, error maps, enums. |
3333
| [Studio Protocol](/docs/references/studio) | 3 | 35 | Studio designer metadata — the authoring surfaces for the protocols above. |
34-
| [System Protocol](/docs/references/system) | 33 | 272 | The runtime environment — logging, jobs, cache, metrics, notifications, i18n and compliance. |
34+
| [System Protocol](/docs/references/system) | 34 | 273 | The runtime environment — logging, jobs, cache, metrics, notifications, i18n and compliance. |
3535
| [UI Protocol](/docs/references/ui) | 16 | 156 | Apps, pages, views, dashboards, reports, actions and themes — the ObjectUI layer. |
36-
| **Total** | **194** | **1530** | 14 protocol modules |
36+
| **Total** | **195** | **1531** | 14 protocol modules |
3737

3838
---
3939

@@ -316,7 +316,7 @@ Studio designer metadata — the authoring surfaces for the protocols above.
316316

317317
## System Protocol
318318

319-
**Source:** `packages/spec/src/system/` · **Import:** `@objectstack/spec/system` · **33 pages, 272 schemas**
319+
**Source:** `packages/spec/src/system/` · **Import:** `@objectstack/spec/system` · **34 pages, 273 schemas**
320320

321321
The runtime environment — logging, jobs, cache, metrics, notifications, i18n and compliance.
322322

@@ -329,6 +329,7 @@ The runtime environment — logging, jobs, cache, metrics, notifications, i18n a
329329
| [`collaboration.zod.ts`](/docs/references/system/collaboration) | `AwarenessEvent`, `AwarenessSession`, `AwarenessUpdate`, `AwarenessUserState`, `CRDTMergeResult`, `CRDTState`, `CRDTType`, `CollaborationMode`, `CollaborationSession`, `CollaborationSessionConfig`, `CollaborativeCursor`, `CounterOperation`, `CursorColorPreset`, `CursorSelection`, `CursorStyle`, `CursorUpdate`, `GCounter`, `LWWRegister`, `ORSet`, `ORSetElement`, `OTComponent`, `OTOperation`, `OTOperationType`, `OTTransformResult`, `PNCounter`, `TextCRDTOperation`, `TextCRDTState`, `UserActivityStatus`, `VectorClock` |
330330
| [`core-services.zod.ts`](/docs/references/system/core-services) | `CoreServiceName`, `KernelServiceMap`, `KernelServiceStatus`, `ServiceConfig`, `ServiceCriticality` |
331331
| [`deploy-bundle.zod.ts`](/docs/references/system/deploy-bundle) | `DeployBundle`, `DeployDiff`, `DeployManifest`, `DeployStatusEnum`, `DeployValidationIssue`, `DeployValidationResult`, `MigrationPlan`, `MigrationStatement`, `SchemaChange` |
332+
| [`dev-login.zod.ts`](/docs/references/system/dev-login) | `DevLogin` |
332333
| [`disaster-recovery.zod.ts`](/docs/references/system/disaster-recovery) | `BackupConfig`, `BackupRetention`, `BackupStrategy`, `DisasterRecoveryPlan`, `FailoverConfig`, `FailoverMode`, `RPO`, `RTO` |
333334
| [`doc.zod.ts`](/docs/references/system/doc) | `Doc` |
334335
| [`email-config.zod.ts`](/docs/references/system/email-config) | `EmailAddressConfig`, `EmailProvider`, `EmailServiceConfig` |
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
title: Dev Login
3+
description: Dev Login protocol schemas
4+
---
5+
6+
{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}
7+
8+
<Callout type="info">
9+
**Source:** `packages/spec/src/system/dev-login.zod.ts`
10+
</Callout>
11+
12+
## TypeScript Usage
13+
14+
```typescript
15+
import { DevLoginSchema } from '@objectstack/spec/system';
16+
import type { DevLogin } from '@objectstack/spec/system';
17+
18+
// Validate data
19+
const result = DevLoginSchema.parse(data);
20+
```
21+
22+
---
23+
24+
## DevLogin
25+
26+
### Properties
27+
28+
| Property | Type | Required | Description |
29+
| :--- | :--- | :--- | :--- |
30+
| **email** | `string` | ✅ | Email of an account this application seeds — printed by the development boot banner |
31+
| **password** | `string` | optional | Development-fixture password printed beside the address; omit when the account signs in another way |
32+
| **label** | `string` | optional | The audience this account belongs to (e.g. `Hiring admin`) — printed before the address |
33+
34+
35+
---
36+

‎content/docs/references/system/index.mdx‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ This section contains all protocol schemas for the system layer of ObjectStack.
1515
<Card href="/docs/references/system/collaboration" title="Collaboration" description="Source: packages/spec/src/system/collaboration.zod.ts" />
1616
<Card href="/docs/references/system/core-services" title="Core Services" description="Source: packages/spec/src/system/core-services.zod.ts" />
1717
<Card href="/docs/references/system/deploy-bundle" title="Deploy Bundle" description="Source: packages/spec/src/system/deploy-bundle.zod.ts" />
18+
<Card href="/docs/references/system/dev-login" title="Dev Login" description="Source: packages/spec/src/system/dev-login.zod.ts" />
1819
<Card href="/docs/references/system/disaster-recovery" title="Disaster Recovery" description="Source: packages/spec/src/system/disaster-recovery.zod.ts" />
1920
<Card href="/docs/references/system/doc" title="Doc" description="Source: packages/spec/src/system/doc.zod.ts" />
2021
<Card href="/docs/references/system/email-config" title="Email Config" description="Source: packages/spec/src/system/email-config.zod.ts" />

‎content/docs/references/system/meta.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
"collaboration",
4040
"doc",
4141
"---More---",
42+
"dev-login",
4243
"stack-server"
4344
]
4445
}

‎docs/audits/2026-07-unknown-key-strictness-ledger.counts.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,4 +264,4 @@ directory rather than per file.
264264
| `marketplace/` | 29 |
265265
| `qa/` | 6 |
266266
| `shared/` | 20 |
267-
| `system/` | 350 |
267+
| `system/` | 351 |

‎packages/cli/src/commands/serve.ts‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5222,6 +5222,14 @@ export default class Serve extends Command {
52225222
// owns that refusal now, and this row just reports what it decided.
52235223
tenancyPosture,
52245224
seededAdmin,
5225+
// #17556 — read straight off the definition this process booted, the
5226+
// same object `config.devPlugins` is read from twenty lines up. The
5227+
// platform describes the account it seeded; only the APPLICATION knows
5228+
// which of its audiences shows something, and these two keys are how it
5229+
// says so. `printServerReady` gates them on `isDev` and scrubs them —
5230+
// they are author-controlled text reaching a terminal.
5231+
devLogins: Array.isArray((config as any)?.devLogins) ? (config as any).devLogins : undefined,
5232+
devHint: typeof (config as any)?.devHint === 'string' ? (config as any).devHint : undefined,
52255233
automation: automationSummary,
52265234
seeds: seedSummary,
52275235
// #17329 — read HERE, inside the banner thunk, so it is the tally as of

0 commit comments

Comments
 (0)