Klingenberg Bygg's plan and words; a plan carries a slideshow; the words model is told what a follower's desk and an inside sent page are
Test / test (pull_request) Successful in 1m17s
Test / test (pull_request) Successful in 1m17s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GT37Z1Xtfd9pUuQtMTg6Yt
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
827a75be3d
commit
a70690a736
+30
-6
@@ -62,6 +62,12 @@ pub struct Plan {
|
||||
/// is what the page-load limit asks for.
|
||||
#[serde(default)]
|
||||
pub doors: Vec<Door>,
|
||||
/// Pictures across the front door, under the wordmark and above
|
||||
/// the question: portal's `slideshow` block, verbatim (`max_height`,
|
||||
/// `slides` with `image`, `caption`, `width`, `height`). What the
|
||||
/// business has made, shown before anything is asked.
|
||||
#[serde(default)]
|
||||
pub slideshow: Option<Value>,
|
||||
/// Texts the site carries word for word - bylaws, terms - each as
|
||||
/// a page of its own where every section of the text is one
|
||||
/// alternative: its heading the card's, its paragraphs the card's
|
||||
@@ -2068,10 +2074,11 @@ pub fn render(plan: &Plan, words: &BTreeMap<String, Value>) -> anyhow::Result<BT
|
||||
plan.front_door_count()
|
||||
);
|
||||
}
|
||||
files.insert(
|
||||
"questions/index.yaml".into(),
|
||||
json!({ "name": text(&front["question"]), "description": text(&front["description"]), "responsible": responsible, "alternatives": alternatives }),
|
||||
);
|
||||
let mut front_page = json!({ "name": text(&front["question"]), "description": text(&front["description"]), "responsible": responsible, "alternatives": alternatives });
|
||||
if let Some(slideshow) = &plan.slideshow {
|
||||
front_page["slideshow"] = slideshow.clone();
|
||||
}
|
||||
files.insert("questions/index.yaml".into(), front_page);
|
||||
|
||||
// A door's page: a nav page (index.yaml), public, holding the
|
||||
// forms of the needs behind it.
|
||||
@@ -2402,6 +2409,8 @@ pub fn render(plan: &Plan, words: &BTreeMap<String, Value>) -> anyhow::Result<BT
|
||||
files.into_iter().map(|(path, value)| Ok((path, serde_yaml::to_string(&value)?))).collect()
|
||||
}
|
||||
|
||||
const DESK_PURPOSE: &str = "a desk: the page one role inside the business works from. Under `buckets`, each entry is a list of cases; `moves` are the buttons that move a case from one stage to the next (key `from->to`), so label each with what the person has just done. Under `links` are pages this role can open. Under `invites`, each entry is a form that invites a person into that group - they get an account and a one-time link - and `invited` is the list of invites sent, where `again` is the button that sends an invite whose link ran out once more. `suggestions`, on the desk that has it, is the list of changes the people responsible for a page have suggested: `looking`, `changed` and `declined` are its buttons.";
|
||||
|
||||
fn words_prompt(plan: &Plan, path: &str, fallback: &Value, tone: Option<&str>) -> String {
|
||||
let voice = match tone {
|
||||
Some(tone) => format!("\n\nTone of voice for every word on this page: {tone}. The site is told in more than one tone, each written in a pass of its own, so keep to this one throughout and do not hedge between tones."),
|
||||
@@ -2417,6 +2426,21 @@ fn words_prompt(plan: &Plan, path: &str, fallback: &Value, tone: Option<&str>) -
|
||||
} else {
|
||||
format!("a public page anyone can open, read-only, listing ONLY the cases that have reached: {shown_only}. Nothing else is shown - do not promise the reader anything waiting or turned down. `question` is what this page asks; `heading` names the list.")
|
||||
};
|
||||
// A desk is not always a place of work: a group that only follows
|
||||
// its own cases reads a list of what it sent in, and the words
|
||||
// should not hand it a job it does not have.
|
||||
let desk_group = path.strip_prefix("questions/").and_then(|p| p.split('/').next()).unwrap_or_default();
|
||||
let own: Vec<&str> = plan.needs.iter().filter(|n| follows_own(n, desk_group)).map(|n| n.lands_in.as_str()).collect();
|
||||
let mut desk_purpose = DESK_PURPOSE.to_string();
|
||||
if !own.is_empty() {
|
||||
desk_purpose.push_str(&format!(" On this page, the list(s) {} show the signed-in person ONLY the cases they sent in themselves: they are following their own case, not working through other people's, so head those lists as theirs ('Your cases') and say nothing of a queue.", own.iter().map(|b| format!("`{b}`")).collect::<Vec<_>>().join(", ")));
|
||||
}
|
||||
// A form a role enters from inside is sent by a colleague at work,
|
||||
// not by a visitor waiting on an answer.
|
||||
let sent_purpose = match plan.needs.iter().find(|n| n.enters_as.as_deref().is_some_and(|g| path == format!("questions/{g}/{}-sent.yaml", n.id))) {
|
||||
Some(n) => format!("a page shown right after someone in the group `{}` has sent a form from inside the business. The reader is that colleague, at work, not a visitor: nobody comes back to them. Say what has now happened to what they entered, and who it reaches.", n.enters_as.as_deref().unwrap_or_default()),
|
||||
None => "a page shown right after someone has sent a form. Say who reads it, when they answer, and what happens next.".to_string(),
|
||||
};
|
||||
let purpose = if path == "site.yaml" {
|
||||
"the invite mail: the first mail a person gets when a member of the business brings them in to help (onto a desk, into a committee), carrying the one-time link that lets them set a password. `subject` and `body` under `invite`. They did not ask for it, so say who brought them in and to what, then the link and when it runs out, then where they sign in afterwards. Plain text, short lines. Keep every `{...}` placeholder exactly as it is: `{name}`, `{username}`, `{group}`, `{link}`, `{expires}`, `{site}`, `{site_name}` are filled in by the site, and `{link}` must stay on a line of its own."
|
||||
} else if path == "aggregates.yaml" {
|
||||
@@ -2430,11 +2454,11 @@ fn words_prompt(plan: &Plan, path: &str, fallback: &Value, tone: Option<&str>) -
|
||||
} else if plan.doors.iter().any(|d| door_path(d) == path) {
|
||||
"a page one kind of visitor reaches from the front door. `question` is what this page asks them; under `needs`, each entry is one alternative they can pick, with the form behind it - or, for a need that is members' only, the card that leads to the members' page, where the form is behind sign-in: say so in its description."
|
||||
} else if path.ends_with("/index.yaml") {
|
||||
"a desk: the page one role inside the business works from. Under `buckets`, each entry is a list of cases; `moves` are the buttons that move a case from one stage to the next (key `from->to`), so label each with what the person has just done. Under `links` are pages this role can open. Under `invites`, each entry is a form that invites a person into that group - they get an account and a one-time link - and `invited` is the list of invites sent, where `again` is the button that sends an invite whose link ran out once more. `suggestions`, on the desk that has it, is the list of changes the people responsible for a page have suggested: `looking`, `changed` and `declined` are its buttons."
|
||||
desk_purpose.as_str()
|
||||
} else if path.ends_with("/[key].yaml") {
|
||||
"a page one record of a public list opens to. This is NOT where a notice is posted: the visitor has read somebody else's record on the list (a notice, an application) and is answering it. `question` asks the visitor what they have to say to it; under `forms`, each entry is one kind of answer (an objection, an appeal, an offer to lend), with the form behind it. An answer that mails the record's owner goes straight to them; nobody at a desk passes it on, so never say so."
|
||||
} else if path.contains("/received/") || path.ends_with("-sent.yaml") {
|
||||
"a page shown right after someone has sent a form. Say who reads it, when they answer, and what happens next."
|
||||
sent_purpose.as_str()
|
||||
} else {
|
||||
"a page only one role inside the business can open, holding one form."
|
||||
};
|
||||
|
||||
@@ -184,6 +184,9 @@ rest. Each is one line in the plan, and the builder does the wiring:
|
||||
- **Carry a text word for word.** `documents` puts bylaws or terms on
|
||||
a page of their own, one card per section, from a markdown file
|
||||
beside the plan. Nothing rewords them.
|
||||
- **Show the work.** `slideshow` on the plan is portal's own block,
|
||||
carried onto the front door as written: a strip of pictures under
|
||||
the question, each with its caption.
|
||||
- **Keep the people in portal.** `people: memberships` keeps every
|
||||
group in portal itself: a desk invite is a place held until the
|
||||
person signs in, a person is their phone number first, and any
|
||||
|
||||
Reference in New Issue
Block a user