Skip to content

Onboarding is a conversation with the agent - #547

Open
alex-clickhouse wants to merge 10 commits into
alex/fb-plan-linksfrom
alex/fb-onboarding-chat
Open

alex-clickhouse wants to merge 10 commits into
alex/fb-plan-linksfrom
alex/fb-onboarding-chat

Conversation

@alex-clickhouse

Copy link
Copy Markdown
Collaborator

Stacked on #542. After setup in the browser, the agent is set up by talking to it, in both
modes. A worker set up without the browser with a task description still sets itself up.

What changes:

  • One onboarding conversation. Finish opens one web chat session, "Onboarding", for the
    person who finished setup. The agent speaks first: a hidden first message (not in the
    transcript) tells it to say that it is set up by talking to it, offer what it can help
    with (personal installs lead with the personal assistant), and ask which items apply.
    For engineering work it asks which repositories to onboard and studies them. It proposes
    cron jobs with gates, the plan-and-approve loop, statuses (the built-in backlog and
    plan_review), tags, skills and, for a worker, the TASK.md sections. It changes nothing
    before the person confirms, then applies, summarizes and calls the new onboarding_done
    tool.
  • Without the browser. nerve init --terminal or --non-interactive with a task
    description records an autonomous onboarding. At the next start with a model, one session
    without a person reads the description and the worker guide, fills TASK.md, creates
    skills, cron jobs and tasks, notifies the operator, and calls onboarding_done. It runs
    once. With an empty description, the worker waits for the conversation in the web UI.
    The terminal prompt is optional and says which path follows.
  • Onboarding state. Migration v060 adds one row: requested, running, done or skipped,
    and the kind (conversation or autonomous). Finish is idempotent (also two clicks at once)
    and always opens the conversation. With a pending restart, the next daemon start opens
    it. A worker without the old TASK.md marker stays done. The "ended without a plan"
    state and "Try again" are removed.
  • Mode step. Personal "works for you", worker "works for a team"; an optional note
    instead of the required task description and the starter buttons.
  • Do not miss it. The Ready screen leads into the conversation. Until onboarding is done
    or skipped, a banner on every page except the conversation says the agent waits, with
    Continue (it can be hidden until the next page). When setup is complete and nobody pressed
    Finish (also an install from before), the banner offers Start, which runs Finish and opens
    the conversation. While a worker sets itself up, the banner only links to that session.
    The setup page offers Start and Skip onboarding (with a confirmation). Every signed-in
    person sees the banner, because the conversation sets up the shared agent.
  • Effective job state. The Automation step shows each built-in job as the scheduler
    runs it: system.yaml with jobs.yaml jobs of the same id replacing it (the same
    merge). Such a job is read-only there, and saving the step leaves it alone.

Verification: backend tests for the changed code, npm run build, Vitest for the changed
specs, migration number check; CI runs the full suites. Screenshots of the Mode step, Ready
screen, banner, setup page and the first agent message (a real run), in light and dark.

Known gaps: the hidden instructions can be shortened by a context compaction. A failed
first turn keeps its session with the error, and onboarding goes back to pending so the
next Finish or start opens a new one. An autonomous session cut by a restart stays open;
the banner links to it.

🤖 Generated with Claude Code

alex-clickhouse and others added 9 commits October 21, 2026 18:24
The worker's onboarding was an autonomous cron-style session that
researched a one-task description and configured the worker on its
own, and a personal install had no onboarding at all. Users read the
Mode step as "give it one task now".

Finish now opens one web chat session titled Onboarding, for both
modes, for the person who finished setup. The engine runs its first
turn with an internal message, so the agent speaks first and the
person sees only its answer. The prompt (`onboarding_prompt`) offers
the list of what the agent can help with (personal installs lead with
the personal assistant), asks for the repositories to onboard for
engineering work, proposes cron jobs, the plan-and-approve loop,
statuses (the built-in `backlog` and `plan_review`), tags and skills,
and applies nothing before the person confirms. A worker also gets its
`TASK.md` rules and the worker guide. The agent ends onboarding with the
new `onboarding_done` tool, which only that session can call.

Migration v060 adds the `onboarding` row: requested, running, done or
skipped. `nerve.onboarding` derives pending, running, done or skipped
from it, opens the conversation once (a compare-and-set, also for
concurrent Finish clicks), and keeps installs from before correct: a
worker without the TASK.md marker is done, and a worker that still has
it gets the conversation when the daemon starts. When a restart is
pending, Finish records a request and the next start opens it. A person
can skip onboarding (`POST /api/setup/onboarding/skip`).

