Skip to main content

Ethos onboarding

The walkthrough of how a new user — or an existing user without an Ethos at their active scope — moves from "the no-Ethos CTA appeared" to "I'm in a Builder pre-seeded with a canonical ethos". Aimed at integrators and operators who need to reason about the contract end-to-end. The reference shapes live in the API reference; this page is the prose that ties them together.

Why this page exists

An Ethos is the persistent, scope-bound character of a Gnostikon agent — what it sounds like, what it is willing to commit to, what is in-bounds. Most working sessions consume an Ethos that already exists at the project, user, or agent scope. Onboarding is the path for the case where no such Ethos exists yet at the actor's accessible scopes.

The flow is special-cased because the Gnostikon backend gates most session work behind a scope readiness check, and a freshly registered actor's scope is by definition not-ready. Without a small, well-scoped bypass, the user would have no way to do the very thing that makes their scope ready: build their first Ethos.

The four moves are:

  1. The client reads availability and surfaces a CTA.
  2. The CTA opens a no-Ethos session via PUT /api/session-scope with a short-lived intent literal.
  3. The user picks from a curated list of canonical seeds.
  4. The client launches the Builder pre-seeded with the chosen seed; on save, the Ethos persists at the user-selected scope and the CTA disappears.

The sections below walk each move. Behavior inside the Builder (auto-Generate, propose bypass, save mechanics) is documented separately in Auto-Generate bypass.

The signal — availability

The client decides whether to show the onboarding CTA from GET /api/auth/me/ethos/availability (find it under Auth · me · ethos in the API reference sidebar). The request carries the scope the client wants to interrogate:

  • scope_kind (required, enum project / user / agent)
  • scope_id (optional, only meaningful for project)

A normal 200 response uses EthosAvailabilityResponseModel:

  • scope_kind, scope_id — echo of the request.
  • has_ethos: booleantrue when an Ethos is materialized at the requested scope.
  • status_code — when has_ethos=false, optionally one of project_scope_not_ready / user_scope_not_ready / agent_scope_not_ready, naming why the scope is not-ready. When has_ethos=true, this is null.

The endpoint also returns a uniform 404 (EthosAvailabilityErrorResponseModel, fields error + code) for the "unknown scope" and "actor-not-a-member" cases — by design, the two cases are indistinguishable in the response, so a client cannot use 404 vs 200 to probe scope existence.

The client renders the CTA when (a) the response is 200 and has_ethos=false and status_code ends in _scope_not_ready. Any other combination keeps the CTA hidden.

Activating the no-Ethos scope

The CTA's first action is PUT /api/session-scope with the body's optional intent field set to the literal string "onboarding". The endpoint schema is in the API reference under Session-scope.

Without intent, the request is rejected at the readiness gate with 409 project_scope_not_ready (or the user / agent variant) — the actor's scope is by definition not-ready until they have built an Ethos. With intent="onboarding", the endpoint applies an explicit access check instead: if the actor can materialize an Ethos for the target scope, the scope switch proceeds and the session is now bound to that scope for the remainder of onboarding.

The intent field is a closed enum. Only "onboarding" is accepted; any other value (including a misspelling like "onboard") is rejected at the Pydantic boundary with 400 validation_error. Absence or null is equivalent and preserves the legacy 200 / 409 path byte-for-byte — no existing caller is affected.

Two things intent deliberately does not do:

  • It does not weaken the access check. An actor who cannot materialize an Ethos for the project still gets 400 validation_error — same shape as an unknown project_id, by design (no information leak about project existence).
  • It is not persisted anywhere. Every request that needs the bypass must carry it. A stale intent on a later request to a now-ready scope is a no-op.

The intent literal is part of the broader intents vocabulary — see the Intents section below for the canonical reference.

Browsing the seed list

Once the no-Ethos scope is active, the client fetches the curated seed list from GET /api/ethos/seeds (find it under Ethos · seeds in the API reference sidebar).

The response is EthosSeedListResponseModel:

  • generated_at — ISO timestamp of when the curated list was assembled.
  • items: EthosSeedItemResponseModel[] — each item has canonical_gnosis_id, display_name, summary, seed_body.

The list is curated, not filtered: every authenticated caller sees the same items in the same order. There are no narrowing parameters. The curation reflects the team's editorial choice of canonical starting ethoses; the list is expected to grow slowly and deliberately, not algorithmically.

The client renders a picker (one row per item, with display_name and summary). On selection, the client passes the chosen item's seed_body through to the Builder as the seed text.

Screen reference: the surface that renders this picker — its visible elements, UI states, and dispatched events — is documented under Client surface → Ethos onboarding seeds.

Handoff to the Builder

Selecting a seed launches the Builder pre-seeded with the chosen seed_body. From the user's perspective, the Builder opens with the seed text already in place — they can edit, iterate, and ultimately save to persist their first Ethos.

The contract for what happens inside the Builder — the auto-Generate behavior on a seeded Builder, the intent="onboarding" bypass that the gnosis endpoints honor during this flow, the save mechanics that flip the CTA off — is documented in Auto-Generate bypass. That page is the canonical home for Builder-internal behavior.

On save, the Builder persists the Ethos at the user-selected scope. The next call to GET /api/auth/me/ethos/availability for that scope returns has_ethos=true, and the client stops rendering the onboarding CTA.

Lifecycle in brief

Three short answers for contributors who need the lifecycle picture without leaving this page:

What triggers has_ethos=false? No Ethos is persisted at the actor's accessible project / user / agent scopes. The optional status_code field on the availability response (*_scope_not_ready) names which scope-kind tier is the cause. A scope can be not-ready for reasons other than missing Ethos, but in the onboarding context the *_scope_not_ready codes are the load-bearing signal — see The signal — availability.

What scope is active during seed selection? The no-Ethos session-scope activated by PUT /api/session-scope with intent="onboarding". The scope is bound to the actor's chosen project (or user / agent), but it cannot do work that requires a materialized Ethos — except the Ethos build itself, which is the whole point of the bypass.

What writes flip the CTA off? The Builder's save persists the Ethos at the selected scope. The next availability call for that scope returns has_ethos=true, status_code=null. The CTA condition (has_ethos=false AND status_code ends in _scope_not_ready) is no longer met. A standalone Ethos lifecycle architecture page is deferred until a second back-link demand surfaces; the three answers above are intentionally co-located with the flow that uses them.

Intents

An intent is a request-scoped marker carried in the body of an otherwise unchanged endpoint. It opts the caller into a specific gate or bypass for the duration of one request. Intents are not persisted, not logged into the user's session, and not echoed in any response — they exist only as input.

Today, exactly one intent literal is defined:

LiteralAccepting endpointsEffect
"onboarding"PUT /api/session-scope (this guide) · POST /api/memory/short-term · POST /api/gnosis/propose · POST /api/gnosis/analyze (see Auto-Generate bypass for the three Builder/STM endpoints).Activates the no-Ethos scope handshake during onboarding (bypasses the *_scope_not_ready gate on session-scope, and the equivalent project_scope_not_ready gate on the three Builder/STM endpoints during the seeded-Builder flow). Any other literal value returns 400 validation_error. Absent or null preserves legacy behavior byte-for-byte.