Class: Insika::Onboarding
- Inherits:
-
Object
- Object
- Insika::Onboarding
- Defined in:
- lib/insika/onboarding.rb
Overview
LLM-first onboarding surface (item 20 / §5.6). The "Flue trick": the insika
serves, from itself, a start.md addressed to the DEVELOPER'S OWN coding agent
("Read /models.json and the public docs mirrored as raw markdown. It is rails new
reimplemented as a prompt, with the generator being the coding agent the developer
already has.
Pure and data-defined: everything it serves comes from injected sources — a
start.md TEMPLATE file, a NAME=>path map of public docs, and (optionally) the
SettingsStore / LLMProviderStore / the served agents. It only READS (no writes, no
RubyLLM, no Executor), so the transport can call it under the constitutional rule.
Secrets never leak: models_json reads the MASKED provider view and omits base
urls/keys entirely — only slugs and model ids, which is all a coding agent needs to
write a correct model/provider line.
Injection (all optional but template_path/docs):
template_path -> the start.md source (a real file, so it stays reviewable/raw)
docs -> { "slug" => "/abs/path.md", … } served at GET /docs/:slug.md.
An explicit ALLOWLIST — the gitignored internal docs are never
in it, and there is no filesystem traversal (only map keys).
settings_store -> platform default_model / fallbacks / thinking (nil = omit)
provider_store -> configured providers + their model ids, MASKED (nil = omit)
agents -> callable returning [{ id:, model:, provider:, description: }]
for the agents already served here (nil = none)
Constant Summary collapse
- MODELS_SCHEMA_VERSION =
Bumped when the models.json SHAPE changes (its own contract, independent of the settings schema). A consuming coding agent can branch on it.
1- PUBLIC_DOCS =
The PUBLIC docs allowlist, repo-relative: slug => path. Explicit on purpose — the gitignored internal docs (FOLLOWUP / techspec / TRANSLATION-TRACKER / …) are NEVER here, so /docs can only ever serve OSS material. Kept in sync with the tracked
.mdprose (README + docs/*.md). { "readme" => "README.md", "why" => "docs/WHY.md", "agents" => "docs/AGENTS.md", "tools" => "docs/TOOLS.md", "skills" => "docs/SKILLS.md", "context" => "docs/CONTEXT.md", "workflows" => "docs/WORKFLOWS.md", "channels" => "docs/CHANNELS.md", "plugins" => "docs/PLUGINS.md", "security" => "docs/SECURITY.md", "architecture" => "docs/ARCHITECTURE.md", "running-local" => "docs/RUNNING-LOCAL.md", "deploy" => "docs/DEPLOY.md", "embedding" => "docs/EMBEDDING.md", "sandbox" => "docs/SANDBOX.md", "benchmark" => "docs/BENCHMARK.md", "observability" => "docs/OBSERVABILITY.md", "evals" => "docs/EVALS.md", "refinement" => "docs/REFINEMENT.md", "loadtest" => "docs/LOADTEST.md", "releasing" => "docs/RELEASING.md" }.freeze
- TEMPLATE =
Repo-relative path to the start.md template.
"docs/onboarding/start.md"
Class Method Summary collapse
-
.standard(root:, settings_store: nil, provider_store: nil, agents: nil) ⇒ Object
Builds the standard onboarding surface rooted at
root(the repo/gem root), wiring the PUBLIC_DOCS allowlist + start.md template.
Instance Method Summary collapse
-
#doc(slug) ⇒ Object
Raw markdown for one public doc, by slug.
-
#docs_index(base_url:) ⇒ Object
The public docs index (name + title + fetchable raw-markdown url).
-
#initialize(template_path:, docs: {}, settings_store: nil, provider_store: nil, agents: nil) ⇒ Onboarding
constructor
A new instance of Onboarding.
-
#models_json(base_url:) ⇒ Object
Machine-readable model catalog.
-
#start_md(base_url:) ⇒ Object
The onboarding skill (raw markdown), with the live base url interpolated so the coding agent knows where to fetch the models list and docs.
Constructor Details
#initialize(template_path:, docs: {}, settings_store: nil, provider_store: nil, agents: nil) ⇒ Onboarding
Returns a new instance of Onboarding.
78 79 80 81 82 83 84 |
# File 'lib/insika/onboarding.rb', line 78 def initialize(template_path:, docs: {}, settings_store: nil, provider_store: nil, agents: nil) @template_path = template_path @docs = docs || {} @settings_store = settings_store @provider_store = provider_store @agents = agents # callable -> [Hash] | nil end |
Class Method Details
.standard(root:, settings_store: nil, provider_store: nil, agents: nil) ⇒ Object
Builds the standard onboarding surface rooted at root (the repo/gem root),
wiring the PUBLIC_DOCS allowlist + start.md template. The three composition roots
(minimal wiring, DSL serve, deployment) pass their own stores/agents on top. A
doc whose file is absent (a slimmed-down gem) is simply dropped — never a boot
failure.
69 70 71 72 73 74 75 76 |
# File 'lib/insika/onboarding.rb', line 69 def self.standard(root:, settings_store: nil, provider_store: nil, agents: nil) docs = PUBLIC_DOCS.each_with_object({}) do |(slug, rel), acc| path = File.join(root, rel) acc[slug] = path if File.file?(path) end new(template_path: File.join(root, TEMPLATE), docs: docs, settings_store: settings_store, provider_store: provider_store, agents: agents) end |
Instance Method Details
#doc(slug) ⇒ Object
Raw markdown for one public doc, by slug. nil = unknown slug (the transport 404s).
No path traversal is possible: slug must be a KEY of the injected allowlist.
The Jekyll frontmatter the docs site needs (title/parent/nav_order — the same files ARE the site's pages) is STRIPPED here: a coding agent asked for the prose, not for sidebar metadata, and this keeps the response byte-identical to what it was before the site existed.
131 132 133 |
# File 'lib/insika/onboarding.rb', line 131 def doc(slug) read_doc(slug)&.sub(FRONTMATTER, "") end |
#docs_index(base_url:) ⇒ Object
The public docs index (name + title + fetchable raw-markdown url). Drives discovery: a coding agent lists this, then GETs the ones it needs.
117 118 119 120 121 122 |
# File 'lib/insika/onboarding.rb', line 117 def docs_index(base_url:) base = normalize_base(base_url) @docs.keys.sort.map do |slug| { name: slug, title: doc_title(slug), url: "#{base}/docs/#{slug}.md" } end end |
#models_json(base_url:) ⇒ Object
Machine-readable model catalog. Everything a coding agent needs to write a valid
model/provider line, and nothing secret. Sources that are absent (nil store)
simply drop their key — a fresh DSL serve still returns a coherent document
(served agents + thinking levels + whatever default is set).
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/insika/onboarding.rb', line 99 def models_json(base_url:) base = normalize_base(base_url) settings = @settings_store&.get || {} { schema_version: MODELS_SCHEMA_VERSION, base_url: base, responses_url: "#{base}/v1/responses", default: default_model(settings), fallbacks: fallback_models(settings), utility_model: presence(settings["utility_model"]), thinking_levels: ModelSelection::THINKING_LEVELS, providers: providers, agents: served_agents }.compact end |
#start_md(base_url:) ⇒ Object
The onboarding skill (raw markdown), with the live base url interpolated so the coding agent knows where to fetch the models list and docs. Read on each request (the file is small and this surface is low-traffic) — editing start.md needs no restart.
90 91 92 93 |
# File 'lib/insika/onboarding.rb', line 90 def start_md(base_url:) base = normalize_base(base_url) substitute(File.read(@template_path), base) end |