The Mode step describes personal as working for you and worker as
working for a team, with an optional note instead of the required task
description and the starter buttons. The Ready screen leads into the
conversation. Until onboarding is done or skipped, the setup banner
shows a reminder with Continue on every page except the conversation
(its close button hides it until the next page), and the setup page
shows the conversation and Skip onboarding behind a confirmation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Automation step read only `config/cron/system.yaml`. A job that the
onboarding conversation turned on through a `jobs.yaml` job with the
same id still showed as off, so a person saw "task planner not
selected" while it ran.

`merge_jobs` is the scheduler's merge (a user job replaces the system
job with the same id), now a function that the scheduler and the setup
checklist share. `list_optional_crons` reports the effective state and
whether `jobs.yaml` replaces the job (`overridden`). The Automation step
shows such a job read-only with "Set in jobs.yaml", and saving the step
leaves its `system.yaml` entry alone, because a change there would not
change what runs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The test asserted that the stop attribution columns are the last two
entries of ACTOR_COLUMNS. The onboarding migration appends its own
column, as the list's docstring asks, so the test now checks that both
columns are in the list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The daemon opens a requested conversation when it starts, through a
task that `AgentEngine.initialize` schedules. The service function had
tests; the call in `initialize` had none. The new test starts a real
engine and checks that it asks `nerve.onboarding.start_on_boot` once,
for its workspace.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`AgentEngine.initialize` opened a requested conversation, so the
one-shot engines of `nerve cron` and `nerve sync` could open it and then
stop in the middle of the first turn. It also opened it without a model
credential. In both cases the row stayed `running` with a session whose
first turn had failed, and nothing tried again.

The gateway lifespan now calls `start_on_boot`, which does nothing
without a model credential, as Finish. A first turn that ends as failed
(`watch_turns`) puts the row back to `requested`, so the next Finish or
daemon start opens a new conversation.

`start` runs one start at a time in the process and creates the session
before it releases the lock, so a second start sees the first one's
session instead of claiming the row again. At start-up without a row,
the session's cause is the system actor, as for other sessions that
Nerve starts for itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`list_optional_crons` had its own copy of the rule that a `jobs.yaml`
job replaces the built-in job, and it loaded `jobs.yaml` without
building gates. The scheduler drops a job whose gate does not build and
runs the built-in job instead, so the Automation step could show a state
that did not run. It now loads both files as the scheduler does and uses
`merge_jobs`; the Automation step's save uses the same list of jobs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Finish answers with the checklist read after it opened the conversation,
so the Ready screen's separate `onboarding` answer only mattered once
onboarding had ended, and then it showed an old "Start the
conversation". The screen now reads the checklist only.

When "Skip onboarding" asks for confirmation, "Keep it" takes the focus
that the removed button had. Test comments use the terms of the
onboarding conversation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A worker set up by `nerve init --terminal` or `--non-interactive` with a
task description sets itself up without a person again. `nerve init`
records an autonomous onboarding in the onboarding row (new `kind`
column), and the gateway runs it once at the next start with a model
credential. The session gets the worker onboarding prompt (research,
TASK.md, skills, cron jobs, tasks, a walkthrough by notify) as a cron
session, and ends with `onboarding_done`, which removes the TASK.md
marker. A failed first turn asks the next start again. The prompt keeps
the planner rules: built-in `backlog` and `plan_review`, and no notify of
its own after `plan_propose`.

Finish in the browser opens the conversation for either kind. A worker
without a task description, and a worker from before the onboarding row,
wait for the conversation in the web UI: the start opens only a requested
row. The terminal wizard takes an optional description and says which
path follows. `onboarding_done` checks the stored row, so a session that
removed the marker itself can still end onboarding. The setup API reports
the kind as `mode.detail.onboarding_kind`. The README and the setup,
worker, cron and API docs describe both paths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The banner shows while the onboarding conversation is due and setup is
complete, also when nobody pressed Finish: a personal install whose steps
are all done, or an install from before onboarding. Its Start button runs
Finish, which is idempotent, and opens the conversation. The setup page
offers the same Start beside Skip onboarding, so such an install starts
the conversation or skips it once.

While a worker sets itself up without a person, the banner, the setup
page, the Mode step and the Ready screen say so and only link to that
session.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…opener

`nerve init` records the autonomous onboarding through
`open_production_db`, as every other CLI path does, so only the shared
opener constructs a `Database`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant