build: slots instead of placeholders, and the first worded run

Fallback words read like finished copy, and nemotron handed the front
door straight back. The prompt now shows every string as a slot saying
what belongs there; a slot echoed back counts as no answer and keeps
the fallback.

runs/contractor-build-2026-09-21: nemotron-3.5-lightning:30b-a3b, the
model that produced no loadable site free-form, words a complete
contractor site in 130 s. Verifier green by construction; held-out
personas route 8/8 with descriptions and 8/8 on headings alone
(qwen3:8b as the visitor). "What happens next" is still 0/2 on the
front door: no heading or description says who reads a submission.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L4jrCgLiKKHAEFuZUJjckH
This commit is contained in:
Bendik Aagaard Lynghaug
2026-09-21 21:08:08 +02:00
co-authored by Claude Fable 5.1
parent 8c286a8483
commit aef0fe1396
24 changed files with 784 additions and 3 deletions
+32
View File
@@ -8,6 +8,38 @@ series for every role, the state machines behind them, and a desk per
handling role. No software is written for the business; the only thing
produced is content, and every line of it is held to a verifier.
## Build: structure from the plan, words from the model
```
portal-trainer build --plan plans/contractor.yaml --out out/nordvik \
--lint question_lint --llm "MODEL=nemotron-3.5-lightning:30b-a3b engines/ollama.sh"
```
A `plan.yaml` is a needs spec that also says what each form collects.
Everything structural is derived from it with no model involved: the
state machines (read off `stages`: walk the line, be turned down at
any point, succeed only from the end, pass over `skippable` stages),
the front door, a followup page per need, a gated directory per group
with its desk and the pages only that group may open, and every button
the machine allows. The verifier accepts the result by construction.
The model writes words only, one page at a time, into a JSON object
whose shape is fixed. Its strings are merged over built-in fallbacks
key by key, so a bad reply costs wording, never a working site.
| Same contractor, same local model (nemotron 30B-A3B on ergo) | Free-form `generate` | `build` |
|---|---|---|
| A site the verifier accepts | never, in 6 rounds and 16 min | first try, 130 s |
| Held-out personas, with descriptions | - | 8 of 8 |
| Held-out personas, headings only | - | 8 of 8 |
For comparison, a Sonnet-class model writing everything free-form
scored 8 of 10 on headings only and silently dropped a stage the owner
had described. `build` cannot drop a stage: it never decides one.
`generate` (below) remains for turning a plain-words intake into a
first plan; what it should hand over is the plan, not the pages.
## How it works
```