rate: the desks are scored on their buttons
The inside of the business reads button labels and nothing else. For every desk and bucket with two or more buttons, a worker is told what just happened to a case, in the state's own words, and picks the button; scored right of total with what each miss was taken for. The vel's neighbour-help desk scores 2 of 4 and the notice board 1 of 2: the first numbers a desk has ever had. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
5e313b7d40
commit
9089503524
@@ -324,6 +324,17 @@ sentences and would pull every page down to "very easy". The
|
||||
`rate.json` in `runs/contractor-build-2026-09-21/` predates this and
|
||||
its `asked` and `next` columns are the old, pinned ones.
|
||||
|
||||
`rate --repo` also scores the **desks**, the inside of the business,
|
||||
which reads button labels and nothing else: for every desk and bucket
|
||||
with two or more buttons, a worker is told what has just happened to
|
||||
a case (in the state's own words, never the label's) and picks the
|
||||
button among that desk's, in a rotated order. Scored per desk as
|
||||
right of total, with what each miss was mistaken for. Measured on
|
||||
2026-09-24: the vel's neighbour-help desk 2 of 4 ("not solved" taken
|
||||
for "connect a neighbour"), the notice board 1 of 2, the planned
|
||||
contractor's subcontractor desk 4 of 7 (checking, approved and
|
||||
rejected all crossed). A desk that scores full marks has labels a
|
||||
stranger to the business could press right.
|
||||
## Measured so far
|
||||
|
||||
**Generated: a building contractor** (`runs/contractor-2026-09-21/`),
|
||||
|
||||
+77
-2
@@ -156,8 +156,57 @@ fn followup_page(repo: Option<&Path>, step: &Value) -> Option<Value> {
|
||||
load_page(repo, action)
|
||||
}
|
||||
|
||||
/// `/` is `questions/index.yaml`; `/a/b` is `questions/a/b.yaml` or
|
||||
/// `questions/a/b/index.yaml`.
|
||||
/// Every desk's buttons per bucket, from the simulation model:
|
||||
/// (page, group, bucket, [(from, to, label)]). A desk with one button
|
||||
/// for a bucket has nothing to confuse and is left out.
|
||||
fn desk_buttons(model: &Value) -> Vec<(String, String, String, Vec<(String, String, String)>)> {
|
||||
let mut out = Vec::new();
|
||||
for (bucket, spec) in model["buckets"].as_object().into_iter().flatten() {
|
||||
for desk in spec["desks"].as_array().into_iter().flatten() {
|
||||
let moves: Vec<(String, String, String)> = desk["moves"]
|
||||
.as_array()
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.map(|m| (s(&m["from"]), s(&m["to"]), s(&m["label"])))
|
||||
.filter(|(_, _, l)| !l.is_empty())
|
||||
.collect();
|
||||
if moves.len() >= 2 {
|
||||
out.push((s(&desk["page"]), s(&desk["group"]), bucket.clone(), moves));
|
||||
}
|
||||
}
|
||||
}
|
||||
out.sort();
|
||||
out
|
||||
}
|
||||
|
||||
fn s(v: &Value) -> String {
|
||||
v.as_str().unwrap_or_default().to_string()
|
||||
}
|
||||
|
||||
/// A worker on the desk, told what just happened to a case, picks
|
||||
/// the button for it. The outcome is put in plain words from the
|
||||
/// state's own name, never from the label, and the buttons are listed
|
||||
/// in a seeded shuffle.
|
||||
fn button_pick(cmd: &str, group: &str, bucket: &str, from: &str, to: &str, labels: &[&str], seed: usize) -> anyhow::Result<String> {
|
||||
let plain = |id: &str| id.replace(['_', '-'], " ");
|
||||
let mut order: Vec<&str> = labels.to_vec();
|
||||
// A fixed rotation per move: the right answer is not always first.
|
||||
let by = seed % order.len().max(1);
|
||||
order.rotate_left(by);
|
||||
let listed: Vec<String> = order.iter().enumerate().map(|(i, l)| format!("{}. {l}", i + 1)).collect();
|
||||
let prompt = format!(
|
||||
"You work on the {} desk of a small organisation. A case in \"{}\" was \"{}\" and has just become \"{}\". The desk shows these buttons for it:\n\n{}\n\nWhich button records what just happened? Reply as JSON: {{\"choice\": <the button's number>}}",
|
||||
plain(group),
|
||||
plain(bucket),
|
||||
plain(from),
|
||||
plain(to),
|
||||
listed.join("\n")
|
||||
);
|
||||
let reply = ask(cmd, &prompt, &seed.to_string())?;
|
||||
let n = reply["choice"].as_u64().unwrap_or(0) as usize;
|
||||
Ok(order.get(n.wrapping_sub(1)).map(|l| l.to_string()).unwrap_or_default())
|
||||
}
|
||||
|
||||
/// Every `mail:` on a state in the repo's aggregates.yaml, as
|
||||
/// (`bucket:state`, subject, body) with the `{placeholders}` taken
|
||||
/// out, so they read as the person will read them.
|
||||
@@ -193,6 +242,8 @@ pub(crate) fn mail_texts(repo: &Path) -> Option<Vec<(String, String, String)>> {
|
||||
Some(out)
|
||||
}
|
||||
|
||||
/// `/` is `questions/index.yaml`; `/a/b` is `questions/a/b.yaml` or
|
||||
/// `questions/a/b/index.yaml`.
|
||||
pub(crate) fn load_page(repo: &Path, route: &str) -> Option<Value> {
|
||||
let rel = route.trim_matches('/');
|
||||
let candidates = if rel.is_empty() {
|
||||
@@ -248,6 +299,30 @@ pub fn rate(argv: &[String]) -> anyhow::Result<()> {
|
||||
}
|
||||
|
||||
let mut report = BTreeMap::new();
|
||||
// The desks: the inside of the business reads button labels and
|
||||
// nothing else, so each desk is scored on whether a worker told
|
||||
// what has just happened to a case picks the right button among
|
||||
// that desk's buttons for the bucket.
|
||||
let desks = desk_buttons(&model);
|
||||
if !desks.is_empty() {
|
||||
println!("{:<20} {:<24} {:>8} {}", "desk", "bucket", "buttons", "mistaken for");
|
||||
for (page, group, bucket, moves) in &desks {
|
||||
let labels: Vec<&str> = moves.iter().map(|(_, _, l)| l.as_str()).collect();
|
||||
let mut right = 0;
|
||||
let mut mistaken = Vec::new();
|
||||
for (i, (from, to, label)) in moves.iter().enumerate() {
|
||||
let picked = button_pick(&cmd, group, bucket, from, to, &labels, i)?;
|
||||
if picked == *label {
|
||||
right += 1;
|
||||
} else {
|
||||
mistaken.push(format!("{to} as {picked:?}"));
|
||||
}
|
||||
}
|
||||
println!("{:<20} {:<24} {:>3} of {:<2} {}", page, bucket, right, moves.len(), mistaken.join("; "));
|
||||
report.insert(format!("desk {page} {bucket}"), json!({ "right": right, "of": moves.len(), "mistaken": mistaken }));
|
||||
}
|
||||
println!();
|
||||
}
|
||||
// The mails first, when the repo carries any: a person reads a
|
||||
// mail alone, with no card around it, so its prose is rated on
|
||||
// its own - LIX and length.
|
||||
|
||||
Reference in New Issue
Block a user