build: --words repeats and layers; the desk's confirm slot says what it is
Two things the uhhm rounds ran into. Only the first --words was read, so a round of hand changes had to be merged into the previous words by hand; now each file layers over the last. And the desk's one confirm slot was described like any button, so a model wrote "Send invite" under the cases list; it now says it serves every list. README: the lift is positional, and where that bit. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
9e7f16c489
commit
9ff9924374
@@ -45,9 +45,13 @@ default.
|
|||||||
|
|
||||||
`--words-from <repo>` lifts the words a repo already has back into the
|
`--words-from <repo>` lifts the words a repo already has back into the
|
||||||
words map by structure, so a rebuild keeps hand-edited wording and
|
words map by structure, so a rebuild keeps hand-edited wording and
|
||||||
adds only the new slots; `--words <json>` overrides slots by page;
|
adds only the new slots. The lift is by position, so it holds for a
|
||||||
|
rebuild of the same shape and not across a restructure: on uhhm.no it
|
||||||
|
put the old front door's three headings on three new cards. `--words
|
||||||
|
<json>` overrides slots by page and may repeat, each file layered over
|
||||||
|
the last (the words a build used, then a round of hand changes);
|
||||||
`words.json` in the output is the words as used. See
|
`words.json` in the output is the words as used. See
|
||||||
`runs/tomtervel-invites-2026-09-23`.
|
`runs/tomtervel-invites-2026-09-23` and `runs/uhhm-2026-09-23`.
|
||||||
|
|
||||||
| Same contractor, same local model (nemotron 30B-A3B on ergo) | Free-form `generate` | `build` |
|
| Same contractor, same local model (nemotron 30B-A3B on ergo) | Free-form `generate` | `build` |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
|
|||||||
+13
-4
@@ -833,7 +833,11 @@ fn slots_in(value: &Value, key: &str, parent: &str) -> Value {
|
|||||||
(_, "question") => "the one question this page asks its reader".to_string(),
|
(_, "question") => "the one question this page asks its reader".to_string(),
|
||||||
(_, "heading" | "next_heading") => "a heading, at most eight words, saying what the reader gets".to_string(),
|
(_, "heading" | "next_heading") => "a heading, at most eight words, saying what the reader gets".to_string(),
|
||||||
(_, "description" | "page_description" | "next_description") => "one or two short sentences".to_string(),
|
(_, "description" | "page_description" | "next_description") => "one or two short sentences".to_string(),
|
||||||
(_, "button" | "back" | "confirm") => "a button, one to three words, saying what pressing it does".to_string(),
|
(_, "button" | "back") => "a button, one to three words, saying what pressing it does".to_string(),
|
||||||
|
// One slot serves every list on the desk, so it cannot
|
||||||
|
// name any one of them: a model once wrote "Send invite"
|
||||||
|
// under the cases list.
|
||||||
|
(_, "confirm") => "the one button under every list on this desk, confirming a move the person has chosen; one word, such as Confirm".to_string(),
|
||||||
(_, "encouragement") => "one reassuring sentence: what happens with what they send, or how little it takes; or empty".to_string(),
|
(_, "encouragement") => "one reassuring sentence: what happens with what they send, or how little it takes; or empty".to_string(),
|
||||||
(_, "icon") => "an icon name from the list at the end, the one that fits this best; or empty".to_string(),
|
(_, "icon") => "an icon name from the list at the end, the one that fits this best; or empty".to_string(),
|
||||||
(_, "for_whom") => "a few words naming who this is for, addressed to them; or empty".to_string(),
|
(_, "for_whom") => "a few words naming who this is for, addressed to them; or empty".to_string(),
|
||||||
@@ -1296,7 +1300,7 @@ fn words_prompt(plan: &Plan, path: &str, fallback: &Value) -> String {
|
|||||||
|
|
||||||
pub fn run(argv: &[String]) -> anyhow::Result<()> {
|
pub fn run(argv: &[String]) -> anyhow::Result<()> {
|
||||||
let value = |flag: &str| argv.iter().position(|a| a == flag).and_then(|i| argv.get(i + 1)).cloned();
|
let value = |flag: &str| argv.iter().position(|a| a == flag).and_then(|i| argv.get(i + 1)).cloned();
|
||||||
let usage = "usage: portal-trainer build --plan <plan.yaml> --out <dir> [--words-from <existing repo>] [--words <overrides.json>] [--llm 'cmd'] [--lint question_lint]";
|
let usage = "usage: portal-trainer build --plan <plan.yaml> --out <dir> [--words-from <existing repo>] [--words <words.json>]... [--llm 'cmd'] [--lint question_lint]";
|
||||||
let plan_path = value("--plan").ok_or_else(|| anyhow!(usage))?;
|
let plan_path = value("--plan").ok_or_else(|| anyhow!(usage))?;
|
||||||
let out = std::path::PathBuf::from(value("--out").ok_or_else(|| anyhow!(usage))?);
|
let out = std::path::PathBuf::from(value("--out").ok_or_else(|| anyhow!(usage))?);
|
||||||
let plan: Plan = serde_yaml::from_str(&std::fs::read_to_string(&plan_path).with_context(|| format!("reading {plan_path}"))?)
|
let plan: Plan = serde_yaml::from_str(&std::fs::read_to_string(&plan_path).with_context(|| format!("reading {plan_path}"))?)
|
||||||
@@ -1313,8 +1317,13 @@ pub fn run(argv: &[String]) -> anyhow::Result<()> {
|
|||||||
Some(repo) => lift_words(&plan, Path::new(&repo))?,
|
Some(repo) => lift_words(&plan, Path::new(&repo))?,
|
||||||
None => fallback_words(&plan),
|
None => fallback_words(&plan),
|
||||||
};
|
};
|
||||||
if let Some(path) = value("--words") {
|
// `--words` may repeat: the words a previous build used, then a
|
||||||
let given: BTreeMap<String, Value> = serde_json::from_str(&std::fs::read_to_string(&path).with_context(|| format!("reading {path}"))?)
|
// round of hand overrides on top, in the order given. Each is
|
||||||
|
// merged key by key over what is there, so a file names only the
|
||||||
|
// slots it changes.
|
||||||
|
let words_files: Vec<&String> = argv.iter().enumerate().filter(|(_, a)| *a == "--words").filter_map(|(i, _)| argv.get(i + 1)).collect();
|
||||||
|
for path in words_files {
|
||||||
|
let given: BTreeMap<String, Value> = serde_json::from_str(&std::fs::read_to_string(path).with_context(|| format!("reading {path}"))?)
|
||||||
.with_context(|| format!("parsing {path}"))?;
|
.with_context(|| format!("parsing {path}"))?;
|
||||||
for (page, v) in given {
|
for (page, v) in given {
|
||||||
match words.get(&page) {
|
match words.get(&page) {
|
||||||
|
|||||||
Reference in New Issue
Block a user