letmesplain

Generation (free, bring-your-own model)

Splain shows your users interactive on-screen walkthroughs of your app; a guide is one such walkthrough. This page is about drafting one with a model.

splain:generate drafts a guide for a resource by having a model read your app's code and produce guide JSON — then it runs that draft through the same validator splain:check uses, mechanically flags every anchor it couldn't verify (an anchor is how a guide step points at a specific element on your page, usually a data-splain marker you place in your code), and lands the result as an unpublished draft. It is a drafting aid for a developer, never an autopilot: nothing it produces reaches your users until a human reviews it, signs off, and publishes.

Bring your own model

Splain ships no model, no API key, and no default endpoint. The one thing generation sends anywhere is your source (at dev time, never runtime data), and you own that crossing. You opt in one of two ways.

The quick way: the reference adapter (OpenAI-compatible, your endpoint)

Set all three and Splain binds its reference client to your endpoint — OpenAI, Azure, OpenRouter, or a fully-local Ollama / LM Studio / vLLM:

SPLAIN_GENERATION_ENDPOINT=https://api.openai.com/v1   # or http://localhost:11434/v1
SPLAIN_GENERATION_KEY=sk-…                             # local hosts: any non-empty string
SPLAIN_GENERATION_MODEL=gpt-4o                         # or llama3.1:70b, etc.

Leave any of them unset (the default) and nothing is bound — splain:generate refuses. The configuration is the consent: your source facts go only to the endpoint you chose (the confirmation prompt names the host and model, never the key), and a fully local endpoint means they never leave your machine at all. The reference prompt asks for the same discipline the pipeline enforces: anchor only to real markers — any non-marker or unverifiable selector is mechanically flagged by the AnchorFlagger for human review before publish — and describe what the user actually does, never a process the interface merely allows.

Two operational notes, said plainly:

The full-control way: bind your own adapter

// A service provider in your app
use Splain\Generation\Contracts\Generator;

public function register(): void
{
    $this->app->bind(Generator::class, YourGenerator::class);
}

Generator is one method:

use Splain\Generation\Contracts\Generator;

class YourGenerator implements Generator
{
    public function draft(array $context, array $feedback = []): array
    {
        // $context = the introspected surface facts (routes, anchors, model, label).
        // $feedback = validator errors from the previous attempt (empty on the first).
        // Call whatever you want — your OpenAI/Anthropic/Azure/Bedrock/local endpoint,
        // with YOUR key — and return a guide array (slug, title, genre, spans, steps).
        // You do NOT need to make it valid; the loop validates and asks you to fix.
        return $yourModel->draft($context, $feedback);
    }
}

Until a Generator is bound, splain:generate refuses to run and tells you so.

What Splain does around your model

You supply the drafting; Splain supplies the conscience, so a weak or careless model still can't ship something dangerous:

  1. Validator-in-the-loop. Each draft is run through ValidateGuide; its errors are fed back to your draft() verbatim, up to splain.generation.max_attempts times, until the guide has zero structural errors. Your model can be dumb — the loop keeps it honest. (It repairs errors only; it never feeds a needs_review flag back as something to "fix", so a model can't learn to delete a flag to pass.)

  2. Mechanical anchor-flagging. The only anchors Splain can verify are the data-splain markers — attributes you add to your HTML (data-splain="…") so guides can reliably point at an element even after a refactor — that splain:introspect actually found in your source. Every other anchor a model emits — a fabricated data-splain value, a bare .fi-* class, a proposed injection — gets a needs_review flag saying "confirm this on the live screen." A model cannot launder a guessed selector as a confident one.

  3. Lands as an unpublished draft. The result is created as an unpublished guide carrying its needs_review flags. Generation itself is free, and so is the review path: open the guide's JSON, resolve each flag by hand (confirm the anchor on the live screen, fix or delete the guessed selector), re-run php artisan splain:check --strict until it passes clean, then flip the guide's status to published. Drafts never reach real users (serve_drafts ships false), so nothing you draft goes live until you publish it.

    The visual side of this — the Studio hub (Splain's guide-management screen), resolving flags in a review inbox, and the governed named-human attested publish sign-off (a recorded sign-off where a named human takes responsibility that the guide reflects how the work is really done) — lives in splain/pro (a separate, proprietary package). Generation does not require it; the by-hand path above is the free equivalent.

Usage

php artisan splain:suggest              # find a resource with no guide
php artisan splain:generate documents   # draft one (asks for confirmation first)
php artisan splain:generate documents --yes   # non-interactive (CI); explicit consent

Honest scope


Rendered from splain@a738cf8. The documentation is rendered from the package repository — the same files that ship with Splain — so the site can't drift from the code.