From 6e4969421172317762fa27f81bf70bf36caf9f4e Mon Sep 17 00:00:00 2001 From: Bendik Aagaard Lynghaug Date: Thu, 24 Sep 2026 06:01:56 +0200 Subject: [PATCH] build: turned_down_from, and uhhm's stages in words A need may say which unfinished stages a case can still be turned down from; the machine no longer offers the bad end from every stage. uhhm's collaborators are declined before they start and depart after, as the live site has it. The uhhm plan gets stages_text and three desk buttons reworded: every uhhm desk scores full marks. Co-Authored-By: Claude Fable 5.1 --- README.md | 5 + plans/uhhm.yaml | 9 + runs/uhhm-2026-09-23/README.md | 10 + runs/uhhm-2026-09-23/site/aggregates.yaml | 1 - .../site/llm/01-aggregates.yaml.md | 132 --- runs/uhhm-2026-09-23/site/model.json | 947 ++++++++++++++++++ runs/uhhm-2026-09-23/site/needs.yaml | 31 + .../site/questions/owners/index.yaml | 7 +- runs/uhhm-2026-09-23/site/words.json | 5 +- runs/uhhm-2026-09-23/words-r8.json | 500 +++++++++ src/build.rs | 27 +- src/plan.rs | 4 + 12 files changed, 1535 insertions(+), 143 deletions(-) delete mode 100644 runs/uhhm-2026-09-23/site/llm/01-aggregates.yaml.md create mode 100644 runs/uhhm-2026-09-23/site/model.json create mode 100644 runs/uhhm-2026-09-23/words-r8.json diff --git a/README.md b/README.md index 4d345bb..cc430f9 100644 --- a/README.md +++ b/README.md @@ -173,6 +173,11 @@ the business's own words and held-out personas. line, the desk buttons' fallback labels, and written into needs.yaml as `stage_words` so the desk score describes an outcome in the site's own words. With them, the vel's desks score 26 of 26. +- `turned_down_from: [, ...]` on a need: the unfinished stages + a case can still be turned down from; omitted, every one. An + applicant is declined before they start and departs after, which + is what uhhm.no's live machine says and what two near-synonymous + desk buttons had been standing in for. - `attended_by:` on a need: who moves cases that no desk button moves, an automation listening on the bucket's events, written into aggregates.yaml as portal's own annotation for the reader. diff --git a/plans/uhhm.yaml b/plans/uhhm.yaml index 5d8eb4d..3e224de 100644 --- a/plans/uhhm.yaml +++ b/plans/uhhm.yaml @@ -77,6 +77,7 @@ needs: done_when: [accepted, declined] success: [accepted] events: { open: submitted } + stages_text: { open: "received", accepted: "taken on", declined: "not a fit for us" } # The client plays too: they can withdraw the brief themselves. moves_by: { declined: [submitter, owners] } mails: { open: submitter, accepted: submitter, declined: submitter } @@ -98,6 +99,7 @@ needs: stages: [open, approved, previewed, declined] done_when: [previewed, declined] success: [previewed] + stages_text: { open: "received", approved: "approved for a draft", previewed: "a preview is ready", declined: "not a fit for us" } mails: { open: submitter, approved: submitter, previewed: submitter, declined: submitter } attended_by: > approved -> previewed: the n8n workflow that builds the draft @@ -123,6 +125,9 @@ needs: done_when: [departed, declined] success: [departed] events: { open: applied, active: onboarded } + # Declined before starting; after that a person departs. + turned_down_from: [open, invited] + stages_text: { open: "received", invited: "invited in", active: "working with us", departed: "moved on", declined: "not a fit for us" } max_fields: 3 collects: - { name: name } @@ -138,6 +143,7 @@ needs: stages: [open, unsubscribed] done_when: [unsubscribed] events: { open: subscribed } + stages_text: { open: "on the list", unsubscribed: "off the list" } moves_by: { unsubscribed: [submitter, owners] } max_fields: 2 collects: @@ -154,6 +160,7 @@ needs: done_when: [approved, rejected] success: [approved] events: { open: proposed } + stages_text: { open: "received", approved: "approved", rejected: "turned down" } max_fields: 3 collects: - { name: submitted_by } @@ -173,6 +180,7 @@ needs: stages: [open, in_dialogue, resolved, declined] done_when: [resolved, declined] success: [resolved] + stages_text: { open: "received", in_dialogue: "in dialogue with a custodian", resolved: "resolved", declined: "closed without action" } # The person affected says when it is resolved, not only the custodian. moves_by: { resolved: [submitter, custodians] } mails: { open: submitter, resolved: submitter, declined: submitter } @@ -193,6 +201,7 @@ needs: stages: [prospect, client, past_client] done_when: [past_client] skippable: [client] + stages_text: { prospect: "a prospect", client: "a client", past_client: "a past client" } events: { prospect: identified, client: became_client, past_client: churned } max_fields: 2 collects: diff --git a/runs/uhhm-2026-09-23/README.md b/runs/uhhm-2026-09-23/README.md index 4335237..2d003ae 100644 --- a/runs/uhhm-2026-09-23/README.md +++ b/runs/uhhm-2026-09-23/README.md @@ -121,3 +121,13 @@ Easier on the whole, and specific where the fallback was formulaic: "Your brief is in front of us. We read the whole thing before we reply, so it may take a few days." `words-r7.json` is the words as used; the branch carries them. + +## Round 8: stage words, and turned down only where the plan says + +`stages_text` on every need, three owners' desk buttons reworded by +hand ("Archive as past client", "Part ways", "Withdraw the invite"), +and `turned_down_from: [open, invited]` on the collaborators, so a +person is declined before they start and departs after - the live +site's machine exactly. Desks: 18 of 18, from 15 of 18 read against +state ids. `words-r8.json` is the words as used; the branch carries +them. diff --git a/runs/uhhm-2026-09-23/site/aggregates.yaml b/runs/uhhm-2026-09-23/site/aggregates.yaml index fbde157..25d90c2 100644 --- a/runs/uhhm-2026-09-23/site/aggregates.yaml +++ b/runs/uhhm-2026-09-23/site/aggregates.yaml @@ -15,7 +15,6 @@ aggregates: transitions: active: - departed - - declined invited: - active - declined diff --git a/runs/uhhm-2026-09-23/site/llm/01-aggregates.yaml.md b/runs/uhhm-2026-09-23/site/llm/01-aggregates.yaml.md deleted file mode 100644 index 0cf9c65..0000000 --- a/runs/uhhm-2026-09-23/site/llm/01-aggregates.yaml.md +++ /dev/null @@ -1,132 +0,0 @@ -You write the words for one page of a website for this business. - -Business: UHHM -About: UHHM builds software around a clear argument: infrastructure, tools and products for organizations that can say what problem they have. It also builds and carries the redoal network - the gesture math, the relay, the first node, and the portal this site runs on - and answers for what that network keeps. -Contact: Bendik Aagaard Lynghaug -Language to write in: en - -Everyone the site serves: -- commission: an organization with a problem it wants software to solve - wants: a considered reply to a brief, from people who read the whole thing. Handled by owners. Stages: open -> accepted -> declined. -- trial: someone who runs a business where requests, approvals and handovers get lost, and wants to see it as a site before committing to anything - wants: a first draft of a site for their own roles, built from their own words and approved by a person first. Handled by owners. Stages: open -> approved -> previewed -> declined. -- collaborate: someone who wants to work with us on what we build - wants: to be read as a person, and invited in if there is a fit. Handled by owners. Stages: open -> invited -> active -> departed -> declined. -- subscribe: someone who wants to hear from us when there is something to say - wants: a note now and then, and an easy way out. Handled by owners. Stages: open -> unsubscribed. -- propose: a visitor who sees how a page on this site should change - wants: the change read by a person and, if approved, made. Handled by owners. Stages: open -> approved -> rejected. -- custody: someone who runs, builds on, or is affected by the redoal network and needs the people who keep its first node - wants: an answer from a custodian, and action where the network keeps something it should not. Handled by custodians. Stages: open -> in_dialogue -> resolved -> declined. -- prospect: an owner who has met an organization worth following up - wants: it written down and walked through its states from the desk. Handled by owners. Stages: prospect -> client -> past_client. - -The page: aggregates.yaml -It is the mails the site sends. Under each bucket, each stage that sends one has a `subject` and a `body`: the mail a person gets the moment their case enters that stage, saying what happened and what, if anything, they can do now. Plain text, short, in the business's voice. Keep every `{...}` placeholder exactly as it is - `{site}`, `{chain}`, `{email}` and any field name are filled in by the site - and keep any line with a link in it. - -Below is the page as JSON. Every value is a slot that says what to write there. Return the same JSON with every slot replaced by your words. Keep every key exactly as it is. A slot left as it is counts as no answer. - -Rules for the words: -- `question` is a question the reader could ask themselves. -- A heading says what the reader gets, in the reader's own words, and must work without its description. -- Lead with what a thing enables. No negations in headings. No trade jargon the reader would not use. -- Descriptions are one or two short sentences. Labels and buttons are a few words. -- The button says what pressing it does; the encouragement says what happens next or how little it takes; the form's guidance line says what to have ready. Placeholders are example answers in the reader's words. -- An icon is one name from this list, chosen for what the reader is doing, or empty: house, building-2, store, school, map-pin, route, footprints, trees, leaf, snowflake, sun, users, user, user-plus, user-check, baby, heart-handshake, hand-helping, handshake, smile, mail, phone, message-circle, megaphone, bell, send, inbox, alert-triangle, siren, shield, construction, hammer, wrench, ruler, package, truck, car, bus, train-front, bike, calendar, clock, file-text, clipboard-list, receipt, wallet, coins, banknote, credit-card, key, lock, search, star, gift, shopping-cart, lightbulb, flag, thumbs-up, check-circle, briefcase, stethoscope, activity, camera, pencil, refresh-cw, list, layers, vote, scale -- Write the way people in this trade and country actually speak. - -{ - "custody_requests": { - "declined": { - "body": "", - "subject": "" - }, - "open": { - "body": "", - "subject": "" - }, - "resolved": { - "body": "", - "subject": "" - } - }, - "projects": { - "accepted": { - "body": "", - "subject": "" - }, - "declined": { - "body": "", - "subject": "" - }, - "open": { - "body": "", - "subject": "" - } - }, - "trials": { - "approved": { - "body": "", - "subject": "" - }, - "declined": { - "body": "", - "subject": "" - }, - "open": { - "body": "", - "subject": "" - }, - "previewed": { - "body": "", - "subject": "" - } - } -} - -Reply with the JSON object only. - -=== REPLY === -```json -{ - "custody_requests": { - "declined": { - "body": "We've read your message and decided this isn't something we can take on as custodians. If you think we've missed something, write back and tell us.", - "subject": "On your message to the custodians" - }, - "open": { - "body": "Your message reached the custodians of the first node. We read everything that comes in - expect a real reply, not an autoresponder.", - "subject": "We have your message" - }, - "resolved": { - "body": "This is resolved. Here's what we found and what we did about it - write back if it isn't settled from where you sit.", - "subject": "Your case is resolved" - } - }, - "projects": { - "accepted": { - "body": "We want to take this on. Next we'll be in touch to talk through scope, timing and how we'll work together.", - "subject": "We'd like to take this on" - }, - "declined": { - "body": "We've read your brief and this isn't one we're taking on. It isn't a judgment on the problem - just not a fit for us right now.", - "subject": "On your brief" - }, - "open": { - "body": "Your brief is in front of us. We read the whole thing before we reply, so it may take a few days - you'll hear from us either way.", - "subject": "We have your brief" - } - }, - "trials": { - "approved": { - "body": "Your draft is approved and on its way. We built it from your own words - have a look when it's ready and tell us what to change.", - "subject": "Your draft is approved" - }, - "declined": { - "body": "We've decided not to build this draft. If the situation changes, get back in touch and we'll take another look.", - "subject": "On your draft" - }, - "open": { - "body": "We have what you sent. A person will read it and build your draft, or tell you why not - you'll hear from us either way.", - "subject": "We're looking at your draft" - }, - "previewed": { - "body": "Your draft is ready to look at. It's built from your own words - the link below takes you straight to it.", - "subject": "Your draft is ready to view" - } - } -} -``` diff --git a/runs/uhhm-2026-09-23/site/model.json b/runs/uhhm-2026-09-23/site/model.json new file mode 100644 index 0000000..2129de9 --- /dev/null +++ b/runs/uhhm-2026-09-23/site/model.json @@ -0,0 +1,947 @@ +{ + "needs": [ + { + "id": "commission", + "who": "an organization with a problem it wants software to solve", + "wants": "a considered reply to a brief, from people who read the whole thing", + "enters_as": null, + "lands_in": "projects", + "handled_by": "owners", + "done_when": [ + "accepted", + "declined" + ], + "success": [ + "accepted" + ], + "steps": [ + { + "page": "/", + "question": "What is UHHM, and what can you get here?", + "context": "What UHHM does: Two things, both built here and both running. A network that answers to you: UHHM builds and carries the redoal network: the gesture math, the relay, the first node, and the portal this page runs on. Run a node, build on it, or have a recording removed, and you say when it is resolved. A considered reply to your brief, and software that fits the argument: Infrastructure, tools and products for organizations that can say what problem they have. Bring the brief, or first see your own way of working as a working site, before you pay for anything. The brief stays yours: you can withdraw it yourself.\nHave a look around first: Nothing to fill in here. What is already out in the world, and the network as it runs right now. What we have built: A few things already out in the world. The redoal network, live: Straight from the relay, the first node of the network.", + "options": [ + { + "name": "Have us build it: send the brief", + "description": "You know the problem and want it solved, whether it is infrastructure, a tool, or the first version of a product. Write it up for people who read the whole thing." + }, + { + "name": "See your own business as a site, before you buy anything", + "description": "Describe how requests, approvals and handovers move through your business today. We approve it by hand and build a working draft of a site for your own roles, for you to click through." + }, + { + "name": "Work with us", + "description": "A developer, a designer, a student: say what you would want to build with us, and we read it as a person and invite you in if there is a fit." + }, + { + "name": "Hear from us now and then", + "description": "You'd rather hear from us now and then than check back on your own." + }, + { + "name": "Suggest a change to this website", + "description": "You have spotted a page on uhhm.no that should read differently, and you know roughly how." + }, + { + "name": "You are on the redoal network: a node, an app, or a recording that concerns you", + "description": "UHHM hosts the redoal network's first node and answers for what it keeps. Write here if you run a node, build on the network, or want something it holds about you looked at." + } + ], + "expect": [ + "Have us build it: send the brief" + ], + "alternative": "Have us build it: send the brief", + "fields": [ + { + "name": "name", + "label": "Your name", + "type": "text", + "optional": false, + "options": [] + }, + { + "name": "email", + "label": "Your email", + "type": "email", + "optional": false, + "options": [] + }, + { + "name": "project", + "label": "The problem you want solved", + "type": "textarea", + "optional": false, + "options": [] + }, + { + "name": "organization", + "label": "Your organization", + "type": "text", + "optional": false, + "options": [] + }, + { + "name": "org_contact", + "label": "Who we should talk to", + "type": "text", + "optional": true, + "options": [] + } + ] + } + ] + }, + { + "id": "trial", + "who": "someone who runs a business where requests, approvals and handovers get lost, and wants to see it as a site before committing to anything", + "wants": "a first draft of a site for their own roles, built from their own words and approved by a person first", + "enters_as": null, + "lands_in": "trials", + "handled_by": "owners", + "done_when": [ + "previewed", + "declined" + ], + "success": [ + "previewed" + ], + "steps": [ + { + "page": "/", + "question": "What is UHHM, and what can you get here?", + "context": "What UHHM does: Two things, both built here and both running. A network that answers to you: UHHM builds and carries the redoal network: the gesture math, the relay, the first node, and the portal this page runs on. Run a node, build on it, or have a recording removed, and you say when it is resolved. A considered reply to your brief, and software that fits the argument: Infrastructure, tools and products for organizations that can say what problem they have. Bring the brief, or first see your own way of working as a working site, before you pay for anything. The brief stays yours: you can withdraw it yourself.\nHave a look around first: Nothing to fill in here. What is already out in the world, and the network as it runs right now. What we have built: A few things already out in the world. The redoal network, live: Straight from the relay, the first node of the network.", + "options": [ + { + "name": "Have us build it: send the brief", + "description": "You know the problem and want it solved, whether it is infrastructure, a tool, or the first version of a product. Write it up for people who read the whole thing." + }, + { + "name": "See your own business as a site, before you buy anything", + "description": "Describe how requests, approvals and handovers move through your business today. We approve it by hand and build a working draft of a site for your own roles, for you to click through." + }, + { + "name": "Work with us", + "description": "A developer, a designer, a student: say what you would want to build with us, and we read it as a person and invite you in if there is a fit." + }, + { + "name": "Hear from us now and then", + "description": "You'd rather hear from us now and then than check back on your own." + }, + { + "name": "Suggest a change to this website", + "description": "You have spotted a page on uhhm.no that should read differently, and you know roughly how." + }, + { + "name": "You are on the redoal network: a node, an app, or a recording that concerns you", + "description": "UHHM hosts the redoal network's first node and answers for what it keeps. Write here if you run a node, build on the network, or want something it holds about you looked at." + } + ], + "expect": [ + "See your own business as a site, before you buy anything" + ], + "alternative": "See your own business as a site, before you buy anything", + "fields": [ + { + "name": "business", + "label": "Your business", + "type": "text", + "optional": false, + "options": [] + }, + { + "name": "email", + "label": "Your email", + "type": "email", + "optional": false, + "options": [] + }, + { + "name": "what_we_do", + "label": "What you do", + "type": "textarea", + "optional": false, + "options": [] + }, + { + "name": "who_comes_to_us", + "label": "Who comes to you", + "type": "textarea", + "optional": false, + "options": [] + }, + { + "name": "what_we_keep_track_of", + "label": "What you keep track of", + "type": "textarea", + "optional": false, + "options": [] + }, + { + "name": "who_is_involved", + "label": "Who's involved", + "type": "textarea", + "optional": false, + "options": [] + }, + { + "name": "what_slows_us_down", + "label": "What slows you down", + "type": "textarea", + "optional": true, + "options": [] + } + ] + } + ] + }, + { + "id": "collaborate", + "who": "someone who wants to work with us on what we build", + "wants": "to be read as a person, and invited in if there is a fit", + "enters_as": null, + "lands_in": "applicants", + "handled_by": "owners", + "done_when": [ + "departed", + "declined" + ], + "success": [ + "departed" + ], + "steps": [ + { + "page": "/", + "question": "What is UHHM, and what can you get here?", + "context": "What UHHM does: Two things, both built here and both running. A network that answers to you: UHHM builds and carries the redoal network: the gesture math, the relay, the first node, and the portal this page runs on. Run a node, build on it, or have a recording removed, and you say when it is resolved. A considered reply to your brief, and software that fits the argument: Infrastructure, tools and products for organizations that can say what problem they have. Bring the brief, or first see your own way of working as a working site, before you pay for anything. The brief stays yours: you can withdraw it yourself.\nHave a look around first: Nothing to fill in here. What is already out in the world, and the network as it runs right now. What we have built: A few things already out in the world. The redoal network, live: Straight from the relay, the first node of the network.", + "options": [ + { + "name": "Have us build it: send the brief", + "description": "You know the problem and want it solved, whether it is infrastructure, a tool, or the first version of a product. Write it up for people who read the whole thing." + }, + { + "name": "See your own business as a site, before you buy anything", + "description": "Describe how requests, approvals and handovers move through your business today. We approve it by hand and build a working draft of a site for your own roles, for you to click through." + }, + { + "name": "Work with us", + "description": "A developer, a designer, a student: say what you would want to build with us, and we read it as a person and invite you in if there is a fit." + }, + { + "name": "Hear from us now and then", + "description": "You'd rather hear from us now and then than check back on your own." + }, + { + "name": "Suggest a change to this website", + "description": "You have spotted a page on uhhm.no that should read differently, and you know roughly how." + }, + { + "name": "You are on the redoal network: a node, an app, or a recording that concerns you", + "description": "UHHM hosts the redoal network's first node and answers for what it keeps. Write here if you run a node, build on the network, or want something it holds about you looked at." + } + ], + "expect": [ + "Work with us" + ], + "alternative": "Work with us", + "fields": [ + { + "name": "name", + "label": "Name", + "type": "text", + "optional": false, + "options": [] + }, + { + "name": "email", + "label": "Email", + "type": "email", + "optional": false, + "options": [] + }, + { + "name": "interest", + "label": "What kind of work", + "type": "textarea", + "optional": false, + "options": [] + }, + { + "name": "note", + "label": "Tell us more", + "type": "textarea", + "optional": true, + "options": [] + } + ] + } + ] + }, + { + "id": "subscribe", + "who": "someone who wants to hear from us when there is something to say", + "wants": "a note now and then, and an easy way out", + "enters_as": null, + "lands_in": "subscribers", + "handled_by": "owners", + "done_when": [ + "unsubscribed" + ], + "success": [], + "steps": [ + { + "page": "/", + "question": "What is UHHM, and what can you get here?", + "context": "What UHHM does: Two things, both built here and both running. A network that answers to you: UHHM builds and carries the redoal network: the gesture math, the relay, the first node, and the portal this page runs on. Run a node, build on it, or have a recording removed, and you say when it is resolved. A considered reply to your brief, and software that fits the argument: Infrastructure, tools and products for organizations that can say what problem they have. Bring the brief, or first see your own way of working as a working site, before you pay for anything. The brief stays yours: you can withdraw it yourself.\nHave a look around first: Nothing to fill in here. What is already out in the world, and the network as it runs right now. What we have built: A few things already out in the world. The redoal network, live: Straight from the relay, the first node of the network.", + "options": [ + { + "name": "Have us build it: send the brief", + "description": "You know the problem and want it solved, whether it is infrastructure, a tool, or the first version of a product. Write it up for people who read the whole thing." + }, + { + "name": "See your own business as a site, before you buy anything", + "description": "Describe how requests, approvals and handovers move through your business today. We approve it by hand and build a working draft of a site for your own roles, for you to click through." + }, + { + "name": "Work with us", + "description": "A developer, a designer, a student: say what you would want to build with us, and we read it as a person and invite you in if there is a fit." + }, + { + "name": "Hear from us now and then", + "description": "You'd rather hear from us now and then than check back on your own." + }, + { + "name": "Suggest a change to this website", + "description": "You have spotted a page on uhhm.no that should read differently, and you know roughly how." + }, + { + "name": "You are on the redoal network: a node, an app, or a recording that concerns you", + "description": "UHHM hosts the redoal network's first node and answers for what it keeps. Write here if you run a node, build on the network, or want something it holds about you looked at." + } + ], + "expect": [ + "Hear from us now and then" + ], + "alternative": "Hear from us now and then", + "fields": [ + { + "name": "name", + "label": "Name", + "type": "text", + "optional": false, + "options": [] + }, + { + "name": "email", + "label": "Email", + "type": "email", + "optional": false, + "options": [] + } + ] + } + ] + }, + { + "id": "propose", + "who": "a visitor who sees how a page on this site should change", + "wants": "the change read by a person and, if approved, made", + "enters_as": null, + "lands_in": "development_proposals", + "handled_by": "owners", + "done_when": [ + "approved", + "rejected" + ], + "success": [ + "approved" + ], + "steps": [ + { + "page": "/", + "question": "What is UHHM, and what can you get here?", + "context": "What UHHM does: Two things, both built here and both running. A network that answers to you: UHHM builds and carries the redoal network: the gesture math, the relay, the first node, and the portal this page runs on. Run a node, build on it, or have a recording removed, and you say when it is resolved. A considered reply to your brief, and software that fits the argument: Infrastructure, tools and products for organizations that can say what problem they have. Bring the brief, or first see your own way of working as a working site, before you pay for anything. The brief stays yours: you can withdraw it yourself.\nHave a look around first: Nothing to fill in here. What is already out in the world, and the network as it runs right now. What we have built: A few things already out in the world. The redoal network, live: Straight from the relay, the first node of the network.", + "options": [ + { + "name": "Have us build it: send the brief", + "description": "You know the problem and want it solved, whether it is infrastructure, a tool, or the first version of a product. Write it up for people who read the whole thing." + }, + { + "name": "See your own business as a site, before you buy anything", + "description": "Describe how requests, approvals and handovers move through your business today. We approve it by hand and build a working draft of a site for your own roles, for you to click through." + }, + { + "name": "Work with us", + "description": "A developer, a designer, a student: say what you would want to build with us, and we read it as a person and invite you in if there is a fit." + }, + { + "name": "Hear from us now and then", + "description": "You'd rather hear from us now and then than check back on your own." + }, + { + "name": "Suggest a change to this website", + "description": "You have spotted a page on uhhm.no that should read differently, and you know roughly how." + }, + { + "name": "You are on the redoal network: a node, an app, or a recording that concerns you", + "description": "UHHM hosts the redoal network's first node and answers for what it keeps. Write here if you run a node, build on the network, or want something it holds about you looked at." + } + ], + "expect": [ + "Suggest a change to this website" + ], + "alternative": "Suggest a change to this website", + "fields": [ + { + "name": "submitted_by", + "label": "Your name", + "type": "text", + "optional": false, + "options": [] + }, + { + "name": "email", + "label": "Email", + "type": "email", + "optional": true, + "options": [] + }, + { + "name": "page", + "label": "Which page", + "type": "text", + "optional": true, + "options": [] + }, + { + "name": "rationale", + "label": "Why this is better", + "type": "textarea", + "optional": false, + "options": [] + }, + { + "name": "proposed_yaml", + "label": "Your proposed text", + "type": "textarea", + "optional": false, + "options": [] + } + ] + } + ] + }, + { + "id": "custody", + "who": "someone who runs, builds on, or is affected by the redoal network and needs the people who keep its first node", + "wants": "an answer from a custodian, and action where the network keeps something it should not", + "enters_as": null, + "lands_in": "custody_requests", + "handled_by": "custodians", + "done_when": [ + "resolved", + "declined" + ], + "success": [ + "resolved" + ], + "steps": [ + { + "page": "/", + "question": "What is UHHM, and what can you get here?", + "context": "What UHHM does: Two things, both built here and both running. A network that answers to you: UHHM builds and carries the redoal network: the gesture math, the relay, the first node, and the portal this page runs on. Run a node, build on it, or have a recording removed, and you say when it is resolved. A considered reply to your brief, and software that fits the argument: Infrastructure, tools and products for organizations that can say what problem they have. Bring the brief, or first see your own way of working as a working site, before you pay for anything. The brief stays yours: you can withdraw it yourself.\nHave a look around first: Nothing to fill in here. What is already out in the world, and the network as it runs right now. What we have built: A few things already out in the world. The redoal network, live: Straight from the relay, the first node of the network.", + "options": [ + { + "name": "Have us build it: send the brief", + "description": "You know the problem and want it solved, whether it is infrastructure, a tool, or the first version of a product. Write it up for people who read the whole thing." + }, + { + "name": "See your own business as a site, before you buy anything", + "description": "Describe how requests, approvals and handovers move through your business today. We approve it by hand and build a working draft of a site for your own roles, for you to click through." + }, + { + "name": "Work with us", + "description": "A developer, a designer, a student: say what you would want to build with us, and we read it as a person and invite you in if there is a fit." + }, + { + "name": "Hear from us now and then", + "description": "You'd rather hear from us now and then than check back on your own." + }, + { + "name": "Suggest a change to this website", + "description": "You have spotted a page on uhhm.no that should read differently, and you know roughly how." + }, + { + "name": "You are on the redoal network: a node, an app, or a recording that concerns you", + "description": "UHHM hosts the redoal network's first node and answers for what it keeps. Write here if you run a node, build on the network, or want something it holds about you looked at." + } + ], + "expect": [ + "You are on the redoal network: a node, an app, or a recording that concerns you" + ], + "alternative": "You are on the redoal network: a node, an app, or a recording that concerns you", + "fields": [ + { + "name": "name", + "label": "Name", + "type": "text", + "optional": false, + "options": [] + }, + { + "name": "email", + "label": "Email", + "type": "email", + "optional": false, + "options": [] + }, + { + "name": "about", + "label": "What's going on", + "type": "select", + "optional": false, + "options": [ + "Running a node of my own", + "Building on the network", + "Something the network keeps about me", + "Something else" + ] + }, + { + "name": "what", + "label": "In short", + "type": "textarea", + "optional": false, + "options": [] + } + ] + } + ] + }, + { + "id": "prospect", + "who": "an owner who has met an organization worth following up", + "wants": "it written down and walked through its states from the desk", + "enters_as": "owners", + "lands_in": "organizations", + "handled_by": "owners", + "done_when": [ + "past_client" + ], + "success": [], + "steps": [ + { + "page": "/owners/prospect", + "question": "Is there an organization I should be keeping track of?", + "context": "", + "options": [ + { + "name": "Keep an organization on your radar", + "description": "One line per organization worth watching, moved through the stages as things develop." + } + ], + "expect": [ + "Keep an organization on your radar" + ], + "alternative": "Keep an organization on your radar", + "fields": [ + { + "name": "name", + "label": "Organization", + "type": "text", + "optional": false, + "options": [] + }, + { + "name": "contact", + "label": "Contact", + "type": "text", + "optional": false, + "options": [] + }, + { + "name": "note", + "label": "Note", + "type": "textarea", + "optional": true, + "options": [] + } + ] + } + ] + } + ], + "buckets": { + "projects": { + "initial": "open", + "transitions": { + "open": [ + "accepted", + "declined" + ] + }, + "words": { + "accepted": "taken on", + "declined": "not a fit for us", + "open": "received" + }, + "desks": [ + { + "page": "/owners", + "group": "owners", + "moves": [ + { + "from": "open", + "to": "accepted", + "label": "Accept brief" + }, + { + "from": "open", + "to": "declined", + "label": "Decline brief" + } + ] + } + ], + "self_moves": [ + "declined" + ] + }, + "trials": { + "initial": "open", + "transitions": { + "approved": [ + "previewed", + "declined" + ], + "open": [ + "approved", + "declined" + ] + }, + "words": { + "approved": "approved for a draft", + "declined": "not a fit for us", + "open": "received", + "previewed": "a preview is ready" + }, + "desks": [ + { + "page": "/owners", + "group": "owners", + "moves": [ + { + "from": "approved", + "to": "previewed", + "label": "Mark previewed" + }, + { + "from": "approved", + "to": "declined", + "label": "Decline trial" + }, + { + "from": "open", + "to": "approved", + "label": "Approve trial" + }, + { + "from": "open", + "to": "declined", + "label": "Decline trial" + } + ] + } + ], + "self_moves": [] + }, + "applicants": { + "initial": "open", + "transitions": { + "active": [ + "departed" + ], + "invited": [ + "active", + "declined" + ], + "open": [ + "invited", + "declined" + ] + }, + "words": { + "active": "working with us", + "declined": "not a fit for us", + "departed": "moved on", + "invited": "invited in", + "open": "received" + }, + "desks": [ + { + "page": "/owners", + "group": "owners", + "moves": [ + { + "from": "active", + "to": "departed", + "label": "Mark departed" + }, + { + "from": "invited", + "to": "active", + "label": "Confirm active" + }, + { + "from": "invited", + "to": "declined", + "label": "Withdraw the invite" + }, + { + "from": "open", + "to": "invited", + "label": "Send invite" + }, + { + "from": "open", + "to": "declined", + "label": "Decline applicant" + } + ] + } + ], + "self_moves": [] + }, + "subscribers": { + "initial": "open", + "transitions": { + "open": [ + "unsubscribed" + ] + }, + "words": { + "open": "on the list", + "unsubscribed": "off the list" + }, + "desks": [ + { + "page": "/owners", + "group": "owners", + "moves": [ + { + "from": "open", + "to": "unsubscribed", + "label": "Unsubscribe them" + } + ] + } + ], + "self_moves": [ + "unsubscribed" + ] + }, + "development_proposals": { + "initial": "open", + "transitions": { + "open": [ + "approved", + "rejected" + ] + }, + "words": { + "approved": "approved", + "open": "received", + "rejected": "turned down" + }, + "desks": [ + { + "page": "/owners", + "group": "owners", + "moves": [ + { + "from": "open", + "to": "approved", + "label": "Approve change" + }, + { + "from": "open", + "to": "rejected", + "label": "Reject change" + } + ] + } + ], + "self_moves": [] + }, + "custody_requests": { + "initial": "open", + "transitions": { + "open": [ + "in_dialogue", + "declined" + ], + "in_dialogue": [ + "resolved", + "declined" + ] + }, + "words": { + "declined": "closed without action", + "in_dialogue": "in dialogue with a custodian", + "open": "received", + "resolved": "resolved" + }, + "desks": [ + { + "page": "/custodians", + "group": "custodians", + "moves": [ + { + "from": "in_dialogue", + "to": "resolved", + "label": "Mark resolved" + }, + { + "from": "in_dialogue", + "to": "declined", + "label": "Close, no action" + }, + { + "from": "open", + "to": "in_dialogue", + "label": "Open dialogue" + }, + { + "from": "open", + "to": "declined", + "label": "Decline case" + } + ] + }, + { + "page": "/owners", + "group": "owners", + "moves": [] + } + ], + "self_moves": [ + "resolved" + ] + }, + "organizations": { + "initial": "prospect", + "transitions": { + "prospect": [ + "client", + "past_client" + ], + "client": [ + "past_client" + ] + }, + "words": { + "client": "a client", + "past_client": "a past client", + "prospect": "a prospect" + }, + "desks": [ + { + "page": "/owners", + "group": "owners", + "moves": [ + { + "from": "client", + "to": "past_client", + "label": "Mark past client" + }, + { + "from": "prospect", + "to": "client", + "label": "Mark as client" + }, + { + "from": "prospect", + "to": "past_client", + "label": "Archive as past client" + } + ] + } + ], + "self_moves": [] + } + }, + "personas": [ + { + "id": "architecture-firm", + "need": "commission", + "text": "We run a small architecture firm and our project files are scattered across three cloud drives. We want to pay someone to set up proper storage and backups we control ourselves." + }, + { + "id": "climbing-gym-founder", + "need": "commission", + "text": "I have an idea for a booking app for climbing gyms and I'm looking for a team that can build the first version with me." + }, + { + "id": "municipality-buyer", + "need": "commission", + "text": "Our municipality needs an internal tool for tracking building permits. We are looking for a vendor who can scope it and deliver it." + }, + { + "id": "exhibition-visitor", + "need": "subscribe", + "text": "I saw your drawing network at an exhibition and liked it. I don't want to get involved, I'd just like to know when there is news." + }, + { + "id": "tech-journalist", + "need": "subscribe", + "text": "I write about Nordic technology. Keep me posted on what you release so I can cover it." + }, + { + "id": "rust-developer", + "need": "collaborate", + "text": "I'm a Rust developer between jobs and I like that you build in the open. I would love to contribute, and maybe join you." + }, + { + "id": "interaction-designer", + "need": "collaborate", + "text": "I'm an interaction designer. I want to work with you on the drawing interface." + }, + { + "id": "student-intern", + "need": "collaborate", + "text": "I'm a computer science student looking for a summer internship doing infrastructure work." + }, + { + "id": "plumber-owner", + "need": "trial", + "text": "I run a plumbing company with four vans. Jobs come in by phone and text and some get lost. I'd like to see how this would look for us before I spend any money." + }, + { + "id": "dance-school", + "need": "trial", + "text": "We are a dance school. Enrolments, cancellations and invoices all go through one email inbox that only I understand. Can you show me a version made for us?" + }, + { + "id": "node-runner", + "need": "custody", + "text": "I run a small server in Berlin and want to host a redoal node that peers with yours. Who do I talk to?" + }, + { + "id": "recorded-name", + "need": "custody", + "text": "Someone left a recording on redoal that says my full name and where I live. I want it gone, and I want to know who is responsible for that network." + }, + { + "id": "app-builder", + "need": "custody", + "text": "I'm building a music app and would like to use redoal's gesture addresses in it. Is that possible, and who decides?" + }, + { + "id": "wording-fix", + "need": "propose", + "text": "The subscribe page should say how often you actually write. I'd like to suggest that exact change to the page." + }, + { + "id": "rewrite", + "need": "propose", + "text": "One page on this site reads wrong to me and I know exactly how I'd rewrite it. Where do I send the new text?" + } + ] +} \ No newline at end of file diff --git a/runs/uhhm-2026-09-23/site/needs.yaml b/runs/uhhm-2026-09-23/site/needs.yaml index 4c8e7d3..b94a531 100644 --- a/runs/uhhm-2026-09-23/site/needs.yaml +++ b/runs/uhhm-2026-09-23/site/needs.yaml @@ -8,6 +8,10 @@ needs: lands_in: projects max_fields: 4 max_steps: 1 + stage_words: + accepted: taken on + declined: not a fit for us + open: received stages: - open - accepted @@ -26,6 +30,11 @@ needs: lands_in: trials max_fields: 6 max_steps: 1 + stage_words: + approved: approved for a draft + declined: not a fit for us + open: received + previewed: a preview is ready stages: - open - approved @@ -45,6 +54,12 @@ needs: lands_in: applicants max_fields: 3 max_steps: 1 + stage_words: + active: working with us + declined: not a fit for us + departed: moved on + invited: invited in + open: received stages: - open - invited @@ -64,6 +79,9 @@ needs: lands_in: subscribers max_fields: 2 max_steps: 1 + stage_words: + open: on the list + unsubscribed: off the list stages: - open - unsubscribed @@ -80,6 +98,10 @@ needs: lands_in: development_proposals max_fields: 3 max_steps: 1 + stage_words: + approved: approved + open: received + rejected: turned down stages: - open - approved @@ -98,6 +120,11 @@ needs: lands_in: custody_requests max_fields: 4 max_steps: 1 + stage_words: + declined: closed without action + in_dialogue: in dialogue with a custodian + open: received + resolved: resolved stages: - open - in_dialogue @@ -117,6 +144,10 @@ needs: lands_in: organizations max_fields: 2 max_steps: 1 + stage_words: + client: a client + past_client: a past client + prospect: a prospect stages: - prospect - client diff --git a/runs/uhhm-2026-09-23/site/questions/owners/index.yaml b/runs/uhhm-2026-09-23/site/questions/owners/index.yaml index a59e0b0..dc1985e 100644 --- a/runs/uhhm-2026-09-23/site/questions/owners/index.yaml +++ b/runs/uhhm-2026-09-23/site/questions/owners/index.yaml @@ -15,14 +15,11 @@ alternatives: - from: active label: Mark departed to: departed - - from: active - label: End collaboration - to: declined - from: invited label: Confirm active to: active - from: invited - label: Withdraw invite + label: Withdraw the invite to: declined - from: open label: Send invite @@ -85,7 +82,7 @@ alternatives: label: Mark as client to: client - from: prospect - label: Mark not pursued + label: Archive as past client to: past_client name: Organizations you're tracking - action: /owners diff --git a/runs/uhhm-2026-09-23/site/words.json b/runs/uhhm-2026-09-23/site/words.json index 3a53bd4..50f504b 100644 --- a/runs/uhhm-2026-09-23/site/words.json +++ b/runs/uhhm-2026-09-23/site/words.json @@ -328,10 +328,9 @@ "description": "People who've asked to build with us, waiting on a decision or already working alongside us.", "heading": "People who want to build with us", "moves": { - "active->declined": "End collaboration", "active->departed": "Mark departed", "invited->active": "Confirm active", - "invited->declined": "Withdraw invite", + "invited->declined": "Withdraw the invite", "open->declined": "Decline applicant", "open->invited": "Send invite" } @@ -355,7 +354,7 @@ "moves": { "client->past_client": "Mark past client", "prospect->client": "Mark as client", - "prospect->past_client": "Mark not pursued" + "prospect->past_client": "Archive as past client" } }, "projects": { diff --git a/runs/uhhm-2026-09-23/words-r8.json b/runs/uhhm-2026-09-23/words-r8.json new file mode 100644 index 0000000..50f504b --- /dev/null +++ b/runs/uhhm-2026-09-23/words-r8.json @@ -0,0 +1,500 @@ +{ + "aggregates.yaml": { + "custody_requests": { + "declined": { + "body": "We've read your message and decided this isn't something we can take on as custodians. If you think we've missed something, write back and tell us.", + "subject": "On your message to the custodians" + }, + "open": { + "body": "Your message reached the custodians of the first node. We read everything that comes in - expect a real reply, not an autoresponder.", + "subject": "We have your message" + }, + "resolved": { + "body": "This is resolved. Here's what we found and what we did about it - write back if it isn't settled from where you sit.", + "subject": "Your case is resolved" + } + }, + "projects": { + "accepted": { + "body": "We want to take this on. Next we'll be in touch to talk through scope, timing and how we'll work together.", + "subject": "We'd like to take this on" + }, + "declined": { + "body": "We've read your brief and this isn't one we're taking on. It isn't a judgment on the problem - just not a fit for us right now.", + "subject": "On your brief" + }, + "open": { + "body": "Your brief is in front of us. We read the whole thing before we reply, so it may take a few days - you'll hear from us either way.\n\nWhat you can do next: {site}/decide/commission?chain={chain}&email={email}", + "subject": "We have your brief" + } + }, + "trials": { + "approved": { + "body": "Your draft is approved and on its way. We built it from your own words - have a look when it's ready and tell us what to change.", + "subject": "Your draft is approved" + }, + "declined": { + "body": "We've decided not to build this draft. If the situation changes, get back in touch and we'll take another look.", + "subject": "On your draft" + }, + "open": { + "body": "We have what you sent. A person will read it and build your draft, or tell you why not - you'll hear from us either way.", + "subject": "We're looking at your draft" + }, + "previewed": { + "body": "Your draft is ready to look at. It's built from your own words - the link below takes you straight to it.", + "subject": "Your draft is ready to view" + } + } + }, + "questions/custodians/index.yaml": { + "buckets": { + "custody_requests": { + "description": "Cases from people who use, build on, or answer for something the redoal network touches, and need a custodian's word on it.", + "heading": "Custody cases waiting on you", + "moves": { + "in_dialogue->declined": "Close, no action", + "in_dialogue->resolved": "Mark resolved", + "open->declined": "Decline case", + "open->in_dialogue": "Open dialogue" + } + } + }, + "confirm": "Confirm", + "description": "The desk for custodians: open custody cases, and the people invited to answer them.", + "invited": { + "description": "Custodians invited but not yet signed in.", + "done": "Signed in", + "heading": "Invited custodians" + }, + "invites": { + "custodians": { + "button": "Invite custodian", + "description": "Give someone an account and a one-time link to join as a custodian.", + "form_title": "Invite a custodian", + "heading": "Bring in another custodian", + "labels": { + "email": "Email", + "name": "Name" + } + } + }, + "links": {}, + "question": "What is waiting on the custodians?" + }, + "questions/decide/commission.yaml": { + "description": "", + "moves": { + "declined": { + "button": "Declined", + "description": "", + "heading": "Declined" + } + }, + "question": "What would you like to do?" + }, + "questions/decide/custody.yaml": { + "description": "", + "moves": { + "resolved": { + "button": "Resolved", + "description": "", + "heading": "Resolved" + } + }, + "question": "What would you like to do?" + }, + "questions/decide/subscribe.yaml": { + "description": "You're on our list. Change your mind whenever you like.", + "moves": { + "unsubscribed": { + "button": "Unsubscribe", + "description": "You'll stop hearing from us. Nothing else changes.", + "heading": "Stop getting notes from us" + } + }, + "question": "Do you want to keep hearing from us?" + }, + "questions/index.yaml": { + "description": "UHHM builds software for organisations that can say what problem they have, and keeps the redoal network underneath.", + "doors": {}, + "needs": { + "collaborate": { + "button": "Get in touch", + "description": "A developer, a designer, a student: say what you would want to build with us, and we read it as a person and invite you in if there is a fit.", + "encouragement": "A person reads this, not a form filter — worth the ten minutes.", + "form_description": "Tell us who you are and what kind of work you're after.", + "form_title": "Say hello", + "heading": "Work with us", + "hints": { + "email": "you@example.com", + "interest": "Backend work, or something design-heavy", + "name": "Your name", + "note": "A bit about what you'd want to work on, or a link to your work" + }, + "icon": "handshake", + "labels": { + "email": "Email", + "interest": "What kind of work", + "name": "Name", + "note": "Tell us more" + }, + "road_description": "We read what you send and get back to you. If there's a fit, we invite you in and you start working with us — and if things ever change, that's fine too.", + "road_icon": "route", + "road_title": "What happens next", + "who_description": "One of the owners reads this directly — for anything urgent, write to bl@uhhm.no.", + "who_icon": "user-check", + "who_title": "Who reads this" + }, + "commission": { + "button": "Send the brief", + "description": "You know the problem and want it solved, whether it is infrastructure, a tool, or the first version of a product. Write it up for people who read the whole thing.", + "encouragement": "Someone here reads every brief in full before writing back.", + "form_description": "The more of the problem you put in, the better the reply.", + "form_title": "Tell us the problem", + "heading": "Have us build it: send the brief", + "hints": { + "email": "sara@company.com", + "name": "Sara Nilsen", + "org_contact": "Sara Nilsen, ops manager", + "organization": "Nilsen Transport AS", + "project": "Our drivers log hours on paper and it takes a day to get them into payroll." + }, + "icon": "briefcase", + "labels": { + "email": "Your email", + "name": "Your name", + "org_contact": "Who we should talk to", + "organization": "Your organization", + "project": "The problem you want solved" + }, + "road_description": "We read your brief and either take it on or tell you honestly if it's not a fit.", + "road_icon": "route", + "road_title": "What happens next", + "who_description": "One of the owners reads it; write to bl@uhhm.no if you'd rather reach us directly.", + "who_icon": "user-check", + "who_title": "Who reads this" + }, + "custody": { + "button": "Send to a custodian", + "description": "UHHM hosts the redoal network's first node and answers for what it keeps. Write here if you run a node, build on the network, or want something it holds about you looked at.", + "encouragement": "A custodian reads every message that comes in here.", + "form_description": "Say which of the four it is, and what you need, in a few lines.", + "form_title": "Reach a custodian", + "heading": "You are on the redoal network: a node, an app, or a recording that concerns you", + "hints": { + "about": "We run our order system on a relay node and it's been dropping messages since Tuesday", + "email": "you@yourcompany.com", + "name": "Your name", + "what": "A relay node is misbehaving" + }, + "icon": "shield", + "labels": { + "about": "What's going on", + "email": "Email", + "name": "Name", + "what": "In short" + }, + "road_description": "A custodian opens a dialogue with you. A node gets its peer, a builder gets an answer, and something the network should not keep gets removed. Either way you hear what was decided.", + "road_icon": "route", + "road_title": "What happens next", + "who_description": "A custodian reads it directly; if it's urgent, write to bl@uhhm.no in the meantime.", + "who_icon": "user-check", + "who_title": "Who reads this" + }, + "propose": { + "button": "Send change", + "description": "You have spotted a page on uhhm.no that should read differently, and you know roughly how.", + "encouragement": "Even a rough version helps — we can tidy the wording ourselves.", + "form_description": "Point us to the page and show us the change.", + "form_title": "Propose a change", + "heading": "Suggest a change to this website", + "hints": { + "email": "you@example.com", + "page": "questions/join/index.yaml", + "proposed_yaml": "heading: \"A clearer way to say this\"", + "rationale": "The current heading doesn't say what you actually get", + "submitted_by": "Your name" + }, + "icon": "pencil", + "labels": { + "email": "Email", + "page": "Which page", + "proposed_yaml": "Your proposed text", + "rationale": "Why this is better", + "submitted_by": "Your name" + }, + "road_description": "Someone here reads your proposal and either makes the change or explains why not. Either way, you'll hear back.", + "road_icon": "route", + "road_title": "What happens next", + "who_description": "An owner reads every proposal before anything changes — reach us at bl@uhhm.no if it's time-sensitive.", + "who_icon": "user-check", + "who_title": "Who reads this" + }, + "subscribe": { + "button": "Sign up", + "description": "You'd rather hear from us now and then than check back on your own.", + "encouragement": "One click unsubscribes you later, no questions asked.", + "form_description": "Just your email, or your name too if you'd like.", + "form_title": "Stay in the loop", + "heading": "Hear from us now and then", + "hints": { + "email": "you@example.com", + "name": "Your name" + }, + "icon": "mail", + "labels": { + "email": "Email", + "name": "Name" + }, + "road_description": "You're on the list. We write when there's something worth saying, and you can leave the list whenever you like.", + "road_icon": "bell", + "road_title": "What happens next", + "who_description": "An owner sends these notes directly — write to bl@uhhm.no if you'd rather ask something first.", + "who_icon": "user-check", + "who_title": "Who reads this" + }, + "trial": { + "button": "Start the draft", + "description": "Describe how requests, approvals and handovers move through your business today. We approve it by hand and build a working draft of a site for your own roles, for you to click through.", + "encouragement": "A person looks at what you send before anything gets built.", + "form_description": "Answer in your own words - there's no wrong way to describe your business.", + "form_title": "Describe your business", + "heading": "See your own business as a site, before you buy anything", + "hints": { + "business": "Nilsen Transport AS", + "email": "sara@company.com", + "what_slows_us_down": "Approvals sit in someone's inbox for days before anyone acts on them.", + "what_we_do": "We move freight for local businesses and handle the paperwork for them.", + "what_we_keep_track_of": "Bookings, driver hours, and which loads are still waiting on a signature.", + "who_comes_to_us": "Local businesses that need something moved on short notice.", + "who_is_involved": "Drivers, dispatchers, and the customers waiting on their delivery." + }, + "icon": "layers", + "labels": { + "business": "Your business", + "email": "Your email", + "what_slows_us_down": "What slows you down", + "what_we_do": "What you do", + "what_we_keep_track_of": "What you keep track of", + "who_comes_to_us": "Who comes to you", + "who_is_involved": "Who's involved" + }, + "road_description": "We look at what you sent and, if it's a fit, build a draft from your own roles and words for you to see before anything moves further.", + "road_icon": "route", + "road_title": "What happens next", + "who_description": "An owner looks at what you send; write to bl@uhhm.no if you'd rather reach us directly.", + "who_icon": "user-check", + "who_title": "Who reads this" + } + }, + "opener": { + "description": "Two things, both built here and both running.", + "features": { + "redoal": { + "description": "UHHM builds and carries the redoal network: the gesture math, the relay, the first node, and the portal this page runs on. Run a node, build on it, or have a recording removed, and you say when it is resolved.", + "heading": "A network that answers to you", + "icon": "waypoints" + }, + "software": { + "description": "Infrastructure, tools and products for organizations that can say what problem they have. Bring the brief, or first see your own way of working as a working site, before you pay for anything. The brief stays yours: you can withdraw it yourself.", + "heading": "A considered reply to your brief, and software that fits the argument", + "icon": "layers" + } + }, + "heading": "What UHHM does" + }, + "question": "What is UHHM, and what can you get here?", + "shopping": { + "description": "Nothing to fill in here. What is already out in the world, and the network as it runs right now.", + "heading": "Have a look around first", + "items": { + "built": { + "description": "A few things already out in the world.", + "heading": "What we have built", + "icon": "hammer" + }, + "voices": { + "description": "Straight from the relay, the first node of the network.", + "heading": "The redoal network, live", + "icon": "activity" + } + } + } + }, + "questions/owners/index.yaml": { + "buckets": { + "applicants": { + "description": "People who've asked to build with us, waiting on a decision or already working alongside us.", + "heading": "People who want to build with us", + "moves": { + "active->departed": "Mark departed", + "invited->active": "Confirm active", + "invited->declined": "Withdraw the invite", + "open->declined": "Decline applicant", + "open->invited": "Send invite" + } + }, + "custody_requests": { + "description": "Questions and issues raised with the people who keep the first node - custodians handle these, but you can see where each one stands.", + "heading": "What's been raised with custodians", + "moves": {} + }, + "development_proposals": { + "description": "Changes visitors have proposed for pages on this site, waiting on a yes or no.", + "heading": "Proposed changes to the site", + "moves": { + "open->approved": "Approve change", + "open->rejected": "Reject change" + } + }, + "organizations": { + "description": "Organizations you've met, tracked from first contact through to client and beyond.", + "heading": "Organizations you're tracking", + "moves": { + "client->past_client": "Mark past client", + "prospect->client": "Mark as client", + "prospect->past_client": "Archive as past client" + } + }, + "projects": { + "description": "Briefs sent in by organizations with a problem they want solved.", + "heading": "Briefs waiting on a reply", + "moves": { + "open->accepted": "Accept brief", + "open->declined": "Decline brief" + } + }, + "subscribers": { + "description": "People who want to hear from you now and then.", + "heading": "Who's hearing from you", + "moves": { + "open->unsubscribed": "Unsubscribe them" + } + }, + "trials": { + "description": "Businesses who want to see their own workflow as a working site before committing to anything.", + "heading": "Trial sites in progress", + "moves": { + "approved->declined": "Decline trial", + "approved->previewed": "Mark previewed", + "open->approved": "Approve trial", + "open->declined": "Decline trial" + } + } + }, + "confirm": "Confirm", + "description": "Everything waiting on you: briefs, trials, applicants, subscribers, and proposals, all in one place.", + "invited": { + "description": "People you've invited in, and whether they've taken it up yet.", + "done": "Taken up", + "heading": "Invites you've sent" + }, + "invites": { + "custodians": { + "button": "Send invite", + "description": "Bring someone in to help keep the first node and answer for the network.", + "form_title": "Invite a custodian", + "heading": "Bring in a custodian", + "labels": { + "email": "Their email", + "name": "Their name" + } + }, + "owners": { + "button": "Send invite", + "description": "Bring someone in to work this desk alongside you.", + "form_title": "Invite an owner", + "heading": "Bring in another owner", + "labels": { + "email": "Their email", + "name": "Their name" + } + } + }, + "links": { + "prospect": { + "button": "Add prospect", + "description": "Write down an organization worth following up, and track it from first contact onward.", + "heading": "Track a new organization" + } + }, + "question": "What's waiting on me today?" + }, + "questions/owners/prospect-sent.yaml": { + "back": "Back to site", + "description": "Thanks — we've got your prospect written down, and it now moves through the desk with everything else we're tracking.", + "next_description": "An owner walks it from prospect to client, and on to past client, as the relationship moves.", + "next_heading": "It's on the desk now", + "question": "What happens to this now?" + }, + "questions/owners/prospect.yaml": { + "button": "Add to prospects", + "description": "One line per organization worth watching, moved through the stages as things develop.", + "encouragement": "Takes a minute, and it's yours to update later.", + "form_description": "Name the organization and where things stand.", + "form_title": "New prospect", + "heading": "Keep an organization on your radar", + "hints": { + "contact": "met at the bakers' conference, [email protected]", + "name": "Nordvik Bakery", + "note": "wants a booking flow, follow up after harvest season" + }, + "icon": "briefcase", + "labels": { + "contact": "Contact", + "name": "Organization", + "note": "Note" + }, + "page_description": "Write down an organization worth following up, and move it through client and past client as things develop.", + "question": "Is there an organization I should be keeping track of?", + "road_description": "It sits as a prospect until something moves - then you mark it client, and later past client if the work ends.", + "road_icon": "route", + "road_title": "Moves as you update it", + "who_description": "Only you see this, so keep it wherever you check next.", + "who_icon": "user", + "who_title": "Just for you" + }, + "questions/received/collaborate.yaml": { + "back": "Back to site", + "description": "Your note is in. An owner reads every one and will get back to you if there's a fit worth exploring.", + "next_description": "If it looks like a fit, we'll reach out to talk about where you could help.", + "next_heading": "We'll write if there's a fit", + "question": "What happens to what I just sent?" + }, + "questions/received/commission.yaml": { + "back": "Back to home", + "description": "Your brief is in. An owner reads every commission end to end before replying.", + "next_description": "Expect a reply within a few working days, either way, straight to your inbox.", + "next_heading": "A real reply from someone who read it", + "question": "What happens now that we've sent our brief?" + }, + "questions/received/custody.yaml": { + "back": "Back to the site", + "description": "A custodian reads every message about the redoal network and what it keeps.", + "next_description": "Expect a reply once your report has been looked at and the network checked.", + "next_heading": "A custodian is on it", + "question": "Who looks after what the network keeps?" + }, + "questions/received/propose.yaml": { + "back": "Back home", + "description": "Your proposal is in. Someone here will read it and decide whether it fits before anything changes on the page.", + "next_description": "We'll write back once it's been read — approved and made live, or turned down with a reason.", + "next_heading": "A read, then a decision", + "question": "What happens to my proposal now?" + }, + "questions/received/subscribe.yaml": { + "back": "Back to site", + "description": "You'll hear from us when there's something worth saying, not on a schedule.", + "next_description": "You're on the list. Every note has an unsubscribe link if it stops being useful.", + "next_heading": "You're on the list", + "question": "What happens now that I've subscribed?" + }, + "questions/received/trial.yaml": { + "back": "Back to site", + "description": "We read every trial request ourselves before anything gets built.", + "next_description": "If it looks like a fit, we'll draft your site from your own words and send it back for your say before anything goes live.", + "next_heading": "A first draft, built from your own words", + "question": "What happens now that you've sent this?" + } +} \ No newline at end of file diff --git a/src/build.rs b/src/build.rs index c974cd2..a5bdd82 100644 --- a/src/build.rs +++ b/src/build.rs @@ -171,6 +171,11 @@ pub struct PlanNeed { /// were complete). #[serde(default)] pub skippable: Vec, + /// The unfinished stages a case may still be turned down from; + /// empty means every one. An applicant is declined before they + /// start and not after, where they depart instead. + #[serde(default)] + pub turned_down_from: Vec, /// Who presses the button that moves a case INTO a stage, where it /// is not `handled_by`: another group, several groups, or /// `submitter` - the person who sent the form, through their own @@ -351,6 +356,11 @@ impl Plan { if need.watched_by.iter().any(|g| g == PUBLIC) && need.enters_as.is_some() { bail!(at("a public view is for what the public sent; an internal need has its group's desk")); } + for state in &need.turned_down_from { + if need.done_when.contains(state) || !need.stages.contains(state) { + bail!(at(&format!("turned_down_from names {state:?}, which is not one of its unfinished stages"))); + } + } for state in need.done_when.iter().chain(&need.skippable) { if !need.stages.contains(state) { bail!(at(&format!("{state:?} is not one of its stages"))); @@ -441,6 +451,7 @@ impl Plan { || other.attended_by != first.attended_by || other.mails != first.mails || other.stages_text != first.stages_text + || other.turned_down_from != first.turned_down_from { bail!("needs {:?} and {:?} both land in {bucket:?} and must agree on stages, done_when, skippable, events, attended_by, mails and who moves and watches it", first.id, other.id); } @@ -516,7 +527,9 @@ pub fn machine(need: &PlanNeed) -> BTreeMap> { } } } - to.extend(ends_badly.iter().map(|s| (*s).clone())); + if need.turned_down_from.is_empty() || need.turned_down_from.contains(stage) { + to.extend(ends_badly.iter().map(|s| (*s).clone())); + } moves.insert((*stage).clone(), to); } moves @@ -1642,7 +1655,7 @@ mod tests { lands_in: "b".into(), handled_by: "g".into(), stages: v(stages), done_when: v(done), success: v(success), skippable: v(skippable), moves_by: BTreeMap::new(), watched_by: vec![], - events: BTreeMap::new(), attended_by: None, mails: BTreeMap::new(), stages_text: BTreeMap::new(), + events: BTreeMap::new(), attended_by: None, mails: BTreeMap::new(), stages_text: BTreeMap::new(), turned_down_from: vec![], collects: vec![], max_fields: 3, } } @@ -1720,6 +1733,16 @@ mod tests { assert!(!m.contains_key("won")); } + #[test] + fn a_case_is_turned_down_only_where_the_plan_says() { + let mut n = need(&["open", "invited", "active", "departed", "declined"], &["departed", "declined"], &["departed"], &[]); + n.turned_down_from = vec!["open".into(), "invited".into()]; + let m = machine(&n); + assert_eq!(m["open"], ["invited", "declined"]); + assert_eq!(m["invited"], ["active", "declined"]); + assert_eq!(m["active"], ["departed"]); + } + #[test] fn a_skippable_stage_can_be_passed_over() { let m = machine(&need(&["open", "awaiting_papers", "approved", "declined"], &["approved", "declined"], &["approved"], &["awaiting_papers"])); diff --git a/src/plan.rs b/src/plan.rs index f90ee3a..8424453 100644 --- a/src/plan.rs +++ b/src/plan.rs @@ -70,6 +70,10 @@ needs: done_when: [, ] # every stage that counts as finished, good or bad success: [] # the finished stages that are a good outcome for the business skippable: [] # stages a case may pass over (papers already complete) + turned_down_from: [open, ] + # the unfinished stages a case can still be turned down from; + # omit for every one. An applicant is declined before they + # start, not after - after, they depart moves_by: { : [, ...] } # only where someone other than handled_by moves a case INTO a # stage: another group, or `submitter` (the person who sent the