"""Admission dispatch for wave wave-firstrun: B-FIRST-RUN — the first-run experience (owner directive 2026-10-17, docs/factory/ spawn-queue.md "NEW PRIORITY 0" + "First-run law" + "Fresh-state detection"). classify + activate + admit (the headless scaffolding entry law, tests/test_construction_admission.py pattern). Research exemption: owner-directive, declared in docs/factory/research-registry.json slug firstrun (approval_ref 'user:wave-firstrun dispatch') — the behavior contract is pinned at directive level; this is a bounded build, no new research questions. Artifacts this wave admits: the tui/firstrun package — fresh-state detection (fresh = no provider selection record + no conversation checkpoint; a checkpoint without a record is gated-but-returning, never welcomed as first run), the selection record law (provider_selection. json in the state home, RUNE_HOME -> ~/.rune, atomic write, defensive read — the same law the live gate ships), the frozen extensive provider catalog (cloud ten + local four, no default), the first-run face (welcome -> provider -> model -> connection test -> done stage machine; listing and probe travel through the lib.ModelLister seam so the owning shell runs them off its UI thread), and the Shell (renders the first-run face while gated, the working face once configured; chat gate enforced on the record — gated sends refused with the honest line, nothing auto-selects or auto-persists). Red-first frozen battery tui/firstrun/firstrun_test.go (detection, transition + connection-test honesty, no-default enforcement, catalog law, face walk, record path) — proven RED against the stub, GREEN on the implementation. Boundary note: tui/cmd/rune-tui/main.go is owned by the concurrent chat-live wave and was NOT touched; the shell adoption seam (render firstrun.Shell while unconfigured) is documented in the package law comment for the file-owning wave. """ import json import os import sys sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src")) from rune import construction_activation as cact # noqa: E402 from rune import construction_admission as cadm # noqa: E402 from rune import construction_mode as cmode # noqa: E402 from rune import scaffold as sc # noqa: E402 ROOT = os.path.join(os.path.dirname(__file__), "..") OBJECTIVE = ( "build B-FIRST-RUN — implement the first-run experience (owner " "directive, docs/factory/spawn-queue.md): a brand-new user opens " "rune and sees the honest welcome (NOT an error, NOT empty-looking " "lanes), picks a provider from the extensive cloud+local catalog " "and a model for it, passes the connection test, and lands in the " "working face. (1) Implement fresh-state detection, wired to the " "live record law: fresh = no provider selection record + no " "conversation checkpoint (provider_selection.json in the state " "home). (2) Create the tui/firstrun package: the welcome surface, " "the provider selector, the model selector for the picked " "provider, the connection test (source answers AND carries the " "picked model — never a silent pass), then the transition to the " "working face. (3) Implement the Shell that renders the first-run " "face when fresh/gated and the normal face when configured, with " "no-default-provider enforcement: chat gated until an explicit " "selection completes (the record is the authority — never a " "cursor, a guess, or a silent default). Red-first frozen battery " "in tui/firstrun/firstrun_test.go" ) AFFECTED = [ "tui/firstrun/firstrun.go", "tui/firstrun/face.go", "tui/firstrun/render.go", "tui/firstrun/shell.go", "tui/firstrun/firstrun_test.go", "docs/factory/research-registry.json", "scripts/admit_wave_firstrun.py", ] def main() -> int: store = os.path.join(ROOT, "store", "scaffold") scaffold = sc.Scaffold(store, "rune") md = cmode.classify_mode( OBJECTIVE, target_project="rune", project_exists=True, affected_surface=AFFECTED, ) print("classified mode:", md.mode) need = { "need_id": "need-wave-firstrun", "objective": OBJECTIVE, "origin": "worker", "project": "rune", "task_ref": "wave-firstrun", "affected_surface": AFFECTED, "evidence_refs": [ "docs/factory/spawn-queue.md", "tui/firstrun/firstrun_test.go", "tui/cmd/rune-tui/provider.go", ], "exemption": ( "owner-directive: B-FIRST-RUN first-run experience per the " "spawn-queue directives (see docs/factory/research-registry." "json slug firstrun, approval_ref 'user:wave-firstrun " "dispatch')"), } act = cact.activate(md, need=need, scaffold=scaffold, builder="wave-firstrun") print("activated:", act.get("activated"), act.get("mode")) env = cadm.admit( act, objective=OBJECTIVE, builder="wave-firstrun", affected_surface=AFFECTED, evidence_refs=need["evidence_refs"], origin="worker", task_ref="wave-firstrun", expected_new_entities=[ "tui/firstrun (the first-run package: detection, record " "law, catalog, face, shell)", "tui/firstrun/firstrun_test.go (frozen battery, red-first)", "unified tui module member: go test ./firstrun/ green", ], expected_runtime_effects=[ "a brand-new user (no provider_selection.json, no " "checkpoint) renders the first-run welcome face — never an " "error, never empty-looking lanes", "the provider catalog shows the cloud ten + local four " "with nothing selected; only enter-picks stage a choice " "and only a passed connection test persists the record", "chat is gated until the record exists: gated sends are " "refused with 'no provider configured — select one to " "begin' and route nowhere; nothing auto-selects", "a configured user (record present) renders the working " "face immediately — no welcome, no selector", ], verification_obligations=[ "cd tui && go test ./firstrun/ -count=1 green (the frozen " "battery)", "cd tui && go vet ./firstrun/ clean", "cd tui && go build ./... green (the module still builds; " "cmd/rune-tui untouched)", ], approval_ref="user:wave-firstrun dispatch", contract_id="ADMIT-wave-firstrun", project_store=store, project_id="rune", ) print(json.dumps(env, indent=2, default=str)[:2400]) return 0 if env.get("success") else 1 if __name__ == "__main__": sys.exit(main())