plan: a spectator role goes back to the model once; bare-word movers and unlabelled groups parse

Re-planning the contractor showed three wasted rounds: `moves_by: {
checking: submitter }` written as a bare word, a group without a
label, and the two featured owner roles left with nothing to play,
which the builder only warned about after the fact. The bare word and
the missing label now parse (a label defaults to the id), and a plan
that parses with spectator roles is sent back once with the finding;
on the contractor, round three fixed both.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Bendik Aagaard Lynghaug
2026-09-24 06:42:05 +02:00
co-authored by Claude Fable 5.1
parent 6e49694211
commit 4d22f4cb44
67 changed files with 6497 additions and 7 deletions
@@ -28,3 +28,19 @@ the tell of a model counting the form; only the crew's report (gloves
on, 3 tolerated, 2 asked) was decided from the intake. `plan` now
says so, and the reference tells the model to decide the number
first.
## Re-planned twice, 2026-09-24
`r2/`: the reference now teaches the opener, the mails, stage words
and `turned_down_from`. The model used the opener, the sender and
mails again, ignored the words and the turned-down key (its ids are
English, so they cost nothing), lost round one to `moves_by: {
checking: submitter }` written as a bare word, and again gave the two
featured owner roles no move. Desks: 19 of 19, English ids scoring
fine. `r3/`: the bare word is accepted, and the spectator finding goes
back to the model for one more round: round two flagged both roles,
round three fixed them, and the built site raised no warning. A group
without a `label` cost a round too; the label now defaults to the id.
The budget tell fired on both: three or four of the needs with
max_fields equal to the form. The model does not decide that number
first however it is told; the reader does.
@@ -0,0 +1,168 @@
# plan.yaml
A plan is a needs spec that also says what each form collects. A
builder turns it into a working site with no further decisions, so
every structural fact about the business has to be in it.
```yaml
business: <name>
language: en # the intake's language code
contact: { name: <who answers for the site>, email: <their email> }
about: >
What the business does, in its own words.
mail: { from: "<name> <post@their-domain>", reply_to: <their inbox> }
# who every mail the site sends comes from; required as soon
# as any need has `mails`
does: # the front door's first card: what the business does, one
- id: kebab-case # or two features, one role each, no button. Say it for the
for: <need id> # role the business exists for and the next one it serves.
text: what it does for them, in the intake's own words
shows: # things a visitor may look at before asking for anything;
- id: kebab-case # each `resource` is portal's resource block, verbatim
text: one line on what it is
resource: { public: true, source: { kind: url, url: https://... } }
doors: # a first question before the forms, only when the offers
- id: kebab-case # are more than one page holds (measured: four doors beat
who: who comes through it # ten offers on one page; six offers on one page beat doors)
groups: # groups with a label the public may see: the committees
- { id: traffic, label: Traffic, invited_by: [board] } # a person can ask to join (optional)
invites_by: [board] # groups whose desk may invite people into every desk
needs:
- id: kebab-case # ASCII only
who: the kind of person, in plain words, in the intake's language
wants: what they want from the business
door: <door id> # the door this public need sits behind; omit to put it on the front door
enters_as: <group> # ONLY for a role inside the business; omit for the public
lands_in: <bucket> # snake_case, ASCII
handled_by: <group> # the role that must see and move the record
stages: [open, <stage>, ..., <end>, <end>]
# EVERY stage the business said a case goes through, first to
# last, read off their own sentences one verb at a time: "they
# come in, we go and look, we send a quote, we win or lose it"
# is [open, surveyed, quoted, won, lost]. A pipeline is not an
# inbox: "answered" is rarely the real outcome.
done_when: [<end>, <end>] # every stage that counts as finished, good or bad
success: [<end>] # the finished stages that are a good outcome for the business
skippable: [<stage>] # stages a case may pass over (papers already complete)
turned_down_from: [open, <stage>]
# 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: { <stage>: [<group>, ...] }
# only where someone other than handled_by moves a case INTO a
# stage: another group, or `submitter` (the person who sent the
# form; the form must then collect a required `email`)
watched_by: [<group>] # groups that see these cases and move nothing; `public` opens a
# read-only page of the bucket to everyone (contact details hidden)
mails: { open: submitter, <end>: submitter, <stage>: <address> }
# who hears when a case enters a stage: `submitter` (the form
# must then collect a required `email`) or one address. Tell the
# person on arrival and on every decision that is about them.
stages_text: { open: <words>, <stage>: <words> }
# each stage in the intake's language, where the ASCII id
# would read wrong to a person: "ikke_lost" is "ikke løst"
events: { open: applied } # ONLY when rebuilding a live site: the event names its
# aggregates.yaml already has, so its records keep replaying
attended_by: > # ONLY for a stage no desk button moves: the automation that does
what moves it, in a sentence
max_fields: 4 # decide this BEFORE writing collects, from what the intake
# says about the person; a budget that equals the form's size
# is counted, not decided, and is reported as such
# how many required fields THIS person will tolerate, from what
# the intake says about them: someone on a phone with gloves
# on gets 3, a first enquiry 4 to 6, a firm's paperwork 5 to 6
collects: # the form; name is snake_case ASCII, type one of
# text (default), email, tel, textarea, date, select
- { name: name }
- { name: phone, type: tel }
- { name: problem, type: textarea }
- { name: urgency, type: select, options: [This week, This month, No rush] }
- { name: can_do, type: select, multiple: true, options: [Drive, Cook, Carry] } # any number
- { name: committee, type: select, options_from: groups } # the labelled groups above
- { name: address, optional: true, private: true } # never on a public page
```
Rules:
- One need per (kind of person, thing they want). Work inside the
business counts: a role recording or posting something is a need
with `enters_as`. The intake's "what slows us down" usually names one.
- Groups are the roles the intake names, as snake_case identifiers.
- The front door opens with `does` (at most two features, one role
each) and `shows`, then holds at most seven offers: doors plus needs
with no door. A door with no need behind it, or a door on a need with
`enters_as`, is refused. A role `does` speaks for must have a move
of its own after sending (`moves_by` with `submitter`) or a public
view, or it is a spectator.
- Every need whose form collects an email should tell the person on
arrival and on the decisions about them (`mails`); the plan then
needs `mail: { from }`.
- Needs that land in the same bucket must have the same stages,
done_when, skippable, moves_by, watched_by and handled_by.
- Every required field is one the business will use to act on the
record. If the intake gives no reason to need it, make it optional or
leave it out. The required fields must not exceed max_fields.
- Only `select` lists `options`, and it must list them.
- Everything a person reads (who, wants, about, select options) is in
the intake's language. Everything a machine reads (ids, buckets,
groups, stages, field names) is plain ASCII: no æ, ø, å.
- Write no personas.
# Task
A business filled in this intake. Write its plan.yaml: every need the intake implies, for the public and for each role inside the business, with the stages read off how the business says things progress, the form each need collects, and how much each person will tolerate.
## Intake
# An intake as the owner of a small building contractor would fill it
# in: plain words, no portal vocabulary. These are the fields the
# onboarding site asks for.
business: Nordvik Bygg
contact: { name: Ingrid Nordvik, email: ingrid@nordvikbygg.example }
language: en
what_we_do: >
We are twelve people. We survey old buildings, draw up rehabilitation
plans, handle the permit application, and do the building work with
our own carpenters plus subcontractors for electrical, plumbing and
scaffolding.
who_comes_to_us: >
Housing boards and private owners who have a problem with a building
and want someone to look at it. Owners who already know what they
want built and need drawings, a permit and a price. Trade firms who
want to be on our list of subcontractors. Carpenters and apprentices
asking if we are hiring.
what_we_keep_track_of: >
Enquiries: they come in, we go and look at the building, we send a
quote, and then we either win it or lose it. Sometimes it is simply
not for us and we say so straight away.
Subcontractors: they apply, we check their company registration,
trade approval and insurance. Often papers are missing and we have to
ask again. Then they are approved or turned down.
Job applications: we read them, invite some to an interview, and hire
or decline.
Things that go wrong on site: a hazard, a near miss, wrong materials
delivered. Someone reports it, the safety officer decides what is
done about it, and closes it when it is fixed.
who_is_involved: >
Two project managers handle enquiries, subcontractors and hiring.
One safety officer handles everything reported from site.
The crew, ours and the subcontractors' people, are the ones on site
who report things. They are on their phones, with gloves on, and
will not fill in a long form.
what_slows_us_down: >
Enquiries arrive by phone, email and text and get lost. We never know
which subcontractors have valid papers. Site problems get mentioned
at lunch and forgotten.
Reply with the YAML only, no prose, no code fences.
@@ -0,0 +1,112 @@
```yaml
business: Nordvik Bygg
language: en
contact: { name: Ingrid Nordvik, email: ingrid@nordvikbygg.example }
about: >
We are twelve people. We survey old buildings, draw up rehabilitation
plans, handle the permit application, and do the building work with
our own carpenters plus subcontractors for electrical, plumbing and
scaffolding.
mail: { from: "Nordvik Bygg <post@nordvikbygg.example>", reply_to: ingrid@nordvikbygg.example }
does:
- id: assess-your-building
for: building-problem
text: We survey the building, draw up a rehabilitation plan, and handle the permit application.
- id: build-your-project
for: new-build-project
text: Already know what you want built? We do the drawings, the permit and the price, then build it with our own carpenters and trusted subcontractors.
groups:
- { id: project_managers, label: Project Managers }
- { id: safety_officer, label: Safety Officer }
- { id: crew, label: Crew }
needs:
- id: building-problem
who: housing boards and private owners with a problem with a building
wants: someone to come and look at the building and tell them what can be done
lands_in: enquiries
handled_by: project_managers
stages: [open, surveyed, quoted, won, lost]
done_when: [won, lost]
success: [won]
skippable: [surveyed, quoted]
mails: { open: submitter, won: submitter, lost: submitter }
max_fields: 4
collects:
- { name: name }
- { name: email, type: email }
- { name: phone, type: tel }
- { name: problem, type: textarea }
- { name: address, optional: true, private: true }
- id: new-build-project
who: owners who already know what they want built
wants: drawings, a permit and a price
lands_in: enquiries
handled_by: project_managers
stages: [open, surveyed, quoted, won, lost]
done_when: [won, lost]
success: [won]
skippable: [surveyed, quoted]
mails: { open: submitter, won: submitter, lost: submitter }
max_fields: 4
collects:
- { name: name }
- { name: email, type: email }
- { name: phone, type: tel }
- { name: project_description, type: textarea }
- id: subcontractor-application
who: trade firms who want to be on the list of subcontractors
wants: to be approved as a subcontractor
lands_in: subcontractors
handled_by: project_managers
stages: [open, checking, papers_requested, approved, declined]
done_when: [approved, declined]
success: [approved]
skippable: [papers_requested]
moves_by: { checking: submitter }
mails: { open: submitter, papers_requested: submitter, approved: submitter, declined: submitter }
max_fields: 5
collects:
- { name: company_name }
- { name: email, type: email }
- { name: phone, type: tel }
- { name: trade, type: select, options: [Electrical, Plumbing, Scaffolding, Other] }
- { name: registration_number }
- { name: insurance_info, type: textarea, optional: true }
- id: job-application
who: carpenters and apprentices asking if we are hiring
wants: a job with us
lands_in: job_applications
handled_by: project_managers
stages: [open, interviewing, hired, declined]
done_when: [hired, declined]
success: [hired]
skippable: [interviewing]
mails: { open: submitter, hired: submitter, declined: submitter }
max_fields: 4
collects:
- { name: name }
- { name: email, type: email }
- { name: phone, type: tel }
- { name: message, type: textarea }
- id: report-site-issue
who: crew, ours and the subcontractors' people, on site
wants: to report a hazard, near miss or wrong delivery
enters_as: crew
lands_in: site_issues
handled_by: safety_officer
stages: [open, action_decided, closed]
done_when: [closed]
success: [closed]
max_fields: 3
collects:
- { name: issue_type, type: select, options: [Hazard, Near miss, Wrong materials, Other] }
- { name: location }
- { name: details, type: textarea, optional: true }
```
@@ -0,0 +1,288 @@
# plan.yaml
A plan is a needs spec that also says what each form collects. A
builder turns it into a working site with no further decisions, so
every structural fact about the business has to be in it.
```yaml
business: <name>
language: en # the intake's language code
contact: { name: <who answers for the site>, email: <their email> }
about: >
What the business does, in its own words.
mail: { from: "<name> <post@their-domain>", reply_to: <their inbox> }
# who every mail the site sends comes from; required as soon
# as any need has `mails`
does: # the front door's first card: what the business does, one
- id: kebab-case # or two features, one role each, no button. Say it for the
for: <need id> # role the business exists for and the next one it serves.
text: what it does for them, in the intake's own words
shows: # things a visitor may look at before asking for anything;
- id: kebab-case # each `resource` is portal's resource block, verbatim
text: one line on what it is
resource: { public: true, source: { kind: url, url: https://... } }
doors: # a first question before the forms, only when the offers
- id: kebab-case # are more than one page holds (measured: four doors beat
who: who comes through it # ten offers on one page; six offers on one page beat doors)
groups: # groups with a label the public may see: the committees
- { id: traffic, label: Traffic, invited_by: [board] } # a person can ask to join (optional)
invites_by: [board] # groups whose desk may invite people into every desk
needs:
- id: kebab-case # ASCII only
who: the kind of person, in plain words, in the intake's language
wants: what they want from the business
door: <door id> # the door this public need sits behind; omit to put it on the front door
enters_as: <group> # ONLY for a role inside the business; omit for the public
lands_in: <bucket> # snake_case, ASCII
handled_by: <group> # the role that must see and move the record
stages: [open, <stage>, ..., <end>, <end>]
# EVERY stage the business said a case goes through, first to
# last, read off their own sentences one verb at a time: "they
# come in, we go and look, we send a quote, we win or lose it"
# is [open, surveyed, quoted, won, lost]. A pipeline is not an
# inbox: "answered" is rarely the real outcome.
done_when: [<end>, <end>] # every stage that counts as finished, good or bad
success: [<end>] # the finished stages that are a good outcome for the business
skippable: [<stage>] # stages a case may pass over (papers already complete)
turned_down_from: [open, <stage>]
# 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: { <stage>: [<group>, ...] }
# only where someone other than handled_by moves a case INTO a
# stage: another group, or `submitter` (the person who sent the
# form; the form must then collect a required `email`)
watched_by: [<group>] # groups that see these cases and move nothing; `public` opens a
# read-only page of the bucket to everyone (contact details hidden)
mails: { open: submitter, <end>: submitter, <stage>: <address> }
# who hears when a case enters a stage: `submitter` (the form
# must then collect a required `email`) or one address. Tell the
# person on arrival and on every decision that is about them.
stages_text: { open: <words>, <stage>: <words> }
# each stage in the intake's language, where the ASCII id
# would read wrong to a person: "ikke_lost" is "ikke løst"
events: { open: applied } # ONLY when rebuilding a live site: the event names its
# aggregates.yaml already has, so its records keep replaying
attended_by: > # ONLY for a stage no desk button moves: the automation that does
what moves it, in a sentence
max_fields: 4 # decide this BEFORE writing collects, from what the intake
# says about the person; a budget that equals the form's size
# is counted, not decided, and is reported as such
# how many required fields THIS person will tolerate, from what
# the intake says about them: someone on a phone with gloves
# on gets 3, a first enquiry 4 to 6, a firm's paperwork 5 to 6
collects: # the form; name is snake_case ASCII, type one of
# text (default), email, tel, textarea, date, select
- { name: name }
- { name: phone, type: tel }
- { name: problem, type: textarea }
- { name: urgency, type: select, options: [This week, This month, No rush] }
- { name: can_do, type: select, multiple: true, options: [Drive, Cook, Carry] } # any number
- { name: committee, type: select, options_from: groups } # the labelled groups above
- { name: address, optional: true, private: true } # never on a public page
```
Rules:
- One need per (kind of person, thing they want). Work inside the
business counts: a role recording or posting something is a need
with `enters_as`. The intake's "what slows us down" usually names one.
- Groups are the roles the intake names, as snake_case identifiers.
- The front door opens with `does` (at most two features, one role
each) and `shows`, then holds at most seven offers: doors plus needs
with no door. A door with no need behind it, or a door on a need with
`enters_as`, is refused. A role `does` speaks for must have a move
of its own after sending (`moves_by` with `submitter`) or a public
view, or it is a spectator.
- Every need whose form collects an email should tell the person on
arrival and on the decisions about them (`mails`); the plan then
needs `mail: { from }`.
- Needs that land in the same bucket must have the same stages,
done_when, skippable, moves_by, watched_by and handled_by.
- Every required field is one the business will use to act on the
record. If the intake gives no reason to need it, make it optional or
leave it out. The required fields must not exceed max_fields.
- Only `select` lists `options`, and it must list them.
- Everything a person reads (who, wants, about, select options) is in
the intake's language. Everything a machine reads (ids, buckets,
groups, stages, field names) is plain ASCII: no æ, ø, å.
- Write no personas.
# Task
A business filled in this intake. Write its plan.yaml: every need the intake implies, for the public and for each role inside the business, with the stages read off how the business says things progress, the form each need collects, and how much each person will tolerate.
## Intake
# An intake as the owner of a small building contractor would fill it
# in: plain words, no portal vocabulary. These are the fields the
# onboarding site asks for.
business: Nordvik Bygg
contact: { name: Ingrid Nordvik, email: ingrid@nordvikbygg.example }
language: en
what_we_do: >
We are twelve people. We survey old buildings, draw up rehabilitation
plans, handle the permit application, and do the building work with
our own carpenters plus subcontractors for electrical, plumbing and
scaffolding.
who_comes_to_us: >
Housing boards and private owners who have a problem with a building
and want someone to look at it. Owners who already know what they
want built and need drawings, a permit and a price. Trade firms who
want to be on our list of subcontractors. Carpenters and apprentices
asking if we are hiring.
what_we_keep_track_of: >
Enquiries: they come in, we go and look at the building, we send a
quote, and then we either win it or lose it. Sometimes it is simply
not for us and we say so straight away.
Subcontractors: they apply, we check their company registration,
trade approval and insurance. Often papers are missing and we have to
ask again. Then they are approved or turned down.
Job applications: we read them, invite some to an interview, and hire
or decline.
Things that go wrong on site: a hazard, a near miss, wrong materials
delivered. Someone reports it, the safety officer decides what is
done about it, and closes it when it is fixed.
who_is_involved: >
Two project managers handle enquiries, subcontractors and hiring.
One safety officer handles everything reported from site.
The crew, ours and the subcontractors' people, are the ones on site
who report things. They are on their phones, with gloves on, and
will not fill in a long form.
what_slows_us_down: >
Enquiries arrive by phone, email and text and get lost. We never know
which subcontractors have valid papers. Site problems get mentioned
at lunch and forgotten.
## Your last attempt was refused
does not parse as a plan: needs[2].moves_by.checking: invalid type: string "submitter", expected a sequence at line 69 column 27
Fix that and return the whole plan again.
```yaml
business: Nordvik Bygg
language: en
contact: { name: Ingrid Nordvik, email: ingrid@nordvikbygg.example }
about: >
We are twelve people. We survey old buildings, draw up rehabilitation
plans, handle the permit application, and do the building work with
our own carpenters plus subcontractors for electrical, plumbing and
scaffolding.
mail: { from: "Nordvik Bygg <post@nordvikbygg.example>", reply_to: ingrid@nordvikbygg.example }
does:
- id: assess-your-building
for: building-problem
text: We survey the building, draw up a rehabilitation plan, and handle the permit application.
- id: build-your-project
for: new-build-project
text: Already know what you want built? We do the drawings, the permit and the price, then build it with our own carpenters and trusted subcontractors.
groups:
- { id: project_managers, label: Project Managers }
- { id: safety_officer, label: Safety Officer }
- { id: crew, label: Crew }
needs:
- id: building-problem
who: housing boards and private owners with a problem with a building
wants: someone to come and look at the building and tell them what can be done
lands_in: enquiries
handled_by: project_managers
stages: [open, surveyed, quoted, won, lost]
done_when: [won, lost]
success: [won]
skippable: [surveyed, quoted]
mails: { open: submitter, won: submitter, lost: submitter }
max_fields: 4
collects:
- { name: name }
- { name: email, type: email }
- { name: phone, type: tel }
- { name: problem, type: textarea }
- { name: address, optional: true, private: true }
- id: new-build-project
who: owners who already know what they want built
wants: drawings, a permit and a price
lands_in: enquiries
handled_by: project_managers
stages: [open, surveyed, quoted, won, lost]
done_when: [won, lost]
success: [won]
skippable: [surveyed, quoted]
mails: { open: submitter, won: submitter, lost: submitter }
max_fields: 4
collects:
- { name: name }
- { name: email, type: email }
- { name: phone, type: tel }
- { name: project_description, type: textarea }
- id: subcontractor-application
who: trade firms who want to be on the list of subcontractors
wants: to be approved as a subcontractor
lands_in: subcontractors
handled_by: project_managers
stages: [open, checking, papers_requested, approved, declined]
done_when: [approved, declined]
success: [approved]
skippable: [papers_requested]
moves_by: { checking: submitter }
mails: { open: submitter, papers_requested: submitter, approved: submitter, declined: submitter }
max_fields: 5
collects:
- { name: company_name }
- { name: email, type: email }
- { name: phone, type: tel }
- { name: trade, type: select, options: [Electrical, Plumbing, Scaffolding, Other] }
- { name: registration_number }
- { name: insurance_info, type: textarea, optional: true }
- id: job-application
who: carpenters and apprentices asking if we are hiring
wants: a job with us
lands_in: job_applications
handled_by: project_managers
stages: [open, interviewing, hired, declined]
done_when: [hired, declined]
success: [hired]
skippable: [interviewing]
mails: { open: submitter, hired: submitter, declined: submitter }
max_fields: 4
collects:
- { name: name }
- { name: email, type: email }
- { name: phone, type: tel }
- { name: message, type: textarea }
- id: report-site-issue
who: crew, ours and the subcontractors' people, on site
wants: to report a hazard, near miss or wrong delivery
enters_as: crew
lands_in: site_issues
handled_by: safety_officer
stages: [open, action_decided, closed]
done_when: [closed]
success: [closed]
max_fields: 3
collects:
- { name: issue_type, type: select, options: [Hazard, Near miss, Wrong materials, Other] }
- { name: location }
- { name: details, type: textarea, optional: true }
```
Reply with the YAML only, no prose, no code fences.
@@ -0,0 +1,112 @@
```yaml
business: Nordvik Bygg
language: en
contact: { name: Ingrid Nordvik, email: ingrid@nordvikbygg.example }
about: >
We are twelve people. We survey old buildings, draw up rehabilitation
plans, handle the permit application, and do the building work with
our own carpenters plus subcontractors for electrical, plumbing and
scaffolding.
mail: { from: "Nordvik Bygg <post@nordvikbygg.example>", reply_to: ingrid@nordvikbygg.example }
does:
- id: assess-your-building
for: building-problem
text: We survey the building, draw up a rehabilitation plan, and handle the permit application.
- id: build-your-project
for: new-build-project
text: Already know what you want built? We do the drawings, the permit and the price, then build it with our own carpenters and trusted subcontractors.
groups:
- { id: project_managers, label: Project Managers }
- { id: safety_officer, label: Safety Officer }
- { id: crew, label: Crew }
needs:
- id: building-problem
who: housing boards and private owners with a problem with a building
wants: someone to come and look at the building and tell them what can be done
lands_in: enquiries
handled_by: project_managers
stages: [open, surveyed, quoted, won, lost]
done_when: [won, lost]
success: [won]
skippable: [surveyed, quoted]
mails: { open: submitter, won: submitter, lost: submitter }
max_fields: 4
collects:
- { name: name }
- { name: email, type: email }
- { name: phone, type: tel }
- { name: problem, type: textarea }
- { name: address, optional: true, private: true }
- id: new-build-project
who: owners who already know what they want built
wants: drawings, a permit and a price
lands_in: enquiries
handled_by: project_managers
stages: [open, surveyed, quoted, won, lost]
done_when: [won, lost]
success: [won]
skippable: [surveyed, quoted]
mails: { open: submitter, won: submitter, lost: submitter }
max_fields: 4
collects:
- { name: name }
- { name: email, type: email }
- { name: phone, type: tel }
- { name: project_description, type: textarea }
- id: subcontractor-application
who: trade firms who want to be on the list of subcontractors
wants: to be approved as a subcontractor
lands_in: subcontractors
handled_by: project_managers
stages: [open, checking, papers_requested, approved, declined]
done_when: [approved, declined]
success: [approved]
skippable: [papers_requested]
moves_by: { checking: [submitter] }
mails: { open: submitter, papers_requested: submitter, approved: submitter, declined: submitter }
max_fields: 5
collects:
- { name: company_name }
- { name: email, type: email }
- { name: phone, type: tel }
- { name: trade, type: select, options: [Electrical, Plumbing, Scaffolding, Other] }
- { name: registration_number }
- { name: insurance_info, type: textarea, optional: true }
- id: job-application
who: carpenters and apprentices asking if we are hiring
wants: a job with us
lands_in: job_applications
handled_by: project_managers
stages: [open, interviewing, hired, declined]
done_when: [hired, declined]
success: [hired]
skippable: [interviewing]
mails: { open: submitter, hired: submitter, declined: submitter }
max_fields: 4
collects:
- { name: name }
- { name: email, type: email }
- { name: phone, type: tel }
- { name: message, type: textarea }
- id: report-site-issue
who: crew, ours and the subcontractors' people, on site
wants: to report a hazard, near miss or wrong delivery
enters_as: crew
lands_in: site_issues
handled_by: safety_officer
stages: [open, action_decided, closed]
done_when: [closed]
success: [closed]
max_fields: 3
collects:
- { name: issue_type, type: select, options: [Hazard, Near miss, Wrong materials, Other] }
- { name: location }
- { name: details, type: textarea, optional: true }
```
@@ -0,0 +1,675 @@
{
"needs": [
{
"id": "building-problem",
"who": "housing boards and private owners with a problem with a building",
"wants": "someone to come and look at the building and tell them what can be done",
"enters_as": null,
"lands_in": "enquiries",
"handled_by": "project_managers",
"done_when": [
"won",
"lost"
],
"success": [
"won"
],
"steps": [
{
"page": "/",
"question": "What do you need done to a building?",
"context": "Building work you can trust from start to finish: Twelve of us, surveying buildings, drawing up plans, getting the permits sorted, and doing the work ourselves alongside trusted subcontractors. Know what's wrong before you spend a krone: We come look at the building, tell you honestly what's wrong and what it'll take to fix. See your project through from drawing to done: We draw up the plans, apply for the permit, and build it with our own carpenters and trusted subcontractors.",
"options": [
{
"name": "Find out what's wrong with your building",
"description": "Tell us what's wrong with the building and we'll come take a look."
},
{
"name": "Get your project drawn, permitted and priced",
"description": "Tell us what you want built and we'll draw it up, handle the permit, and give you a price."
},
{
"name": "Get work as one of our subcontractors",
"description": "Electrical, plumbing or scaffolding firms, get on our list and we'll bring you in on jobs."
},
{
"name": "Work with our carpenters",
"description": "Carpenters and apprentices, tell us a bit about yourself and we'll see where you'd fit."
}
],
"expect": [
"Find out what's wrong with your building"
],
"alternative": "Find out what's wrong with your building",
"fields": [
{
"name": "name",
"label": "Name",
"type": "text",
"optional": false,
"options": []
},
{
"name": "email",
"label": "Email",
"type": "email",
"optional": false,
"options": []
},
{
"name": "phone",
"label": "Phone",
"type": "tel",
"optional": false,
"options": []
},
{
"name": "problem",
"label": "What's the problem?",
"type": "textarea",
"optional": false,
"options": []
},
{
"name": "address",
"label": "Address",
"type": "text",
"optional": true,
"options": []
}
]
}
]
},
{
"id": "new-build-project",
"who": "owners who already know what they want built",
"wants": "drawings, a permit and a price",
"enters_as": null,
"lands_in": "enquiries",
"handled_by": "project_managers",
"done_when": [
"won",
"lost"
],
"success": [
"won"
],
"steps": [
{
"page": "/",
"question": "What do you need done to a building?",
"context": "Building work you can trust from start to finish: Twelve of us, surveying buildings, drawing up plans, getting the permits sorted, and doing the work ourselves alongside trusted subcontractors. Know what's wrong before you spend a krone: We come look at the building, tell you honestly what's wrong and what it'll take to fix. See your project through from drawing to done: We draw up the plans, apply for the permit, and build it with our own carpenters and trusted subcontractors.",
"options": [
{
"name": "Find out what's wrong with your building",
"description": "Tell us what's wrong with the building and we'll come take a look."
},
{
"name": "Get your project drawn, permitted and priced",
"description": "Tell us what you want built and we'll draw it up, handle the permit, and give you a price."
},
{
"name": "Get work as one of our subcontractors",
"description": "Electrical, plumbing or scaffolding firms, get on our list and we'll bring you in on jobs."
},
{
"name": "Work with our carpenters",
"description": "Carpenters and apprentices, tell us a bit about yourself and we'll see where you'd fit."
}
],
"expect": [
"Get your project drawn, permitted and priced"
],
"alternative": "Get your project drawn, permitted and priced",
"fields": [
{
"name": "name",
"label": "Name",
"type": "text",
"optional": false,
"options": []
},
{
"name": "email",
"label": "Email",
"type": "email",
"optional": false,
"options": []
},
{
"name": "phone",
"label": "Phone",
"type": "tel",
"optional": false,
"options": []
},
{
"name": "project_description",
"label": "What do you want to build?",
"type": "textarea",
"optional": false,
"options": []
}
]
}
]
},
{
"id": "subcontractor-application",
"who": "trade firms who want to be on the list of subcontractors",
"wants": "to be approved as a subcontractor",
"enters_as": null,
"lands_in": "subcontractors",
"handled_by": "project_managers",
"done_when": [
"approved",
"declined"
],
"success": [
"approved"
],
"steps": [
{
"page": "/",
"question": "What do you need done to a building?",
"context": "Building work you can trust from start to finish: Twelve of us, surveying buildings, drawing up plans, getting the permits sorted, and doing the work ourselves alongside trusted subcontractors. Know what's wrong before you spend a krone: We come look at the building, tell you honestly what's wrong and what it'll take to fix. See your project through from drawing to done: We draw up the plans, apply for the permit, and build it with our own carpenters and trusted subcontractors.",
"options": [
{
"name": "Find out what's wrong with your building",
"description": "Tell us what's wrong with the building and we'll come take a look."
},
{
"name": "Get your project drawn, permitted and priced",
"description": "Tell us what you want built and we'll draw it up, handle the permit, and give you a price."
},
{
"name": "Get work as one of our subcontractors",
"description": "Electrical, plumbing or scaffolding firms, get on our list and we'll bring you in on jobs."
},
{
"name": "Work with our carpenters",
"description": "Carpenters and apprentices, tell us a bit about yourself and we'll see where you'd fit."
}
],
"expect": [
"Get work as one of our subcontractors"
],
"alternative": "Get work as one of our subcontractors",
"fields": [
{
"name": "company_name",
"label": "Company name",
"type": "text",
"optional": false,
"options": []
},
{
"name": "email",
"label": "Email",
"type": "email",
"optional": false,
"options": []
},
{
"name": "phone",
"label": "Phone",
"type": "tel",
"optional": false,
"options": []
},
{
"name": "trade",
"label": "Trade",
"type": "select",
"optional": false,
"options": [
"Electrical",
"Plumbing",
"Scaffolding",
"Other"
]
},
{
"name": "registration_number",
"label": "Registration number",
"type": "text",
"optional": false,
"options": []
},
{
"name": "insurance_info",
"label": "Insurance details",
"type": "textarea",
"optional": true,
"options": []
}
]
}
]
},
{
"id": "job-application",
"who": "carpenters and apprentices asking if we are hiring",
"wants": "a job with us",
"enters_as": null,
"lands_in": "job_applications",
"handled_by": "project_managers",
"done_when": [
"hired",
"declined"
],
"success": [
"hired"
],
"steps": [
{
"page": "/",
"question": "What do you need done to a building?",
"context": "Building work you can trust from start to finish: Twelve of us, surveying buildings, drawing up plans, getting the permits sorted, and doing the work ourselves alongside trusted subcontractors. Know what's wrong before you spend a krone: We come look at the building, tell you honestly what's wrong and what it'll take to fix. See your project through from drawing to done: We draw up the plans, apply for the permit, and build it with our own carpenters and trusted subcontractors.",
"options": [
{
"name": "Find out what's wrong with your building",
"description": "Tell us what's wrong with the building and we'll come take a look."
},
{
"name": "Get your project drawn, permitted and priced",
"description": "Tell us what you want built and we'll draw it up, handle the permit, and give you a price."
},
{
"name": "Get work as one of our subcontractors",
"description": "Electrical, plumbing or scaffolding firms, get on our list and we'll bring you in on jobs."
},
{
"name": "Work with our carpenters",
"description": "Carpenters and apprentices, tell us a bit about yourself and we'll see where you'd fit."
}
],
"expect": [
"Work with our carpenters"
],
"alternative": "Work with our carpenters",
"fields": [
{
"name": "name",
"label": "Name",
"type": "text",
"optional": false,
"options": []
},
{
"name": "email",
"label": "Email",
"type": "email",
"optional": false,
"options": []
},
{
"name": "phone",
"label": "Phone",
"type": "tel",
"optional": false,
"options": []
},
{
"name": "message",
"label": "Tell us about yourself",
"type": "textarea",
"optional": false,
"options": []
}
]
}
]
},
{
"id": "report-site-issue",
"who": "crew, ours and the subcontractors' people, on site",
"wants": "to report a hazard, near miss or wrong delivery",
"enters_as": "crew",
"lands_in": "site_issues",
"handled_by": "safety_officer",
"done_when": [
"closed"
],
"success": [
"closed"
],
"steps": [
{
"page": "/crew/report-site-issue",
"question": "Has something happened on site that needs attention?",
"context": "",
"options": [
{
"name": "Flag something wrong on site",
"description": "Tell the safety officer about a hazard, near miss or wrong delivery on site."
}
],
"expect": [
"Flag something wrong on site"
],
"alternative": "Flag something wrong on site",
"fields": [
{
"name": "issue_type",
"label": "Type of issue",
"type": "select",
"optional": false,
"options": [
"Hazard",
"Near miss",
"Wrong materials",
"Other"
]
},
{
"name": "location",
"label": "Where on site",
"type": "text",
"optional": false,
"options": []
},
{
"name": "details",
"label": "What happened",
"type": "textarea",
"optional": true,
"options": []
}
]
}
]
}
],
"buckets": {
"enquiries": {
"initial": "open",
"transitions": {
"open": [
"surveyed",
"quoted",
"won",
"lost"
],
"quoted": [
"won",
"lost"
],
"surveyed": [
"quoted",
"won",
"lost"
]
},
"words": {
"lost": "lost",
"open": "open",
"quoted": "quoted",
"surveyed": "surveyed",
"won": "won"
},
"desks": [
{
"page": "/project_managers",
"group": "project_managers",
"moves": [
{
"from": "open",
"to": "surveyed",
"label": "Mark as surveyed"
},
{
"from": "open",
"to": "quoted",
"label": "Send straight to quoted"
},
{
"from": "open",
"to": "won",
"label": "Mark as won"
},
{
"from": "open",
"to": "lost",
"label": "Mark as lost"
},
{
"from": "quoted",
"to": "won",
"label": "Mark as won"
},
{
"from": "quoted",
"to": "lost",
"label": "Mark as lost"
},
{
"from": "surveyed",
"to": "quoted",
"label": "Quote sent"
},
{
"from": "surveyed",
"to": "won",
"label": "Mark as won"
},
{
"from": "surveyed",
"to": "lost",
"label": "Mark as lost"
}
]
}
],
"self_moves": []
},
"subcontractors": {
"initial": "open",
"transitions": {
"checking": [
"papers_requested",
"approved",
"declined"
],
"open": [
"checking",
"declined"
],
"papers_requested": [
"approved",
"declined"
]
},
"words": {
"approved": "approved",
"checking": "checking",
"declined": "declined",
"open": "open",
"papers_requested": "papers requested"
},
"desks": [
{
"page": "/project_managers",
"group": "project_managers",
"moves": [
{
"from": "checking",
"to": "papers_requested",
"label": "Ask for their papers"
},
{
"from": "checking",
"to": "approved",
"label": "Approve them"
},
{
"from": "checking",
"to": "declined",
"label": "Turn them down"
},
{
"from": "open",
"to": "declined",
"label": "Turn them down"
},
{
"from": "papers_requested",
"to": "approved",
"label": "Approve them"
},
{
"from": "papers_requested",
"to": "declined",
"label": "Turn them down"
}
]
}
],
"self_moves": [
"checking"
]
},
"job_applications": {
"initial": "open",
"transitions": {
"interviewing": [
"hired",
"declined"
],
"open": [
"interviewing",
"hired",
"declined"
]
},
"words": {
"declined": "declined",
"hired": "hired",
"interviewing": "interviewing",
"open": "open"
},
"desks": [
{
"page": "/project_managers",
"group": "project_managers",
"moves": [
{
"from": "interviewing",
"to": "hired",
"label": "Offer the job"
},
{
"from": "interviewing",
"to": "declined",
"label": "Not taking them on"
},
{
"from": "open",
"to": "interviewing",
"label": "Call in for interview"
},
{
"from": "open",
"to": "hired",
"label": "Offer the job"
},
{
"from": "open",
"to": "declined",
"label": "Not taking them on"
}
]
}
],
"self_moves": []
},
"site_issues": {
"initial": "open",
"transitions": {
"action_decided": [
"closed"
],
"open": [
"action_decided"
]
},
"words": {
"action_decided": "action decided",
"closed": "closed",
"open": "open"
},
"desks": [
{
"page": "/safety_officer",
"group": "safety_officer",
"moves": [
{
"from": "action_decided",
"to": "closed",
"label": "Mark sorted"
},
{
"from": "open",
"to": "action_decided",
"label": "Log what we're doing"
}
]
}
],
"self_moves": []
}
},
"personas": [
{
"id": "housing-board-damp",
"need": "building-problem",
"text": "I chair the board of a housing cooperative from the seventies. Two ground-floor flats have damp in the walls and we don't know if it's drainage or the facade. We need someone to tell us."
},
{
"id": "buyer-before-purchase",
"need": "building-problem",
"text": "We are about to buy an old timber house. Before we sign we want a professional to go through it and tell us what is wrong with it."
},
{
"id": "attic-conversion",
"need": "new-build-project",
"text": "We want to turn our attic into two bedrooms and a bathroom. We need drawings and someone who handles the application to the municipality."
},
{
"id": "shop-to-flat",
"need": "new-build-project",
"text": "I own a former shop on the ground floor and want to make it a flat. I have no idea what is allowed. Can you sort that out and give me a price?"
},
{
"id": "electrician-firm",
"need": "subcontractor-application",
"text": "We are a four-person electrical firm, fully certified. We would like to get on your list so you call us when you need electricians."
},
{
"id": "scaffolder",
"need": "subcontractor-application",
"text": "I run a scaffolding company. How do I get to do jobs for you?"
},
{
"id": "carpenter-jobseeker",
"need": "job-application",
"text": "I'm a certified carpenter with eight years on rehabilitation jobs. Are you hiring?"
},
{
"id": "apprentice",
"need": "job-application",
"text": "I finish vocational school this spring and need an apprenticeship as a bricklayer."
},
{
"id": "loose-railing",
"need": "report-site-issue",
"text": "The railing on the third level of the scaffold on the north side is loose. Nobody got hurt but someone will."
},
{
"id": "wrong-membrane",
"need": "report-site-issue",
"text": "The bathroom membrane delivered today is not the one in the specification. We stopped work on it."
}
]
}
@@ -0,0 +1,153 @@
business: Nordvik Bygg
language: en
contact: { name: Ingrid Nordvik, email: ingrid@nordvikbygg.example }
about: >
We are twelve people. We survey old buildings, draw up rehabilitation
plans, handle the permit application, and do the building work with
our own carpenters plus subcontractors for electrical, plumbing and
scaffolding.
mail: { from: "Nordvik Bygg <post@nordvikbygg.example>", reply_to: ingrid@nordvikbygg.example }
does:
- id: assess-your-building
for: building-problem
text: We survey the building, draw up a rehabilitation plan, and handle the permit application.
- id: build-your-project
for: new-build-project
text: Already know what you want built? We do the drawings, the permit and the price, then build it with our own carpenters and trusted subcontractors.
groups:
- { id: project_managers, label: Project Managers }
- { id: safety_officer, label: Safety Officer }
- { id: crew, label: Crew }
needs:
- id: building-problem
who: housing boards and private owners with a problem with a building
wants: someone to come and look at the building and tell them what can be done
lands_in: enquiries
handled_by: project_managers
stages: [open, surveyed, quoted, won, lost]
done_when: [won, lost]
success: [won]
skippable: [surveyed, quoted]
mails: { open: submitter, won: submitter, lost: submitter }
max_fields: 4
collects:
- { name: name }
- { name: email, type: email }
- { name: phone, type: tel }
- { name: problem, type: textarea }
- { name: address, optional: true, private: true }
- id: new-build-project
who: owners who already know what they want built
wants: drawings, a permit and a price
lands_in: enquiries
handled_by: project_managers
stages: [open, surveyed, quoted, won, lost]
done_when: [won, lost]
success: [won]
skippable: [surveyed, quoted]
mails: { open: submitter, won: submitter, lost: submitter }
max_fields: 4
collects:
- { name: name }
- { name: email, type: email }
- { name: phone, type: tel }
- { name: project_description, type: textarea }
- id: subcontractor-application
who: trade firms who want to be on the list of subcontractors
wants: to be approved as a subcontractor
lands_in: subcontractors
handled_by: project_managers
stages: [open, checking, papers_requested, approved, declined]
done_when: [approved, declined]
success: [approved]
skippable: [papers_requested]
moves_by: { checking: [submitter] }
mails: { open: submitter, papers_requested: submitter, approved: submitter, declined: submitter }
max_fields: 5
collects:
- { name: company_name }
- { name: email, type: email }
- { name: phone, type: tel }
- { name: trade, type: select, options: [Electrical, Plumbing, Scaffolding, Other] }
- { name: registration_number }
- { name: insurance_info, type: textarea, optional: true }
- id: job-application
who: carpenters and apprentices asking if we are hiring
wants: a job with us
lands_in: job_applications
handled_by: project_managers
stages: [open, interviewing, hired, declined]
done_when: [hired, declined]
success: [hired]
skippable: [interviewing]
mails: { open: submitter, hired: submitter, declined: submitter }
max_fields: 4
collects:
- { name: name }
- { name: email, type: email }
- { name: phone, type: tel }
- { name: message, type: textarea }
- id: report-site-issue
who: crew, ours and the subcontractors' people, on site
wants: to report a hazard, near miss or wrong delivery
enters_as: crew
lands_in: site_issues
handled_by: safety_officer
stages: [open, action_decided, closed]
done_when: [closed]
success: [closed]
max_fields: 3
collects:
- { name: issue_type, type: select, options: [Hazard, Near miss, Wrong materials, Other] }
- { name: location }
- { name: details, type: textarea, optional: true }
# Held out from every model that writes words.
personas:
- id: housing-board-damp
need: building-problem
text: |
I chair the board of a housing cooperative from the seventies. Two ground-floor flats have damp in the walls and we don't know if it's drainage or the facade. We need someone to tell us.
- id: buyer-before-purchase
need: building-problem
text: |
We are about to buy an old timber house. Before we sign we want a professional to go through it and tell us what is wrong with it.
- id: attic-conversion
need: new-build-project
text: |
We want to turn our attic into two bedrooms and a bathroom. We need drawings and someone who handles the application to the municipality.
- id: shop-to-flat
need: new-build-project
text: |
I own a former shop on the ground floor and want to make it a flat. I have no idea what is allowed. Can you sort that out and give me a price?
- id: electrician-firm
need: subcontractor-application
text: |
We are a four-person electrical firm, fully certified. We would like to get on your list so you call us when you need electricians.
- id: scaffolder
need: subcontractor-application
text: |
I run a scaffolding company. How do I get to do jobs for you?
- id: carpenter-jobseeker
need: job-application
text: |
I'm a certified carpenter with eight years on rehabilitation jobs. Are you hiring?
- id: apprentice
need: job-application
text: |
I finish vocational school this spring and need an apprenticeship as a bricklayer.
- id: loose-railing
need: report-site-issue
text: |
The railing on the third level of the scaffold on the north side is loose. Nobody got hurt but someone will.
- id: wrong-membrane
need: report-site-issue
text: |
The bathroom membrane delivered today is not the one in the specification. We stopped work on it.
@@ -0,0 +1,192 @@
{
"building-problem": {
"pages": [
{
"control": {
"asked": 0,
"next": 0,
"where": 0,
"which": 0
},
"control_ok": true,
"followup_shown": true,
"lix": 27.38888888888889,
"options": 4,
"overloaded": false,
"page": "/",
"personas": 2,
"required_fields": 4,
"understanding": {
"asked": 2,
"next": 2,
"where": 2,
"which": 2
}
}
],
"steps": 1
},
"desk /project_managers enquiries": {
"mistaken": [],
"of": 9,
"right": 9
},
"desk /project_managers job_applications": {
"mistaken": [],
"of": 5,
"right": 5
},
"desk /project_managers subcontractors": {
"mistaken": [],
"of": 5,
"right": 5
},
"job-application": {
"pages": [
{
"control": {
"asked": 0,
"next": 0,
"where": 0,
"which": 0
},
"control_ok": true,
"followup_shown": true,
"lix": 26.51438053097345,
"options": 4,
"overloaded": false,
"page": "/",
"personas": 2,
"required_fields": 4,
"understanding": {
"asked": 2,
"next": 2,
"where": 2,
"which": 2
}
}
],
"steps": 1
},
"mail enquiries:lost": {
"lix": 15.298245614035087,
"words": 39
},
"mail enquiries:open": {
"lix": 31.666666666666668,
"words": 20
},
"mail enquiries:won": {
"lix": 16.523809523809526,
"words": 22
},
"mail job_applications:declined": {
"lix": 30.06060606060606,
"words": 23
},
"mail job_applications:hired": {
"lix": 21.285714285714285,
"words": 22
},
"mail job_applications:open": {
"lix": 16.424242424242426,
"words": 22
},
"mail subcontractors:approved": {
"lix": 11.878787878787879,
"words": 22
},
"mail subcontractors:declined": {
"lix": 15.0,
"words": 21
},
"mail subcontractors:open": {
"lix": 18.952380952380953,
"words": 25
},
"mail subcontractors:papers_requested": {
"lix": 24.5,
"words": 26
},
"new-build-project": {
"pages": [
{
"control": {
"asked": 0,
"next": 0,
"where": 0,
"which": 0
},
"control_ok": true,
"followup_shown": true,
"lix": 27.407894736842103,
"options": 4,
"overloaded": false,
"page": "/",
"personas": 2,
"required_fields": 4,
"understanding": {
"asked": 2,
"next": 2,
"where": 2,
"which": 2
}
}
],
"steps": 1
},
"report-site-issue": {
"pages": [
{
"control": {
"asked": 0,
"next": 0,
"where": 0,
"which": 0
},
"control_ok": true,
"followup_shown": true,
"lix": 30.43333333333333,
"options": 1,
"overloaded": false,
"page": "/crew/report-site-issue",
"personas": 2,
"required_fields": 2,
"understanding": {
"asked": 2,
"next": 2,
"where": 2,
"which": 2
}
}
],
"steps": 1
},
"subcontractor-application": {
"pages": [
{
"control": {
"asked": 0,
"next": 0,
"where": 0,
"which": 0
},
"control_ok": true,
"followup_shown": true,
"lix": 27.38888888888889,
"options": 4,
"overloaded": false,
"page": "/",
"personas": 2,
"required_fields": 5,
"understanding": {
"asked": 2,
"next": 2,
"where": 2,
"which": 2
}
}
],
"steps": 1
}
}
@@ -0,0 +1,127 @@
aggregates:
- bucket: enquiries
initial: open
states:
lost:
event: lost
mail:
body: Thanks for the chance to look at this - not one we're able to take on. If plans change or you want a second opinion down the line, get in touch.
subject: Not one we can take on this time
to: submitter
open:
event: received
mail:
body: We've had your enquiry land. A project manager will come back to you shortly about a visit.
subject: Got your enquiry
to: submitter
quoted:
event: quoted
surveyed:
event: surveyed
won:
event: won
mail:
body: Good news - we're going ahead with your job. Your project manager will be in touch about next steps.
subject: You're booked in
to: submitter
transitions:
open:
- surveyed
- quoted
- won
- lost
quoted:
- won
- lost
surveyed:
- quoted
- won
- lost
- bucket: job_applications
initial: open
states:
declined:
event: declined
mail:
body: Thanks for applying - we've filled the position for now. We'll keep your details on file if that changes.
subject: No opening right now
to: submitter
hired:
event: hired
mail:
body: Welcome aboard - we'd like to bring you on. We'll be in touch about start dates and paperwork.
subject: Welcome to the team
to: submitter
interviewing:
event: interviewing
open:
event: received
mail:
body: Thanks for reaching out about work with us. We'll look over what you've sent and get back to you.
subject: Got your application
to: submitter
transitions:
interviewing:
- hired
- declined
open:
- interviewing
- hired
- declined
- bucket: site_issues
initial: open
states:
action_decided:
event: action_decided
closed:
event: closed
open:
event: received
transitions:
action_decided:
- closed
open:
- action_decided
- bucket: subcontractors
initial: open
states:
approved:
event: approved
mail:
body: You're on our list of subcontractors now. We'll be in touch when a job comes up that fits.
subject: You're on our list
to: submitter
checking:
event: checking
declined:
event: declined
mail:
body: Thanks for applying - we won't be adding you to our list at this time.
subject: Not adding you to our list
to: submitter
open:
event: received
mail:
body: |-
We've had your application to become a subcontractor. We'll look it over and be in touch.
What you can do next: {site}/decide/subcontractor-application?chain={chain}&email={email}
subject: Got your application
to: submitter
papers_requested:
event: papers_requested
mail:
body: We need a few papers from you before we can approve you - see the link below for what's missing.
subject: We need some paperwork from you
to: submitter
transitions:
checking:
- papers_requested
- approved
- declined
open:
- checking
- declined
papers_requested:
- approved
- declined
@@ -0,0 +1,128 @@
You write the words for one page of a website for this business.
Business: Nordvik Bygg
About: We are twelve people. We survey old buildings, draw up rehabilitation plans, handle the permit application, and do the building work with our own carpenters plus subcontractors for electrical, plumbing and scaffolding.
Contact: Ingrid Nordvik <ingrid@nordvikbygg.example>
Language to write in: en
Everyone the site serves:
- building-problem: housing boards and private owners with a problem with a building - wants: someone to come and look at the building and tell them what can be done. Handled by project_managers. Stages: open -> surveyed -> quoted -> won -> lost.
- new-build-project: owners who already know what they want built - wants: drawings, a permit and a price. Handled by project_managers. Stages: open -> surveyed -> quoted -> won -> lost.
- subcontractor-application: trade firms who want to be on the list of subcontractors - wants: to be approved as a subcontractor. Handled by project_managers. Stages: open -> checking -> papers_requested -> approved -> declined.
- job-application: carpenters and apprentices asking if we are hiring - wants: a job with us. Handled by project_managers. Stages: open -> interviewing -> hired -> declined.
- report-site-issue: crew, ours and the subcontractors' people, on site - wants: to report a hazard, near miss or wrong delivery. Handled by safety_officer. Stages: open -> action_decided -> closed.
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.
{
"enquiries": {
"lost": {
"body": "<WRITE: a form label for the field `body`>",
"subject": "<WRITE: a form label for the field `subject`>"
},
"open": {
"body": "<WRITE: a form label for the field `body`>",
"subject": "<WRITE: a form label for the field `subject`>"
},
"won": {
"body": "<WRITE: a form label for the field `body`>",
"subject": "<WRITE: a form label for the field `subject`>"
}
},
"job_applications": {
"declined": {
"body": "<WRITE: a form label for the field `body`>",
"subject": "<WRITE: a form label for the field `subject`>"
},
"hired": {
"body": "<WRITE: a form label for the field `body`>",
"subject": "<WRITE: a form label for the field `subject`>"
},
"open": {
"body": "<WRITE: a form label for the field `body`>",
"subject": "<WRITE: a form label for the field `subject`>"
}
},
"subcontractors": {
"approved": {
"body": "<WRITE: a form label for the field `body`>",
"subject": "<WRITE: a form label for the field `subject`>"
},
"declined": {
"body": "<WRITE: a form label for the field `body`>",
"subject": "<WRITE: a form label for the field `subject`>"
},
"open": {
"body": "<WRITE: a form label for the field `body`>",
"subject": "<WRITE: a form label for the field `subject`>"
},
"papers_requested": {
"body": "<WRITE: a form label for the field `body`>",
"subject": "<WRITE: a form label for the field `subject`>"
}
}
}
Reply with the JSON object only.
=== REPLY ===
{
"enquiries": {
"lost": {
"body": "Thanks for the chance to look at this - not one we're able to take on. If plans change or you want a second opinion down the line, get in touch.",
"subject": "Not one we can take on this time"
},
"open": {
"body": "We've had your enquiry land. A project manager will come back to you shortly about a visit.",
"subject": "Got your enquiry"
},
"won": {
"body": "Good news - we're going ahead with your job. Your project manager will be in touch about next steps.",
"subject": "You're booked in"
}
},
"job_applications": {
"declined": {
"body": "Thanks for applying - we've filled the position for now. We'll keep your details on file if that changes.",
"subject": "No opening right now"
},
"hired": {
"body": "Welcome aboard - we'd like to bring you on. We'll be in touch about start dates and paperwork.",
"subject": "Welcome to the team"
},
"open": {
"body": "Thanks for reaching out about work with us. We'll look over what you've sent and get back to you.",
"subject": "Got your application"
}
},
"subcontractors": {
"approved": {
"body": "You're on our list of subcontractors now. We'll be in touch when a job comes up that fits.",
"subject": "You're on our list"
},
"declined": {
"body": "Thanks for applying - we won't be adding you to our list at this time.",
"subject": "Not adding you to our list"
},
"open": {
"body": "We've had your application to become a subcontractor. We'll look it over and be in touch.",
"subject": "Got your application"
},
"papers_requested": {
"body": "We need a few papers from you before we can approve you - see the link below for what's missing.",
"subject": "We need some paperwork from you"
}
}
}
@@ -0,0 +1,94 @@
You write the words for one page of a website for this business.
Business: Nordvik Bygg
About: We are twelve people. We survey old buildings, draw up rehabilitation plans, handle the permit application, and do the building work with our own carpenters plus subcontractors for electrical, plumbing and scaffolding.
Contact: Ingrid Nordvik <ingrid@nordvikbygg.example>
Language to write in: en
Everyone the site serves:
- building-problem: housing boards and private owners with a problem with a building - wants: someone to come and look at the building and tell them what can be done. Handled by project_managers. Stages: open -> surveyed -> quoted -> won -> lost.
- new-build-project: owners who already know what they want built - wants: drawings, a permit and a price. Handled by project_managers. Stages: open -> surveyed -> quoted -> won -> lost.
- subcontractor-application: trade firms who want to be on the list of subcontractors - wants: to be approved as a subcontractor. Handled by project_managers. Stages: open -> checking -> papers_requested -> approved -> declined.
- job-application: carpenters and apprentices asking if we are hiring - wants: a job with us. Handled by project_managers. Stages: open -> interviewing -> hired -> declined.
- report-site-issue: crew, ours and the subcontractors' people, on site - wants: to report a hazard, near miss or wrong delivery. Handled by safety_officer. Stages: open -> action_decided -> closed.
The page: questions/crew/index.yaml
It is 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.
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.
{
"buckets": {},
"confirm": "<WRITE: the one button under every list on this desk, confirming a move the person has chosen; one word, such as Confirm>",
"description": "<WRITE: one or two short sentences>",
"invited": {
"description": "<WRITE: one or two short sentences>",
"done": "<WRITE: a form label for the field `done`>",
"heading": "<WRITE: a heading, at most eight words, saying what the reader gets>"
},
"invites": {
"crew": {
"button": "<WRITE: a button, one to three words, saying what pressing it does>",
"description": "<WRITE: one or two short sentences>",
"form_title": "<WRITE: a title for the form, two to four words>",
"heading": "<WRITE: a heading, at most eight words, saying what the reader gets>",
"labels": {
"email": "<WRITE: a form label for the field `email`>",
"name": "<WRITE: a form label for the field `name`>"
}
}
},
"links": {
"report-site-issue": {
"button": "<WRITE: a button, one to three words, saying what pressing it does>",
"description": "<WRITE: one or two short sentences>",
"heading": "<WRITE: a heading, at most eight words, saying what the reader gets>"
}
},
"question": "<WRITE: the one question this page asks its reader>"
}
Reply with the JSON object only.
=== REPLY ===
```json
{
"buckets": {},
"confirm": "Confirm",
"description": "Everything on your desk right now: enquiries, quotes, subcontractors and job applications, all in one place.",
"invited": {
"description": "People you've invited who haven't taken up their invite yet, and those who have.",
"done": "Signed up",
"heading": "Who's joined so far"
},
"invites": {
"crew": {
"button": "Send invite",
"description": "Give a colleague their own login so they can work cases with you.",
"form_title": "Invite a colleague",
"heading": "Bring a colleague onto the desk",
"labels": {
"email": "Their email",
"name": "Their name"
}
}
},
"links": {
"report-site-issue": {
"button": "Open site issues",
"description": "See what's been reported from site and decide what to do about it.",
"heading": "Handle hazards and near misses reported from site"
}
},
"question": "What needs your attention right now?"
}
```
@@ -0,0 +1,48 @@
You write the words for one page of a website for this business.
Business: Nordvik Bygg
About: We are twelve people. We survey old buildings, draw up rehabilitation plans, handle the permit application, and do the building work with our own carpenters plus subcontractors for electrical, plumbing and scaffolding.
Contact: Ingrid Nordvik <ingrid@nordvikbygg.example>
Language to write in: en
Everyone the site serves:
- building-problem: housing boards and private owners with a problem with a building - wants: someone to come and look at the building and tell them what can be done. Handled by project_managers. Stages: open -> surveyed -> quoted -> won -> lost.
- new-build-project: owners who already know what they want built - wants: drawings, a permit and a price. Handled by project_managers. Stages: open -> surveyed -> quoted -> won -> lost.
- subcontractor-application: trade firms who want to be on the list of subcontractors - wants: to be approved as a subcontractor. Handled by project_managers. Stages: open -> checking -> papers_requested -> approved -> declined.
- job-application: carpenters and apprentices asking if we are hiring - wants: a job with us. Handled by project_managers. Stages: open -> interviewing -> hired -> declined.
- report-site-issue: crew, ours and the subcontractors' people, on site - wants: to report a hazard, near miss or wrong delivery. Handled by safety_officer. Stages: open -> action_decided -> closed.
The page: questions/crew/report-site-issue-sent.yaml
It is a page shown right after someone has sent a form. Say who reads it, when they answer, and what happens next.
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.
{
"back": "<WRITE: a button, one to three words, saying what pressing it does>",
"description": "<WRITE: one or two short sentences>",
"next_description": "<WRITE: one or two short sentences>",
"next_heading": "<WRITE: a heading, at most eight words, saying what the reader gets>",
"question": "<WRITE: the one question this page asks its reader>"
}
Reply with the JSON object only.
=== REPLY ===
```json
{
"back": "Back to site",
"description": "Thanks for flagging this. It's gone straight to our safety officer.",
"next_description": "Our safety officer looks at every report the same day and decides what needs doing.",
"next_heading": "Safety officer reviews it today",
"question": "What happens now that I've sent this in?"
}
```
@@ -0,0 +1,88 @@
You write the words for one page of a website for this business.
Business: Nordvik Bygg
About: We are twelve people. We survey old buildings, draw up rehabilitation plans, handle the permit application, and do the building work with our own carpenters plus subcontractors for electrical, plumbing and scaffolding.
Contact: Ingrid Nordvik <ingrid@nordvikbygg.example>
Language to write in: en
Everyone the site serves:
- building-problem: housing boards and private owners with a problem with a building - wants: someone to come and look at the building and tell them what can be done. Handled by project_managers. Stages: open -> surveyed -> quoted -> won -> lost.
- new-build-project: owners who already know what they want built - wants: drawings, a permit and a price. Handled by project_managers. Stages: open -> surveyed -> quoted -> won -> lost.
- subcontractor-application: trade firms who want to be on the list of subcontractors - wants: to be approved as a subcontractor. Handled by project_managers. Stages: open -> checking -> papers_requested -> approved -> declined.
- job-application: carpenters and apprentices asking if we are hiring - wants: a job with us. Handled by project_managers. Stages: open -> interviewing -> hired -> declined.
- report-site-issue: crew, ours and the subcontractors' people, on site - wants: to report a hazard, near miss or wrong delivery. Handled by safety_officer. Stages: open -> action_decided -> closed.
The page: questions/crew/report-site-issue.yaml
It is a page only one role inside the business can open, holding one form.
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.
{
"button": "<WRITE: a button, one to three words, saying what pressing it does>",
"description": "<WRITE: one or two short sentences>",
"encouragement": "<WRITE: one reassuring sentence: what happens with what they send, or how little it takes; or empty>",
"form_description": "<WRITE: one short line of guidance above the form, or empty>",
"form_title": "<WRITE: a title for the form, two to four words>",
"heading": "<WRITE: a heading, at most eight words, saying what the reader gets>",
"hints": {
"details": "<WRITE: an example answer for the field `details`, as a placeholder; or empty>",
"issue_type": "<WRITE: an example answer for the field `issue_type`, as a placeholder; or empty>",
"location": "<WRITE: an example answer for the field `location`, as a placeholder; or empty>"
},
"icon": "<WRITE: an icon name from the list at the end, the one that fits this best; or empty>",
"labels": {
"details": "<WRITE: a form label for the field `details`>",
"issue_type": "<WRITE: a form label for the field `issue_type`>",
"location": "<WRITE: a form label for the field `location`>"
},
"page_description": "<WRITE: one or two short sentences>",
"question": "<WRITE: the one question this page asks its reader>",
"road_description": "<WRITE: one or two sentences walking the reader through the stages this goes through, in order (the stages are listed above), in their words>",
"road_icon": "<WRITE: an icon name from the list at the end that fits this section; or empty>",
"road_title": "<WRITE: a short title, two to four words, for the line that says what happens after sending>",
"who_description": "<WRITE: one sentence: who reads what they send, and how to reach them meanwhile>",
"who_icon": "<WRITE: an icon name from the list at the end that fits this section; or empty>",
"who_title": "<WRITE: a short title, two to four words, for the line that says who reads it>"
}
Reply with the JSON object only.
=== REPLY ===
```json
{
"button": "Report issue",
"description": "Tell the safety officer about a hazard, near miss or wrong delivery on site.",
"encouragement": "Takes a minute — even a rough description helps.",
"form_description": "Say where and what's happened, as plainly as you can.",
"form_title": "Report the issue",
"heading": "Flag something wrong on site",
"hints": {
"details": "Scaffold missing a guardrail on the second lift, someone could fall.",
"issue_type": "Near miss",
"location": "Storgata 14, east scaffold"
},
"icon": "alert-triangle",
"labels": {
"details": "What happened",
"issue_type": "Type of issue",
"location": "Where on site"
},
"page_description": "See a hazard, a near miss or the wrong delivery turn up? Let us know before it becomes a problem.",
"question": "Has something happened on site that needs attention?",
"road_description": "Your report goes to the safety officer, who decides what to do about it, then closes it once it's sorted.",
"road_icon": "clipboard-list",
"road_title": "What happens next",
"who_description": "The safety officer reads this directly — if it's urgent, call Ingrid Nordvik on ingrid@nordvikbygg.example in the meantime.",
"who_icon": "shield",
"who_title": "Who sees this"
}
```
@@ -0,0 +1,56 @@
You write the words for one page of a website for this business.
Business: Nordvik Bygg
About: We are twelve people. We survey old buildings, draw up rehabilitation plans, handle the permit application, and do the building work with our own carpenters plus subcontractors for electrical, plumbing and scaffolding.
Contact: Ingrid Nordvik <ingrid@nordvikbygg.example>
Language to write in: en
Everyone the site serves:
- building-problem: housing boards and private owners with a problem with a building - wants: someone to come and look at the building and tell them what can be done. Handled by project_managers. Stages: open -> surveyed -> quoted -> won -> lost.
- new-build-project: owners who already know what they want built - wants: drawings, a permit and a price. Handled by project_managers. Stages: open -> surveyed -> quoted -> won -> lost.
- subcontractor-application: trade firms who want to be on the list of subcontractors - wants: to be approved as a subcontractor. Handled by project_managers. Stages: open -> checking -> papers_requested -> approved -> declined.
- job-application: carpenters and apprentices asking if we are hiring - wants: a job with us. Handled by project_managers. Stages: open -> interviewing -> hired -> declined.
- report-site-issue: crew, ours and the subcontractors' people, on site - wants: to report a hazard, near miss or wrong delivery. Handled by safety_officer. Stages: open -> action_decided -> closed.
The page: questions/decide/subcontractor-application.yaml
It is a page the person who sent a form reaches later, through a private link, to make a decision that is theirs: under `moves`, each entry is one thing they can decide.
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.
{
"description": "<WRITE: one or two short sentences>",
"moves": {
"checking": {
"button": "<WRITE: a button, one to three words, saying what pressing it does>",
"description": "<WRITE: one or two short sentences>",
"heading": "<WRITE: a heading, at most eight words, saying what the reader gets>"
}
},
"question": "<WRITE: the one question this page asks its reader>"
}
Reply with the JSON object only.
=== REPLY ===
```json
{
"description": "We've had a look and want to move forward. Let us know if you're ready to send over your paperwork.",
"moves": {
"checking": {
"button": "Continue review",
"description": "We're still checking you over and will be in touch shortly about next steps.",
"heading": "Keep your application under review"
}
},
"question": "Are you ready to move this application forward?"
}
```
@@ -0,0 +1,318 @@
You write the words for one page of a website for this business.
Business: Nordvik Bygg
About: We are twelve people. We survey old buildings, draw up rehabilitation plans, handle the permit application, and do the building work with our own carpenters plus subcontractors for electrical, plumbing and scaffolding.
Contact: Ingrid Nordvik <ingrid@nordvikbygg.example>
Language to write in: en
Everyone the site serves:
- building-problem: housing boards and private owners with a problem with a building - wants: someone to come and look at the building and tell them what can be done. Handled by project_managers. Stages: open -> surveyed -> quoted -> won -> lost.
- new-build-project: owners who already know what they want built - wants: drawings, a permit and a price. Handled by project_managers. Stages: open -> surveyed -> quoted -> won -> lost.
- subcontractor-application: trade firms who want to be on the list of subcontractors - wants: to be approved as a subcontractor. Handled by project_managers. Stages: open -> checking -> papers_requested -> approved -> declined.
- job-application: carpenters and apprentices asking if we are hiring - wants: a job with us. Handled by project_managers. Stages: open -> interviewing -> hired -> declined.
- report-site-issue: crew, ours and the subcontractors' people, on site - wants: to report a hazard, near miss or wrong delivery. Handled by safety_officer. Stages: open -> action_decided -> closed.
The page: questions/index.yaml
It is the front door: the first page any visitor sees. `question` is the page's one question. It opens modestly: `opener` is the first card, what the business does in several features and no button at all - a stranger reads it and decides whether to trust the place, so say what is true and useful, not what to press. `shopping` is the second card, things a visitor may look at before asking for anything. Only then come the offers: under `doors`, each entry is one alternative that leads to a page of its own for one kind of visitor, and its heading must let that visitor recognise themselves from the heading alone; under `needs`, each entry is one alternative with a form behind 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.
{
"description": "<WRITE: one or two short sentences>",
"doors": {},
"needs": {
"building-problem": {
"button": "<WRITE: a button, one to three words, saying what pressing it does>",
"description": "<WRITE: one or two short sentences>",
"encouragement": "<WRITE: one reassuring sentence: what happens with what they send, or how little it takes; or empty>",
"form_description": "<WRITE: one short line of guidance above the form, or empty>",
"form_title": "<WRITE: a title for the form, two to four words>",
"heading": "<WRITE: a heading, at most eight words, saying what the reader gets>",
"hints": {
"address": "<WRITE: an example answer for the field `address`, as a placeholder; or empty>",
"email": "<WRITE: an example answer for the field `email`, as a placeholder; or empty>",
"name": "<WRITE: an example answer for the field `name`, as a placeholder; or empty>",
"phone": "<WRITE: an example answer for the field `phone`, as a placeholder; or empty>",
"problem": "<WRITE: an example answer for the field `problem`, as a placeholder; or empty>"
},
"icon": "<WRITE: an icon name from the list at the end, the one that fits this best; or empty>",
"labels": {
"address": "<WRITE: a form label for the field `address`>",
"email": "<WRITE: a form label for the field `email`>",
"name": "<WRITE: a form label for the field `name`>",
"phone": "<WRITE: a form label for the field `phone`>",
"problem": "<WRITE: a form label for the field `problem`>"
},
"road_description": "<WRITE: one or two sentences walking the reader through the stages this goes through, in order (the stages are listed above), in their words>",
"road_icon": "<WRITE: an icon name from the list at the end that fits this section; or empty>",
"road_title": "<WRITE: a short title, two to four words, for the line that says what happens after sending>",
"who_description": "<WRITE: one sentence: who reads what they send, and how to reach them meanwhile>",
"who_icon": "<WRITE: an icon name from the list at the end that fits this section; or empty>",
"who_title": "<WRITE: a short title, two to four words, for the line that says who reads it>"
},
"job-application": {
"button": "<WRITE: a button, one to three words, saying what pressing it does>",
"description": "<WRITE: one or two short sentences>",
"encouragement": "<WRITE: one reassuring sentence: what happens with what they send, or how little it takes; or empty>",
"form_description": "<WRITE: one short line of guidance above the form, or empty>",
"form_title": "<WRITE: a title for the form, two to four words>",
"heading": "<WRITE: a heading, at most eight words, saying what the reader gets>",
"hints": {
"email": "<WRITE: an example answer for the field `email`, as a placeholder; or empty>",
"message": "<WRITE: an example answer for the field `message`, as a placeholder; or empty>",
"name": "<WRITE: an example answer for the field `name`, as a placeholder; or empty>",
"phone": "<WRITE: an example answer for the field `phone`, as a placeholder; or empty>"
},
"icon": "<WRITE: an icon name from the list at the end, the one that fits this best; or empty>",
"labels": {
"email": "<WRITE: a form label for the field `email`>",
"message": "<WRITE: a form label for the field `message`>",
"name": "<WRITE: a form label for the field `name`>",
"phone": "<WRITE: a form label for the field `phone`>"
},
"road_description": "<WRITE: one or two sentences walking the reader through the stages this goes through, in order (the stages are listed above), in their words>",
"road_icon": "<WRITE: an icon name from the list at the end that fits this section; or empty>",
"road_title": "<WRITE: a short title, two to four words, for the line that says what happens after sending>",
"who_description": "<WRITE: one sentence: who reads what they send, and how to reach them meanwhile>",
"who_icon": "<WRITE: an icon name from the list at the end that fits this section; or empty>",
"who_title": "<WRITE: a short title, two to four words, for the line that says who reads it>"
},
"new-build-project": {
"button": "<WRITE: a button, one to three words, saying what pressing it does>",
"description": "<WRITE: one or two short sentences>",
"encouragement": "<WRITE: one reassuring sentence: what happens with what they send, or how little it takes; or empty>",
"form_description": "<WRITE: one short line of guidance above the form, or empty>",
"form_title": "<WRITE: a title for the form, two to four words>",
"heading": "<WRITE: a heading, at most eight words, saying what the reader gets>",
"hints": {
"email": "<WRITE: an example answer for the field `email`, as a placeholder; or empty>",
"name": "<WRITE: an example answer for the field `name`, as a placeholder; or empty>",
"phone": "<WRITE: an example answer for the field `phone`, as a placeholder; or empty>",
"project_description": "<WRITE: an example answer for the field `project_description`, as a placeholder; or empty>"
},
"icon": "<WRITE: an icon name from the list at the end, the one that fits this best; or empty>",
"labels": {
"email": "<WRITE: a form label for the field `email`>",
"name": "<WRITE: a form label for the field `name`>",
"phone": "<WRITE: a form label for the field `phone`>",
"project_description": "<WRITE: a form label for the field `project_description`>"
},
"road_description": "<WRITE: one or two sentences walking the reader through the stages this goes through, in order (the stages are listed above), in their words>",
"road_icon": "<WRITE: an icon name from the list at the end that fits this section; or empty>",
"road_title": "<WRITE: a short title, two to four words, for the line that says what happens after sending>",
"who_description": "<WRITE: one sentence: who reads what they send, and how to reach them meanwhile>",
"who_icon": "<WRITE: an icon name from the list at the end that fits this section; or empty>",
"who_title": "<WRITE: a short title, two to four words, for the line that says who reads it>"
},
"subcontractor-application": {
"button": "<WRITE: a button, one to three words, saying what pressing it does>",
"description": "<WRITE: one or two short sentences>",
"encouragement": "<WRITE: one reassuring sentence: what happens with what they send, or how little it takes; or empty>",
"form_description": "<WRITE: one short line of guidance above the form, or empty>",
"form_title": "<WRITE: a title for the form, two to four words>",
"heading": "<WRITE: a heading, at most eight words, saying what the reader gets>",
"hints": {
"company_name": "<WRITE: an example answer for the field `company_name`, as a placeholder; or empty>",
"email": "<WRITE: an example answer for the field `email`, as a placeholder; or empty>",
"insurance_info": "<WRITE: an example answer for the field `insurance_info`, as a placeholder; or empty>",
"phone": "<WRITE: an example answer for the field `phone`, as a placeholder; or empty>",
"registration_number": "<WRITE: an example answer for the field `registration_number`, as a placeholder; or empty>",
"trade": "<WRITE: an example answer for the field `trade`, as a placeholder; or empty>"
},
"icon": "<WRITE: an icon name from the list at the end, the one that fits this best; or empty>",
"labels": {
"company_name": "<WRITE: a form label for the field `company_name`>",
"email": "<WRITE: a form label for the field `email`>",
"insurance_info": "<WRITE: a form label for the field `insurance_info`>",
"phone": "<WRITE: a form label for the field `phone`>",
"registration_number": "<WRITE: a form label for the field `registration_number`>",
"trade": "<WRITE: a form label for the field `trade`>"
},
"road_description": "<WRITE: one or two sentences walking the reader through the stages this goes through, in order (the stages are listed above), in their words>",
"road_icon": "<WRITE: an icon name from the list at the end that fits this section; or empty>",
"road_title": "<WRITE: a short title, two to four words, for the line that says what happens after sending>",
"who_description": "<WRITE: one sentence: who reads what they send, and how to reach them meanwhile>",
"who_icon": "<WRITE: an icon name from the list at the end that fits this section; or empty>",
"who_title": "<WRITE: a short title, two to four words, for the line that says who reads it>"
}
},
"opener": {
"description": "<WRITE: one or two short sentences>",
"features": {
"assess-your-building": {
"description": "<WRITE: one or two short sentences>",
"heading": "<WRITE: a heading, at most eight words, saying what the reader gets>",
"icon": "<WRITE: an icon name from the list at the end, the one that fits this best; or empty>"
},
"build-your-project": {
"description": "<WRITE: one or two short sentences>",
"heading": "<WRITE: a heading, at most eight words, saying what the reader gets>",
"icon": "<WRITE: an icon name from the list at the end, the one that fits this best; or empty>"
}
},
"heading": "<WRITE: a heading, at most eight words, saying what the reader gets>"
},
"question": "<WRITE: the one question this page asks its reader>",
"shopping": {
"description": "<WRITE: one or two short sentences>",
"heading": "<WRITE: a heading, at most eight words, saying what the reader gets>",
"items": {}
}
}
Reply with the JSON object only.
=== REPLY ===
```json
{
"description": "We look after old buildings and new ones, from the first survey to the last coat of paint, with our own carpenters and trusted subcontractors for the rest.",
"doors": {},
"needs": {
"building-problem": {
"button": "Get a survey",
"description": "Tell us what's wrong with the building and we'll come take a look.",
"encouragement": "A project manager reads every message and gets back to you, usually within a couple of days.",
"form_description": "A few lines about what you're seeing is enough to start.",
"form_title": "Describe the building",
"heading": "Find out what's wrong with your building",
"hints": {
"address": "Storgata 14, 0155 Oslo",
"email": "kari@example.com",
"name": "Kari Hansen",
"phone": "912 34 567",
"problem": "Damp patches on the ceiling in two flats, worse after rain"
},
"icon": "search",
"labels": {
"address": "Address",
"email": "Email",
"name": "Name",
"phone": "Phone",
"problem": "What's the problem?"
},
"road_description": "We look at what you've sent and book a site visit to survey the building. From there we quote the work, and if you go ahead, the job is won and we get started.",
"road_icon": "route",
"road_title": "What happens next",
"who_description": "A project manager handles this from start to finish, and you can call or email us any time in between.",
"who_icon": "user-check",
"who_title": "Who you'll hear from"
},
"job-application": {
"button": "Send application",
"description": "Carpenters and apprentices, tell us a bit about yourself and we'll see where you'd fit.",
"encouragement": "No CV needed to get started, just tell us who you are.",
"form_description": "A short message about your experience is all we need to begin.",
"form_title": "Tell us about yourself",
"heading": "Work with our carpenters",
"hints": {
"email": "ola@example.com",
"message": "Trained carpenter, three years on renovation sites, looking for full-time work",
"name": "Ola Berg",
"phone": "901 23 456"
},
"icon": "hammer",
"labels": {
"email": "Email",
"message": "Tell us about yourself",
"name": "Name",
"phone": "Phone"
},
"road_description": "We read your message and if it looks like a fit, we call you in for a chat. If that goes well, we'll offer you a start.",
"road_icon": "route",
"road_title": "What happens next",
"who_description": "A project manager reads applications and can answer questions by phone or email while you wait.",
"who_icon": "user-check",
"who_title": "Who you'll hear from"
},
"new-build-project": {
"button": "Start my project",
"description": "Tell us what you want built and we'll draw it up, handle the permit, and give you a price.",
"encouragement": "A project manager looks at every project and comes back to you with next steps.",
"form_description": "Sketches or plans help, but a description is enough to start.",
"form_title": "Describe your project",
"heading": "Get your project drawn, permitted and priced",
"hints": {
"email": "per@example.com",
"name": "Per Olsen",
"phone": "923 45 678",
"project_description": "Extending the kitchen with a 20m2 addition and a new terrace"
},
"icon": "ruler",
"labels": {
"email": "Email",
"name": "Name",
"phone": "Phone",
"project_description": "What do you want to build?"
},
"road_description": "We survey the site, draw up plans and quote the job. Once you accept, we apply for the permit and get building.",
"road_icon": "route",
"road_title": "What happens next",
"who_description": "A project manager owns your project and is your point of contact by phone or email throughout.",
"who_icon": "user-check",
"who_title": "Who you'll hear from"
},
"subcontractor-application": {
"button": "Apply now",
"description": "Electrical, plumbing or scaffolding firms, get on our list and we'll bring you in on jobs.",
"encouragement": "Send what you have now, we'll ask for anything more we need.",
"form_description": "Have your registration number and insurance details to hand.",
"form_title": "Apply as a subcontractor",
"heading": "Get work as one of our subcontractors",
"hints": {
"company_name": "Berg Elektro AS",
"email": "post@bergelektro.example",
"insurance_info": "Liability insurance with If, policy 123456",
"phone": "934 56 789",
"registration_number": "918 234 567",
"trade": "Electrical"
},
"icon": "handshake",
"labels": {
"company_name": "Company name",
"email": "Email",
"insurance_info": "Insurance details",
"phone": "Phone",
"registration_number": "Registration number",
"trade": "Trade"
},
"road_description": "We check your papers, and may ask for more documents. Once everything's in order, you're approved and on our list.",
"road_icon": "route",
"road_title": "What happens next",
"who_description": "A project manager checks applications and can be reached by phone or email if you have questions.",
"who_icon": "user-check",
"who_title": "Who you'll hear from"
}
},
"opener": {
"description": "Twelve of us, surveying buildings, drawing up plans, getting the permits sorted, and doing the work ourselves alongside trusted subcontractors.",
"features": {
"assess-your-building": {
"description": "We come look at the building, tell you honestly what's wrong and what it'll take to fix.",
"heading": "Know what's wrong before you spend a krone",
"icon": "search"
},
"build-your-project": {
"description": "We draw up the plans, apply for the permit, and build it with our own carpenters and trusted subcontractors.",
"heading": "See your project through from drawing to done",
"icon": "ruler"
}
},
"heading": "Building work you can trust from start to finish"
},
"question": "What do you need done to a building?",
"shopping": {
"description": "Not ready to send anything yet? Have a look at how we work and what we've done before.",
"heading": "See how we work before you get in touch",
"items": {}
}
}
```
@@ -0,0 +1,160 @@
You write the words for one page of a website for this business.
Business: Nordvik Bygg
About: We are twelve people. We survey old buildings, draw up rehabilitation plans, handle the permit application, and do the building work with our own carpenters plus subcontractors for electrical, plumbing and scaffolding.
Contact: Ingrid Nordvik <ingrid@nordvikbygg.example>
Language to write in: en
Everyone the site serves:
- building-problem: housing boards and private owners with a problem with a building - wants: someone to come and look at the building and tell them what can be done. Handled by project_managers. Stages: open -> surveyed -> quoted -> won -> lost.
- new-build-project: owners who already know what they want built - wants: drawings, a permit and a price. Handled by project_managers. Stages: open -> surveyed -> quoted -> won -> lost.
- subcontractor-application: trade firms who want to be on the list of subcontractors - wants: to be approved as a subcontractor. Handled by project_managers. Stages: open -> checking -> papers_requested -> approved -> declined.
- job-application: carpenters and apprentices asking if we are hiring - wants: a job with us. Handled by project_managers. Stages: open -> interviewing -> hired -> declined.
- report-site-issue: crew, ours and the subcontractors' people, on site - wants: to report a hazard, near miss or wrong delivery. Handled by safety_officer. Stages: open -> action_decided -> closed.
The page: questions/project_managers/index.yaml
It is 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.
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.
{
"buckets": {
"enquiries": {
"description": "<WRITE: one or two short sentences>",
"heading": "<WRITE: a heading, at most eight words, saying what the reader gets>",
"moves": {
"open->lost": "<WRITE: a button, two to four words, for a case going open->lost>",
"open->quoted": "<WRITE: a button, two to four words, for a case going open->quoted>",
"open->surveyed": "<WRITE: a button, two to four words, for a case going open->surveyed>",
"open->won": "<WRITE: a button, two to four words, for a case going open->won>",
"quoted->lost": "<WRITE: a button, two to four words, for a case going quoted->lost>",
"quoted->won": "<WRITE: a button, two to four words, for a case going quoted->won>",
"surveyed->lost": "<WRITE: a button, two to four words, for a case going surveyed->lost>",
"surveyed->quoted": "<WRITE: a button, two to four words, for a case going surveyed->quoted>",
"surveyed->won": "<WRITE: a button, two to four words, for a case going surveyed->won>"
}
},
"job_applications": {
"description": "<WRITE: one or two short sentences>",
"heading": "<WRITE: a heading, at most eight words, saying what the reader gets>",
"moves": {
"interviewing->declined": "<WRITE: a button, two to four words, for a case going interviewing->declined>",
"interviewing->hired": "<WRITE: a button, two to four words, for a case going interviewing->hired>",
"open->declined": "<WRITE: a button, two to four words, for a case going open->declined>",
"open->hired": "<WRITE: a button, two to four words, for a case going open->hired>",
"open->interviewing": "<WRITE: a button, two to four words, for a case going open->interviewing>"
}
},
"subcontractors": {
"description": "<WRITE: one or two short sentences>",
"heading": "<WRITE: a heading, at most eight words, saying what the reader gets>",
"moves": {
"checking->approved": "<WRITE: a button, two to four words, for a case going checking->approved>",
"checking->declined": "<WRITE: a button, two to four words, for a case going checking->declined>",
"checking->papers_requested": "<WRITE: a button, two to four words, for a case going checking->papers_requested>",
"open->declined": "<WRITE: a button, two to four words, for a case going open->declined>",
"papers_requested->approved": "<WRITE: a button, two to four words, for a case going papers_requested->approved>",
"papers_requested->declined": "<WRITE: a button, two to four words, for a case going papers_requested->declined>"
}
}
},
"confirm": "<WRITE: the one button under every list on this desk, confirming a move the person has chosen; one word, such as Confirm>",
"description": "<WRITE: one or two short sentences>",
"invited": {
"description": "<WRITE: one or two short sentences>",
"done": "<WRITE: a form label for the field `done`>",
"heading": "<WRITE: a heading, at most eight words, saying what the reader gets>"
},
"invites": {
"project_managers": {
"button": "<WRITE: a button, one to three words, saying what pressing it does>",
"description": "<WRITE: one or two short sentences>",
"form_title": "<WRITE: a title for the form, two to four words>",
"heading": "<WRITE: a heading, at most eight words, saying what the reader gets>",
"labels": {
"email": "<WRITE: a form label for the field `email`>",
"name": "<WRITE: a form label for the field `name`>"
}
}
},
"links": {},
"question": "<WRITE: the one question this page asks its reader>"
}
Reply with the JSON object only.
=== REPLY ===
```json
{
"buckets": {
"enquiries": {
"description": "Every building problem and new-build request that has come in, from first contact to won or lost.",
"heading": "New enquiries and where they stand",
"moves": {
"open->lost": "Mark as lost",
"open->quoted": "Send straight to quoted",
"open->surveyed": "Mark as surveyed",
"open->won": "Mark as won",
"quoted->lost": "Mark as lost",
"quoted->won": "Mark as won",
"surveyed->lost": "Mark as lost",
"surveyed->quoted": "Quote sent",
"surveyed->won": "Mark as won"
}
},
"job_applications": {
"description": "Carpenters and apprentices who have asked if we're hiring.",
"heading": "People asking for work",
"moves": {
"interviewing->declined": "Not taking them on",
"interviewing->hired": "Offer the job",
"open->declined": "Not taking them on",
"open->hired": "Offer the job",
"open->interviewing": "Call in for interview"
}
},
"subcontractors": {
"description": "Trade firms asking to get on our subcontractor list, and where each one is in the vetting.",
"heading": "Firms wanting to be a subcontractor",
"moves": {
"checking->approved": "Approve them",
"checking->declined": "Turn them down",
"checking->papers_requested": "Ask for their papers",
"open->declined": "Turn them down",
"papers_requested->approved": "Approve them",
"papers_requested->declined": "Turn them down"
}
}
},
"confirm": "Confirm",
"description": "Your desk for enquiries, job applications and subcontractors waiting on you.",
"invited": {
"description": "Project managers you've invited who haven't signed in yet.",
"done": "Signed in",
"heading": "Invites waiting to be taken up"
},
"invites": {
"project_managers": {
"button": "Send invite",
"description": "Bring another project manager onto the desk, with their own login.",
"form_title": "Invite a project manager",
"heading": "Add another project manager",
"labels": {
"email": "Their email address",
"name": "Their name"
}
}
},
"links": {},
"question": "What's waiting on me today?"
}
```
@@ -0,0 +1,48 @@
You write the words for one page of a website for this business.
Business: Nordvik Bygg
About: We are twelve people. We survey old buildings, draw up rehabilitation plans, handle the permit application, and do the building work with our own carpenters plus subcontractors for electrical, plumbing and scaffolding.
Contact: Ingrid Nordvik <ingrid@nordvikbygg.example>
Language to write in: en
Everyone the site serves:
- building-problem: housing boards and private owners with a problem with a building - wants: someone to come and look at the building and tell them what can be done. Handled by project_managers. Stages: open -> surveyed -> quoted -> won -> lost.
- new-build-project: owners who already know what they want built - wants: drawings, a permit and a price. Handled by project_managers. Stages: open -> surveyed -> quoted -> won -> lost.
- subcontractor-application: trade firms who want to be on the list of subcontractors - wants: to be approved as a subcontractor. Handled by project_managers. Stages: open -> checking -> papers_requested -> approved -> declined.
- job-application: carpenters and apprentices asking if we are hiring - wants: a job with us. Handled by project_managers. Stages: open -> interviewing -> hired -> declined.
- report-site-issue: crew, ours and the subcontractors' people, on site - wants: to report a hazard, near miss or wrong delivery. Handled by safety_officer. Stages: open -> action_decided -> closed.
The page: questions/received/building-problem.yaml
It is a page shown right after someone has sent a form. Say who reads it, when they answer, and what happens next.
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.
{
"back": "<WRITE: a button, one to three words, saying what pressing it does>",
"description": "<WRITE: one or two short sentences>",
"next_description": "<WRITE: one or two short sentences>",
"next_heading": "<WRITE: a heading, at most eight words, saying what the reader gets>",
"question": "<WRITE: the one question this page asks its reader>"
}
Reply with the JSON object only.
=== REPLY ===
```json
{
"back": "Back to home",
"description": "Thanks for telling us about the building. We've logged your report and will take it from here.",
"next_description": "A project manager will go through what you've sent and get in touch to arrange a look at the building.",
"next_heading": "A project manager gets in touch",
"question": "What happens now that we've sent this in?"
}
```
@@ -0,0 +1,48 @@
You write the words for one page of a website for this business.
Business: Nordvik Bygg
About: We are twelve people. We survey old buildings, draw up rehabilitation plans, handle the permit application, and do the building work with our own carpenters plus subcontractors for electrical, plumbing and scaffolding.
Contact: Ingrid Nordvik <ingrid@nordvikbygg.example>
Language to write in: en
Everyone the site serves:
- building-problem: housing boards and private owners with a problem with a building - wants: someone to come and look at the building and tell them what can be done. Handled by project_managers. Stages: open -> surveyed -> quoted -> won -> lost.
- new-build-project: owners who already know what they want built - wants: drawings, a permit and a price. Handled by project_managers. Stages: open -> surveyed -> quoted -> won -> lost.
- subcontractor-application: trade firms who want to be on the list of subcontractors - wants: to be approved as a subcontractor. Handled by project_managers. Stages: open -> checking -> papers_requested -> approved -> declined.
- job-application: carpenters and apprentices asking if we are hiring - wants: a job with us. Handled by project_managers. Stages: open -> interviewing -> hired -> declined.
- report-site-issue: crew, ours and the subcontractors' people, on site - wants: to report a hazard, near miss or wrong delivery. Handled by safety_officer. Stages: open -> action_decided -> closed.
The page: questions/received/job-application.yaml
It is a page shown right after someone has sent a form. Say who reads it, when they answer, and what happens next.
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.
{
"back": "<WRITE: a button, one to three words, saying what pressing it does>",
"description": "<WRITE: one or two short sentences>",
"next_description": "<WRITE: one or two short sentences>",
"next_heading": "<WRITE: a heading, at most eight words, saying what the reader gets>",
"question": "<WRITE: the one question this page asks its reader>"
}
Reply with the JSON object only.
=== REPLY ===
```json
{
"back": "Back to home",
"description": "Thanks for reaching out. Ingrid or one of the project managers will read it and get back to you.",
"next_description": "We'll go through your application and be in touch, whether that's to set up a chat or let you know where things stand.",
"next_heading": "We'll be in touch about work with us",
"question": "What happens now that I've sent my application?"
}
```
@@ -0,0 +1,48 @@
You write the words for one page of a website for this business.
Business: Nordvik Bygg
About: We are twelve people. We survey old buildings, draw up rehabilitation plans, handle the permit application, and do the building work with our own carpenters plus subcontractors for electrical, plumbing and scaffolding.
Contact: Ingrid Nordvik <ingrid@nordvikbygg.example>
Language to write in: en
Everyone the site serves:
- building-problem: housing boards and private owners with a problem with a building - wants: someone to come and look at the building and tell them what can be done. Handled by project_managers. Stages: open -> surveyed -> quoted -> won -> lost.
- new-build-project: owners who already know what they want built - wants: drawings, a permit and a price. Handled by project_managers. Stages: open -> surveyed -> quoted -> won -> lost.
- subcontractor-application: trade firms who want to be on the list of subcontractors - wants: to be approved as a subcontractor. Handled by project_managers. Stages: open -> checking -> papers_requested -> approved -> declined.
- job-application: carpenters and apprentices asking if we are hiring - wants: a job with us. Handled by project_managers. Stages: open -> interviewing -> hired -> declined.
- report-site-issue: crew, ours and the subcontractors' people, on site - wants: to report a hazard, near miss or wrong delivery. Handled by safety_officer. Stages: open -> action_decided -> closed.
The page: questions/received/new-build-project.yaml
It is a page shown right after someone has sent a form. Say who reads it, when they answer, and what happens next.
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.
{
"back": "<WRITE: a button, one to three words, saying what pressing it does>",
"description": "<WRITE: one or two short sentences>",
"next_description": "<WRITE: one or two short sentences>",
"next_heading": "<WRITE: a heading, at most eight words, saying what the reader gets>",
"question": "<WRITE: the one question this page asks its reader>"
}
Reply with the JSON object only.
=== REPLY ===
```json
{
"back": "Back to homepage",
"description": "Thanks for sending us your project. A project manager will look it over and get back to you with next steps.",
"next_description": "We'll book a site visit to see the building and talk through what you have in mind before we draw anything up.",
"next_heading": "We'll arrange a survey",
"question": "What happens now that we've sent this?"
}
```
@@ -0,0 +1,46 @@
You write the words for one page of a website for this business.
Business: Nordvik Bygg
About: We are twelve people. We survey old buildings, draw up rehabilitation plans, handle the permit application, and do the building work with our own carpenters plus subcontractors for electrical, plumbing and scaffolding.
Contact: Ingrid Nordvik <ingrid@nordvikbygg.example>
Language to write in: en
Everyone the site serves:
- building-problem: housing boards and private owners with a problem with a building - wants: someone to come and look at the building and tell them what can be done. Handled by project_managers. Stages: open -> surveyed -> quoted -> won -> lost.
- new-build-project: owners who already know what they want built - wants: drawings, a permit and a price. Handled by project_managers. Stages: open -> surveyed -> quoted -> won -> lost.
- subcontractor-application: trade firms who want to be on the list of subcontractors - wants: to be approved as a subcontractor. Handled by project_managers. Stages: open -> checking -> papers_requested -> approved -> declined.
- job-application: carpenters and apprentices asking if we are hiring - wants: a job with us. Handled by project_managers. Stages: open -> interviewing -> hired -> declined.
- report-site-issue: crew, ours and the subcontractors' people, on site - wants: to report a hazard, near miss or wrong delivery. Handled by safety_officer. Stages: open -> action_decided -> closed.
The page: questions/received/subcontractor-application.yaml
It is a page shown right after someone has sent a form. Say who reads it, when they answer, and what happens next.
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.
{
"back": "<WRITE: a button, one to three words, saying what pressing it does>",
"description": "<WRITE: one or two short sentences>",
"next_description": "<WRITE: one or two short sentences>",
"next_heading": "<WRITE: a heading, at most eight words, saying what the reader gets>",
"question": "<WRITE: the one question this page asks its reader>"
}
Reply with the JSON object only.
=== REPLY ===
{
"back": "Back to home",
"description": "Thanks for applying to join our subcontractor list. A project manager will check your details and papers before you're added.",
"next_description": "We'll look over your firm's details and get back to you about what's needed to be approved.",
"next_heading": "We'll review your application",
"question": "What happens now that we've applied?"
}
@@ -0,0 +1,100 @@
You write the words for one page of a website for this business.
Business: Nordvik Bygg
About: We are twelve people. We survey old buildings, draw up rehabilitation plans, handle the permit application, and do the building work with our own carpenters plus subcontractors for electrical, plumbing and scaffolding.
Contact: Ingrid Nordvik <ingrid@nordvikbygg.example>
Language to write in: en
Everyone the site serves:
- building-problem: housing boards and private owners with a problem with a building - wants: someone to come and look at the building and tell them what can be done. Handled by project_managers. Stages: open -> surveyed -> quoted -> won -> lost.
- new-build-project: owners who already know what they want built - wants: drawings, a permit and a price. Handled by project_managers. Stages: open -> surveyed -> quoted -> won -> lost.
- subcontractor-application: trade firms who want to be on the list of subcontractors - wants: to be approved as a subcontractor. Handled by project_managers. Stages: open -> checking -> papers_requested -> approved -> declined.
- job-application: carpenters and apprentices asking if we are hiring - wants: a job with us. Handled by project_managers. Stages: open -> interviewing -> hired -> declined.
- report-site-issue: crew, ours and the subcontractors' people, on site - wants: to report a hazard, near miss or wrong delivery. Handled by safety_officer. Stages: open -> action_decided -> closed.
The page: questions/safety_officer/index.yaml
It is 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.
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.
{
"buckets": {
"site_issues": {
"description": "<WRITE: one or two short sentences>",
"heading": "<WRITE: a heading, at most eight words, saying what the reader gets>",
"moves": {
"action_decided->closed": "<WRITE: a button, two to four words, for a case going action_decided->closed>",
"open->action_decided": "<WRITE: a button, two to four words, for a case going open->action_decided>"
}
}
},
"confirm": "<WRITE: the one button under every list on this desk, confirming a move the person has chosen; one word, such as Confirm>",
"description": "<WRITE: one or two short sentences>",
"invited": {
"description": "<WRITE: one or two short sentences>",
"done": "<WRITE: a form label for the field `done`>",
"heading": "<WRITE: a heading, at most eight words, saying what the reader gets>"
},
"invites": {
"safety_officer": {
"button": "<WRITE: a button, one to three words, saying what pressing it does>",
"description": "<WRITE: one or two short sentences>",
"form_title": "<WRITE: a title for the form, two to four words>",
"heading": "<WRITE: a heading, at most eight words, saying what the reader gets>",
"labels": {
"email": "<WRITE: a form label for the field `email`>",
"name": "<WRITE: a form label for the field `name`>"
}
}
},
"links": {},
"question": "<WRITE: the one question this page asks its reader>"
}
Reply with the JSON object only.
=== REPLY ===
```json
{
"buckets": {
"site_issues": {
"description": "Hazards, near misses and delivery mix-ups reported from site, waiting on a decision or a close-out.",
"heading": "What's been reported from site",
"moves": {
"action_decided->closed": "Mark sorted",
"open->action_decided": "Log what we're doing"
}
}
},
"confirm": "Confirm",
"description": "Everything reported from site, so you can decide what to do and close it out.",
"invited": {
"description": "Safety officers you've invited and whether they've signed up yet.",
"done": "Signed up",
"heading": "Safety officers you've invited"
},
"invites": {
"safety_officer": {
"button": "Send invite",
"description": "Give another safety officer their own account and a one-time link to sign up.",
"form_title": "Invite a safety officer",
"heading": "Bring in another safety officer",
"labels": {
"email": "Email address",
"name": "Full name"
}
}
},
"links": {},
"question": "What's been reported from site that still needs sorting?"
}
```
@@ -0,0 +1,166 @@
needs:
- done_when:
- won
- lost
enters: /
handled_by: project_managers
id: building-problem
lands_in: enquiries
max_fields: 4
max_steps: 1
stage_words:
lost: lost
open: open
quoted: quoted
surveyed: surveyed
won: won
stages:
- open
- surveyed
- quoted
- won
- lost
success:
- won
via: Find out what's wrong with your building
wants: someone to come and look at the building and tell them what can be done
who: housing boards and private owners with a problem with a building
- done_when:
- won
- lost
enters: /
handled_by: project_managers
id: new-build-project
lands_in: enquiries
max_fields: 4
max_steps: 1
stage_words:
lost: lost
open: open
quoted: quoted
surveyed: surveyed
won: won
stages:
- open
- surveyed
- quoted
- won
- lost
success:
- won
via: Get your project drawn, permitted and priced
wants: drawings, a permit and a price
who: owners who already know what they want built
- done_when:
- approved
- declined
enters: /
handled_by: project_managers
id: subcontractor-application
lands_in: subcontractors
max_fields: 5
max_steps: 1
stage_words:
approved: approved
checking: checking
declined: declined
open: open
papers_requested: papers requested
stages:
- open
- checking
- papers_requested
- approved
- declined
success:
- approved
via: Get work as one of our subcontractors
wants: to be approved as a subcontractor
who: trade firms who want to be on the list of subcontractors
- done_when:
- hired
- declined
enters: /
handled_by: project_managers
id: job-application
lands_in: job_applications
max_fields: 4
max_steps: 1
stage_words:
declined: declined
hired: hired
interviewing: interviewing
open: open
stages:
- open
- interviewing
- hired
- declined
success:
- hired
via: Work with our carpenters
wants: a job with us
who: carpenters and apprentices asking if we are hiring
- done_when:
- closed
enters: /crew/report-site-issue
enters_as: crew
handled_by: safety_officer
id: report-site-issue
lands_in: site_issues
max_fields: 3
max_steps: 1
stage_words:
action_decided: action decided
closed: closed
open: open
stages:
- open
- action_decided
- closed
success:
- closed
via: Flag something wrong on site
wants: to report a hazard, near miss or wrong delivery
who: crew, ours and the subcontractors' people, on site
personas:
- id: housing-board-damp
need: building-problem
text: |
I chair the board of a housing cooperative from the seventies. Two ground-floor flats have damp in the walls and we don't know if it's drainage or the facade. We need someone to tell us.
- id: buyer-before-purchase
need: building-problem
text: |
We are about to buy an old timber house. Before we sign we want a professional to go through it and tell us what is wrong with it.
- id: attic-conversion
need: new-build-project
text: |
We want to turn our attic into two bedrooms and a bathroom. We need drawings and someone who handles the application to the municipality.
- id: shop-to-flat
need: new-build-project
text: |
I own a former shop on the ground floor and want to make it a flat. I have no idea what is allowed. Can you sort that out and give me a price?
- id: electrician-firm
need: subcontractor-application
text: |
We are a four-person electrical firm, fully certified. We would like to get on your list so you call us when you need electricians.
- id: scaffolder
need: subcontractor-application
text: |
I run a scaffolding company. How do I get to do jobs for you?
- id: carpenter-jobseeker
need: job-application
text: |
I'm a certified carpenter with eight years on rehabilitation jobs. Are you hiring?
- id: apprentice
need: job-application
text: |
I finish vocational school this spring and need an apprenticeship as a bricklayer.
- id: loose-railing
need: report-site-issue
text: |
The railing on the third level of the scaffold on the north side is loose. Nobody got hurt but someone will.
- id: wrong-membrane
need: report-site-issue
text: |
The bathroom membrane delivered today is not the one in the specification. We stopped work on it.
@@ -0,0 +1 @@
qualifies: crew
@@ -0,0 +1,44 @@
alternatives:
- action: /crew/report-site-issue
consequence:
- Open site issues
description: See what's been reported from site and decide what to do about it.
name: Handle hazards and near misses reported from site
- action: /crew
consequence:
- Send invite
description: Give a colleague their own login so they can work cases with you.
features:
- description: ''
name: Invite a colleague
requirements:
- label: Their name
name: name
- label: Their email
name: email
type: email
invite:
group: crew
name: Bring a colleague onto the desk
- action: /crew
consequence:
- Confirm
description: People you've invited who haven't taken up their invite yet, and those who have.
features:
- description: ''
name: ''
resource:
requires_group: crew
source:
bucket: portal_invites
kind: kv
transitions:
- from: open
label: Signed up
to: done
name: Who's joined so far
description: 'Everything on your desk right now: enquiries, quotes, subcontractors and job applications, all in one place.'
name: What needs your attention right now?
responsible:
contact: ingrid@nordvikbygg.example
name: Ingrid Nordvik
@@ -0,0 +1,10 @@
alternatives:
- description: Our safety officer looks at every report the same day and decides what needs doing.
name: Safety officer reviews it today
- action: /crew
consequence:
- Back to site
description: ''
name: Back to site
description: Thanks for flagging this. It's gone straight to our safety officer.
name: What happens now that I've sent this in?
@@ -0,0 +1,43 @@
alternatives:
- action: /crew/report-site-issue-sent
consequence:
- Report issue
description: Tell the safety officer about a hazard, near miss or wrong delivery on site.
encouragements:
- Takes a minute — even a rough description helps.
features:
- description: Your report goes to the safety officer, who decides what to do about it, then closes it once it's sorted.
icon: lucide:clipboard-list
name: What happens next
- description: The safety officer reads this directly — if it's urgent, call Ingrid Nordvik on ingrid@nordvikbygg.example in the meantime.
icon: lucide:shield
name: Who sees this
- description: Say where and what's happened, as plainly as you can.
icon: lucide:alert-triangle
name: Report the issue
requirements:
- label: Type of issue
name: issue_type
options:
- Hazard
- Near miss
- Wrong materials
- Other
type: select
- label: Where on site
name: location
placeholder: Storgata 14, east scaffold
type: text
- label: What happened
name: details
optional: true
placeholder: Scaffold missing a guardrail on the second lift, someone could fall.
type: textarea
name: Flag something wrong on site
record_as: site_issues
description: See a hazard, a near miss or the wrong delivery turn up? Let us know before it becomes a problem.
followup: false
name: Has something happened on site that needs attention?
responsible:
contact: ingrid@nordvikbygg.example
name: Ingrid Nordvik
@@ -0,0 +1,9 @@
alternatives:
- description: We're still checking you over and will be in touch shortly about next steps.
name: Keep your application under review
self_transition:
bucket: subcontractors
label: Continue review
to: checking
description: We've had a look and want to move forward. Let us know if you're ready to send over your paperwork.
name: Are you ready to move this application forward?
@@ -0,0 +1,173 @@
alternatives:
- description: Twelve of us, surveying buildings, drawing up plans, getting the permits sorted, and doing the work ourselves alongside trusted subcontractors.
features:
- description: We come look at the building, tell you honestly what's wrong and what it'll take to fix.
icon: lucide:search
name: Know what's wrong before you spend a krone
- description: We draw up the plans, apply for the permit, and build it with our own carpenters and trusted subcontractors.
icon: lucide:ruler
name: See your project through from drawing to done
name: Building work you can trust from start to finish
- action: /received/building-problem
consequence:
- Get a survey
description: Tell us what's wrong with the building and we'll come take a look.
encouragements:
- A project manager reads every message and gets back to you, usually within a couple of days.
features:
- description: We look at what you've sent and book a site visit to survey the building. From there we quote the work, and if you go ahead, the job is won and we get started.
icon: lucide:route
name: What happens next
- description: A project manager handles this from start to finish, and you can call or email us any time in between.
icon: lucide:user-check
name: Who you'll hear from
- description: A few lines about what you're seeing is enough to start.
icon: lucide:search
name: Describe the building
requirements:
- label: Name
name: name
placeholder: Kari Hansen
type: text
- label: Email
name: email
placeholder: kari@example.com
type: email
- label: Phone
name: phone
placeholder: 912 34 567
type: tel
- label: What's the problem?
name: problem
placeholder: Damp patches on the ceiling in two flats, worse after rain
type: textarea
- label: Address
name: address
optional: true
placeholder: Storgata 14, 0155 Oslo
type: text
name: Find out what's wrong with your building
record_as: enquiries
- action: /received/new-build-project
consequence:
- Start my project
description: Tell us what you want built and we'll draw it up, handle the permit, and give you a price.
encouragements:
- A project manager looks at every project and comes back to you with next steps.
features:
- description: We survey the site, draw up plans and quote the job. Once you accept, we apply for the permit and get building.
icon: lucide:route
name: What happens next
- description: A project manager owns your project and is your point of contact by phone or email throughout.
icon: lucide:user-check
name: Who you'll hear from
- description: Sketches or plans help, but a description is enough to start.
icon: lucide:ruler
name: Describe your project
requirements:
- label: Name
name: name
placeholder: Per Olsen
type: text
- label: Email
name: email
placeholder: per@example.com
type: email
- label: Phone
name: phone
placeholder: 923 45 678
type: tel
- label: What do you want to build?
name: project_description
placeholder: Extending the kitchen with a 20m2 addition and a new terrace
type: textarea
name: Get your project drawn, permitted and priced
record_as: enquiries
- action: /received/subcontractor-application
consequence:
- Apply now
description: Electrical, plumbing or scaffolding firms, get on our list and we'll bring you in on jobs.
encouragements:
- Send what you have now, we'll ask for anything more we need.
features:
- description: We check your papers, and may ask for more documents. Once everything's in order, you're approved and on our list.
icon: lucide:route
name: What happens next
- description: A project manager checks applications and can be reached by phone or email if you have questions.
icon: lucide:user-check
name: Who you'll hear from
- description: Have your registration number and insurance details to hand.
icon: lucide:handshake
name: Apply as a subcontractor
requirements:
- label: Company name
name: company_name
placeholder: Berg Elektro AS
type: text
- label: Email
name: email
placeholder: post@bergelektro.example
type: email
- label: Phone
name: phone
placeholder: 934 56 789
type: tel
- label: Trade
name: trade
options:
- Electrical
- Plumbing
- Scaffolding
- Other
type: select
- label: Registration number
name: registration_number
placeholder: 918 234 567
type: text
- label: Insurance details
name: insurance_info
optional: true
placeholder: Liability insurance with If, policy 123456
type: textarea
name: Get work as one of our subcontractors
record_as: subcontractors
- action: /received/job-application
consequence:
- Send application
description: Carpenters and apprentices, tell us a bit about yourself and we'll see where you'd fit.
encouragements:
- No CV needed to get started, just tell us who you are.
features:
- description: We read your message and if it looks like a fit, we call you in for a chat. If that goes well, we'll offer you a start.
icon: lucide:route
name: What happens next
- description: A project manager reads applications and can answer questions by phone or email while you wait.
icon: lucide:user-check
name: Who you'll hear from
- description: A short message about your experience is all we need to begin.
icon: lucide:hammer
name: Tell us about yourself
requirements:
- label: Name
name: name
placeholder: Ola Berg
type: text
- label: Email
name: email
placeholder: ola@example.com
type: email
- label: Phone
name: phone
placeholder: 901 23 456
type: tel
- label: Tell us about yourself
name: message
placeholder: Trained carpenter, three years on renovation sites, looking for full-time work
type: textarea
name: Work with our carpenters
record_as: job_applications
description: We look after old buildings and new ones, from the first survey to the last coat of paint, with our own carpenters and trusted subcontractors for the rest.
name: What do you need done to a building?
responsible:
contact: ingrid@nordvikbygg.example
name: Ingrid Nordvik
@@ -0,0 +1 @@
qualifies: project_managers
@@ -0,0 +1,141 @@
alternatives:
- action: /project_managers
consequence:
- Confirm
description: Every building problem and new-build request that has come in, from first contact to won or lost.
features:
- description: ''
name: ''
resource:
requires_group: project_managers
source:
bucket: enquiries
kind: kv
transitions:
- from: open
label: Mark as surveyed
to: surveyed
- from: open
label: Send straight to quoted
to: quoted
- from: open
label: Mark as won
to: won
- from: open
label: Mark as lost
to: lost
- from: quoted
label: Mark as won
to: won
- from: quoted
label: Mark as lost
to: lost
- from: surveyed
label: Quote sent
to: quoted
- from: surveyed
label: Mark as won
to: won
- from: surveyed
label: Mark as lost
to: lost
name: New enquiries and where they stand
- action: /project_managers
consequence:
- Confirm
description: Carpenters and apprentices who have asked if we're hiring.
features:
- description: ''
name: ''
resource:
requires_group: project_managers
source:
bucket: job_applications
kind: kv
transitions:
- from: interviewing
label: Offer the job
to: hired
- from: interviewing
label: Not taking them on
to: declined
- from: open
label: Call in for interview
to: interviewing
- from: open
label: Offer the job
to: hired
- from: open
label: Not taking them on
to: declined
name: People asking for work
- action: /project_managers
consequence:
- Confirm
description: Trade firms asking to get on our subcontractor list, and where each one is in the vetting.
features:
- description: ''
name: ''
resource:
requires_group: project_managers
source:
bucket: subcontractors
kind: kv
transitions:
- from: checking
label: Ask for their papers
to: papers_requested
- from: checking
label: Approve them
to: approved
- from: checking
label: Turn them down
to: declined
- from: open
label: Turn them down
to: declined
- from: papers_requested
label: Approve them
to: approved
- from: papers_requested
label: Turn them down
to: declined
name: Firms wanting to be a subcontractor
- action: /project_managers
consequence:
- Send invite
description: Bring another project manager onto the desk, with their own login.
features:
- description: ''
name: Invite a project manager
requirements:
- label: Their name
name: name
- label: Their email address
name: email
type: email
invite:
group: project_managers
name: Add another project manager
- action: /project_managers
consequence:
- Confirm
description: Project managers you've invited who haven't signed in yet.
features:
- description: ''
name: ''
resource:
requires_group: project_managers
source:
bucket: portal_invites
kind: kv
transitions:
- from: open
label: Signed in
to: done
name: Invites waiting to be taken up
description: Your desk for enquiries, job applications and subcontractors waiting on you.
name: What's waiting on me today?
responsible:
contact: ingrid@nordvikbygg.example
name: Ingrid Nordvik
@@ -0,0 +1,10 @@
alternatives:
- description: A project manager will go through what you've sent and get in touch to arrange a look at the building.
name: A project manager gets in touch
- action: /
consequence:
- Back to home
description: ''
name: Back to home
description: Thanks for telling us about the building. We've logged your report and will take it from here.
name: What happens now that we've sent this in?
@@ -0,0 +1,10 @@
alternatives:
- description: We'll go through your application and be in touch, whether that's to set up a chat or let you know where things stand.
name: We'll be in touch about work with us
- action: /
consequence:
- Back to home
description: ''
name: Back to home
description: Thanks for reaching out. Ingrid or one of the project managers will read it and get back to you.
name: What happens now that I've sent my application?
@@ -0,0 +1,10 @@
alternatives:
- description: We'll book a site visit to see the building and talk through what you have in mind before we draw anything up.
name: We'll arrange a survey
- action: /
consequence:
- Back to homepage
description: ''
name: Back to homepage
description: Thanks for sending us your project. A project manager will look it over and get back to you with next steps.
name: What happens now that we've sent this?
@@ -0,0 +1,10 @@
alternatives:
- description: We'll look over your firm's details and get back to you about what's needed to be approved.
name: We'll review your application
- action: /
consequence:
- Back to home
description: ''
name: Back to home
description: Thanks for applying to join our subcontractor list. A project manager will check your details and papers before you're added.
name: What happens now that we've applied?
@@ -0,0 +1 @@
qualifies: safety_officer
@@ -0,0 +1,59 @@
alternatives:
- action: /safety_officer
consequence:
- Confirm
description: Hazards, near misses and delivery mix-ups reported from site, waiting on a decision or a close-out.
features:
- description: ''
name: ''
resource:
requires_group: safety_officer
source:
bucket: site_issues
kind: kv
transitions:
- from: action_decided
label: Mark sorted
to: closed
- from: open
label: Log what we're doing
to: action_decided
name: What's been reported from site
- action: /safety_officer
consequence:
- Send invite
description: Give another safety officer their own account and a one-time link to sign up.
features:
- description: ''
name: Invite a safety officer
requirements:
- label: Full name
name: name
- label: Email address
name: email
type: email
invite:
group: safety_officer
name: Bring in another safety officer
- action: /safety_officer
consequence:
- Confirm
description: Safety officers you've invited and whether they've signed up yet.
features:
- description: ''
name: ''
resource:
requires_group: safety_officer
source:
bucket: portal_invites
kind: kv
transitions:
- from: open
label: Signed up
to: done
name: Safety officers you've invited
description: Everything reported from site, so you can decide what to do and close it out.
name: What's been reported from site that still needs sorting?
responsible:
contact: ingrid@nordvikbygg.example
name: Ingrid Nordvik
@@ -0,0 +1,5 @@
lang: en
mail:
from: Nordvik Bygg <post@nordvikbygg.example>
reply_to: ingrid@nordvikbygg.example
title: Nordvik Bygg
@@ -0,0 +1,391 @@
{
"aggregates.yaml": {
"enquiries": {
"lost": {
"body": "Thanks for the chance to look at this - not one we're able to take on. If plans change or you want a second opinion down the line, get in touch.",
"subject": "Not one we can take on this time"
},
"open": {
"body": "We've had your enquiry land. A project manager will come back to you shortly about a visit.",
"subject": "Got your enquiry"
},
"won": {
"body": "Good news - we're going ahead with your job. Your project manager will be in touch about next steps.",
"subject": "You're booked in"
}
},
"job_applications": {
"declined": {
"body": "Thanks for applying - we've filled the position for now. We'll keep your details on file if that changes.",
"subject": "No opening right now"
},
"hired": {
"body": "Welcome aboard - we'd like to bring you on. We'll be in touch about start dates and paperwork.",
"subject": "Welcome to the team"
},
"open": {
"body": "Thanks for reaching out about work with us. We'll look over what you've sent and get back to you.",
"subject": "Got your application"
}
},
"subcontractors": {
"approved": {
"body": "You're on our list of subcontractors now. We'll be in touch when a job comes up that fits.",
"subject": "You're on our list"
},
"declined": {
"body": "Thanks for applying - we won't be adding you to our list at this time.",
"subject": "Not adding you to our list"
},
"open": {
"body": "We've had your application to become a subcontractor. We'll look it over and be in touch.\n\nWhat you can do next: {site}/decide/subcontractor-application?chain={chain}&email={email}",
"subject": "Got your application"
},
"papers_requested": {
"body": "We need a few papers from you before we can approve you - see the link below for what's missing.",
"subject": "We need some paperwork from you"
}
}
},
"questions/crew/index.yaml": {
"buckets": {},
"confirm": "Confirm",
"description": "Everything on your desk right now: enquiries, quotes, subcontractors and job applications, all in one place.",
"invited": {
"description": "People you've invited who haven't taken up their invite yet, and those who have.",
"done": "Signed up",
"heading": "Who's joined so far"
},
"invites": {
"crew": {
"button": "Send invite",
"description": "Give a colleague their own login so they can work cases with you.",
"form_title": "Invite a colleague",
"heading": "Bring a colleague onto the desk",
"labels": {
"email": "Their email",
"name": "Their name"
}
}
},
"links": {
"report-site-issue": {
"button": "Open site issues",
"description": "See what's been reported from site and decide what to do about it.",
"heading": "Handle hazards and near misses reported from site"
}
},
"question": "What needs your attention right now?"
},
"questions/crew/report-site-issue-sent.yaml": {
"back": "Back to site",
"description": "Thanks for flagging this. It's gone straight to our safety officer.",
"next_description": "Our safety officer looks at every report the same day and decides what needs doing.",
"next_heading": "Safety officer reviews it today",
"question": "What happens now that I've sent this in?"
},
"questions/crew/report-site-issue.yaml": {
"button": "Report issue",
"description": "Tell the safety officer about a hazard, near miss or wrong delivery on site.",
"encouragement": "Takes a minute — even a rough description helps.",
"form_description": "Say where and what's happened, as plainly as you can.",
"form_title": "Report the issue",
"heading": "Flag something wrong on site",
"hints": {
"details": "Scaffold missing a guardrail on the second lift, someone could fall.",
"issue_type": "Near miss",
"location": "Storgata 14, east scaffold"
},
"icon": "alert-triangle",
"labels": {
"details": "What happened",
"issue_type": "Type of issue",
"location": "Where on site"
},
"page_description": "See a hazard, a near miss or the wrong delivery turn up? Let us know before it becomes a problem.",
"question": "Has something happened on site that needs attention?",
"road_description": "Your report goes to the safety officer, who decides what to do about it, then closes it once it's sorted.",
"road_icon": "clipboard-list",
"road_title": "What happens next",
"who_description": "The safety officer reads this directly — if it's urgent, call Ingrid Nordvik on ingrid@nordvikbygg.example in the meantime.",
"who_icon": "shield",
"who_title": "Who sees this"
},
"questions/decide/subcontractor-application.yaml": {
"description": "We've had a look and want to move forward. Let us know if you're ready to send over your paperwork.",
"moves": {
"checking": {
"button": "Continue review",
"description": "We're still checking you over and will be in touch shortly about next steps.",
"heading": "Keep your application under review"
}
},
"question": "Are you ready to move this application forward?"
},
"questions/index.yaml": {
"description": "We look after old buildings and new ones, from the first survey to the last coat of paint, with our own carpenters and trusted subcontractors for the rest.",
"doors": {},
"needs": {
"building-problem": {
"button": "Get a survey",
"description": "Tell us what's wrong with the building and we'll come take a look.",
"encouragement": "A project manager reads every message and gets back to you, usually within a couple of days.",
"form_description": "A few lines about what you're seeing is enough to start.",
"form_title": "Describe the building",
"heading": "Find out what's wrong with your building",
"hints": {
"address": "Storgata 14, 0155 Oslo",
"email": "kari@example.com",
"name": "Kari Hansen",
"phone": "912 34 567",
"problem": "Damp patches on the ceiling in two flats, worse after rain"
},
"icon": "search",
"labels": {
"address": "Address",
"email": "Email",
"name": "Name",
"phone": "Phone",
"problem": "What's the problem?"
},
"road_description": "We look at what you've sent and book a site visit to survey the building. From there we quote the work, and if you go ahead, the job is won and we get started.",
"road_icon": "route",
"road_title": "What happens next",
"who_description": "A project manager handles this from start to finish, and you can call or email us any time in between.",
"who_icon": "user-check",
"who_title": "Who you'll hear from"
},
"job-application": {
"button": "Send application",
"description": "Carpenters and apprentices, tell us a bit about yourself and we'll see where you'd fit.",
"encouragement": "No CV needed to get started, just tell us who you are.",
"form_description": "A short message about your experience is all we need to begin.",
"form_title": "Tell us about yourself",
"heading": "Work with our carpenters",
"hints": {
"email": "ola@example.com",
"message": "Trained carpenter, three years on renovation sites, looking for full-time work",
"name": "Ola Berg",
"phone": "901 23 456"
},
"icon": "hammer",
"labels": {
"email": "Email",
"message": "Tell us about yourself",
"name": "Name",
"phone": "Phone"
},
"road_description": "We read your message and if it looks like a fit, we call you in for a chat. If that goes well, we'll offer you a start.",
"road_icon": "route",
"road_title": "What happens next",
"who_description": "A project manager reads applications and can answer questions by phone or email while you wait.",
"who_icon": "user-check",
"who_title": "Who you'll hear from"
},
"new-build-project": {
"button": "Start my project",
"description": "Tell us what you want built and we'll draw it up, handle the permit, and give you a price.",
"encouragement": "A project manager looks at every project and comes back to you with next steps.",
"form_description": "Sketches or plans help, but a description is enough to start.",
"form_title": "Describe your project",
"heading": "Get your project drawn, permitted and priced",
"hints": {
"email": "per@example.com",
"name": "Per Olsen",
"phone": "923 45 678",
"project_description": "Extending the kitchen with a 20m2 addition and a new terrace"
},
"icon": "ruler",
"labels": {
"email": "Email",
"name": "Name",
"phone": "Phone",
"project_description": "What do you want to build?"
},
"road_description": "We survey the site, draw up plans and quote the job. Once you accept, we apply for the permit and get building.",
"road_icon": "route",
"road_title": "What happens next",
"who_description": "A project manager owns your project and is your point of contact by phone or email throughout.",
"who_icon": "user-check",
"who_title": "Who you'll hear from"
},
"subcontractor-application": {
"button": "Apply now",
"description": "Electrical, plumbing or scaffolding firms, get on our list and we'll bring you in on jobs.",
"encouragement": "Send what you have now, we'll ask for anything more we need.",
"form_description": "Have your registration number and insurance details to hand.",
"form_title": "Apply as a subcontractor",
"heading": "Get work as one of our subcontractors",
"hints": {
"company_name": "Berg Elektro AS",
"email": "post@bergelektro.example",
"insurance_info": "Liability insurance with If, policy 123456",
"phone": "934 56 789",
"registration_number": "918 234 567",
"trade": "Electrical"
},
"icon": "handshake",
"labels": {
"company_name": "Company name",
"email": "Email",
"insurance_info": "Insurance details",
"phone": "Phone",
"registration_number": "Registration number",
"trade": "Trade"
},
"road_description": "We check your papers, and may ask for more documents. Once everything's in order, you're approved and on our list.",
"road_icon": "route",
"road_title": "What happens next",
"who_description": "A project manager checks applications and can be reached by phone or email if you have questions.",
"who_icon": "user-check",
"who_title": "Who you'll hear from"
}
},
"opener": {
"description": "Twelve of us, surveying buildings, drawing up plans, getting the permits sorted, and doing the work ourselves alongside trusted subcontractors.",
"features": {
"assess-your-building": {
"description": "We come look at the building, tell you honestly what's wrong and what it'll take to fix.",
"heading": "Know what's wrong before you spend a krone",
"icon": "search"
},
"build-your-project": {
"description": "We draw up the plans, apply for the permit, and build it with our own carpenters and trusted subcontractors.",
"heading": "See your project through from drawing to done",
"icon": "ruler"
}
},
"heading": "Building work you can trust from start to finish"
},
"question": "What do you need done to a building?",
"shopping": {
"description": "Not ready to send anything yet? Have a look at how we work and what we've done before.",
"heading": "See how we work before you get in touch",
"items": {}
}
},
"questions/project_managers/index.yaml": {
"buckets": {
"enquiries": {
"description": "Every building problem and new-build request that has come in, from first contact to won or lost.",
"heading": "New enquiries and where they stand",
"moves": {
"open->lost": "Mark as lost",
"open->quoted": "Send straight to quoted",
"open->surveyed": "Mark as surveyed",
"open->won": "Mark as won",
"quoted->lost": "Mark as lost",
"quoted->won": "Mark as won",
"surveyed->lost": "Mark as lost",
"surveyed->quoted": "Quote sent",
"surveyed->won": "Mark as won"
}
},
"job_applications": {
"description": "Carpenters and apprentices who have asked if we're hiring.",
"heading": "People asking for work",
"moves": {
"interviewing->declined": "Not taking them on",
"interviewing->hired": "Offer the job",
"open->declined": "Not taking them on",
"open->hired": "Offer the job",
"open->interviewing": "Call in for interview"
}
},
"subcontractors": {
"description": "Trade firms asking to get on our subcontractor list, and where each one is in the vetting.",
"heading": "Firms wanting to be a subcontractor",
"moves": {
"checking->approved": "Approve them",
"checking->declined": "Turn them down",
"checking->papers_requested": "Ask for their papers",
"open->declined": "Turn them down",
"papers_requested->approved": "Approve them",
"papers_requested->declined": "Turn them down"
}
}
},
"confirm": "Confirm",
"description": "Your desk for enquiries, job applications and subcontractors waiting on you.",
"invited": {
"description": "Project managers you've invited who haven't signed in yet.",
"done": "Signed in",
"heading": "Invites waiting to be taken up"
},
"invites": {
"project_managers": {
"button": "Send invite",
"description": "Bring another project manager onto the desk, with their own login.",
"form_title": "Invite a project manager",
"heading": "Add another project manager",
"labels": {
"email": "Their email address",
"name": "Their name"
}
}
},
"links": {},
"question": "What's waiting on me today?"
},
"questions/received/building-problem.yaml": {
"back": "Back to home",
"description": "Thanks for telling us about the building. We've logged your report and will take it from here.",
"next_description": "A project manager will go through what you've sent and get in touch to arrange a look at the building.",
"next_heading": "A project manager gets in touch",
"question": "What happens now that we've sent this in?"
},
"questions/received/job-application.yaml": {
"back": "Back to home",
"description": "Thanks for reaching out. Ingrid or one of the project managers will read it and get back to you.",
"next_description": "We'll go through your application and be in touch, whether that's to set up a chat or let you know where things stand.",
"next_heading": "We'll be in touch about work with us",
"question": "What happens now that I've sent my application?"
},
"questions/received/new-build-project.yaml": {
"back": "Back to homepage",
"description": "Thanks for sending us your project. A project manager will look it over and get back to you with next steps.",
"next_description": "We'll book a site visit to see the building and talk through what you have in mind before we draw anything up.",
"next_heading": "We'll arrange a survey",
"question": "What happens now that we've sent this?"
},
"questions/received/subcontractor-application.yaml": {
"back": "Back to home",
"description": "Thanks for applying to join our subcontractor list. A project manager will check your details and papers before you're added.",
"next_description": "We'll look over your firm's details and get back to you about what's needed to be approved.",
"next_heading": "We'll review your application",
"question": "What happens now that we've applied?"
},
"questions/safety_officer/index.yaml": {
"buckets": {
"site_issues": {
"description": "Hazards, near misses and delivery mix-ups reported from site, waiting on a decision or a close-out.",
"heading": "What's been reported from site",
"moves": {
"action_decided->closed": "Mark sorted",
"open->action_decided": "Log what we're doing"
}
}
},
"confirm": "Confirm",
"description": "Everything reported from site, so you can decide what to do and close it out.",
"invited": {
"description": "Safety officers you've invited and whether they've signed up yet.",
"done": "Signed up",
"heading": "Safety officers you've invited"
},
"invites": {
"safety_officer": {
"button": "Send invite",
"description": "Give another safety officer their own account and a one-time link to sign up.",
"form_title": "Invite a safety officer",
"heading": "Bring in another safety officer",
"labels": {
"email": "Email address",
"name": "Full name"
}
}
},
"links": {},
"question": "What's been reported from site that still needs sorting?"
}
}
@@ -0,0 +1,168 @@
# plan.yaml
A plan is a needs spec that also says what each form collects. A
builder turns it into a working site with no further decisions, so
every structural fact about the business has to be in it.
```yaml
business: <name>
language: en # the intake's language code
contact: { name: <who answers for the site>, email: <their email> }
about: >
What the business does, in its own words.
mail: { from: "<name> <post@their-domain>", reply_to: <their inbox> }
# who every mail the site sends comes from; required as soon
# as any need has `mails`
does: # the front door's first card: what the business does, one
- id: kebab-case # or two features, one role each, no button. Say it for the
for: <need id> # role the business exists for and the next one it serves.
text: what it does for them, in the intake's own words
shows: # things a visitor may look at before asking for anything;
- id: kebab-case # each `resource` is portal's resource block, verbatim
text: one line on what it is
resource: { public: true, source: { kind: url, url: https://... } }
doors: # a first question before the forms, only when the offers
- id: kebab-case # are more than one page holds (measured: four doors beat
who: who comes through it # ten offers on one page; six offers on one page beat doors)
groups: # groups with a label the public may see: the committees
- { id: traffic, label: Traffic, invited_by: [board] } # a person can ask to join (optional)
invites_by: [board] # groups whose desk may invite people into every desk
needs:
- id: kebab-case # ASCII only
who: the kind of person, in plain words, in the intake's language
wants: what they want from the business
door: <door id> # the door this public need sits behind; omit to put it on the front door
enters_as: <group> # ONLY for a role inside the business; omit for the public
lands_in: <bucket> # snake_case, ASCII
handled_by: <group> # the role that must see and move the record
stages: [open, <stage>, ..., <end>, <end>]
# EVERY stage the business said a case goes through, first to
# last, read off their own sentences one verb at a time: "they
# come in, we go and look, we send a quote, we win or lose it"
# is [open, surveyed, quoted, won, lost]. A pipeline is not an
# inbox: "answered" is rarely the real outcome.
done_when: [<end>, <end>] # every stage that counts as finished, good or bad
success: [<end>] # the finished stages that are a good outcome for the business
skippable: [<stage>] # stages a case may pass over (papers already complete)
turned_down_from: [open, <stage>]
# 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: { <stage>: [<group>, ...] }
# only where someone other than handled_by moves a case INTO a
# stage: another group, or `submitter` (the person who sent the
# form; the form must then collect a required `email`)
watched_by: [<group>] # groups that see these cases and move nothing; `public` opens a
# read-only page of the bucket to everyone (contact details hidden)
mails: { open: submitter, <end>: submitter, <stage>: <address> }
# who hears when a case enters a stage: `submitter` (the form
# must then collect a required `email`) or one address. Tell the
# person on arrival and on every decision that is about them.
stages_text: { open: <words>, <stage>: <words> }
# each stage in the intake's language, where the ASCII id
# would read wrong to a person: "ikke_lost" is "ikke løst"
events: { open: applied } # ONLY when rebuilding a live site: the event names its
# aggregates.yaml already has, so its records keep replaying
attended_by: > # ONLY for a stage no desk button moves: the automation that does
what moves it, in a sentence
max_fields: 4 # decide this BEFORE writing collects, from what the intake
# says about the person; a budget that equals the form's size
# is counted, not decided, and is reported as such
# how many required fields THIS person will tolerate, from what
# the intake says about them: someone on a phone with gloves
# on gets 3, a first enquiry 4 to 6, a firm's paperwork 5 to 6
collects: # the form; name is snake_case ASCII, type one of
# text (default), email, tel, textarea, date, select
- { name: name }
- { name: phone, type: tel }
- { name: problem, type: textarea }
- { name: urgency, type: select, options: [This week, This month, No rush] }
- { name: can_do, type: select, multiple: true, options: [Drive, Cook, Carry] } # any number
- { name: committee, type: select, options_from: groups } # the labelled groups above
- { name: address, optional: true, private: true } # never on a public page
```
Rules:
- One need per (kind of person, thing they want). Work inside the
business counts: a role recording or posting something is a need
with `enters_as`. The intake's "what slows us down" usually names one.
- Groups are the roles the intake names, as snake_case identifiers.
- The front door opens with `does` (at most two features, one role
each) and `shows`, then holds at most seven offers: doors plus needs
with no door. A door with no need behind it, or a door on a need with
`enters_as`, is refused. A role `does` speaks for must have a move
of its own after sending (`moves_by` with `submitter`) or a public
view, or it is a spectator.
- Every need whose form collects an email should tell the person on
arrival and on the decisions about them (`mails`); the plan then
needs `mail: { from }`.
- Needs that land in the same bucket must have the same stages,
done_when, skippable, moves_by, watched_by and handled_by.
- Every required field is one the business will use to act on the
record. If the intake gives no reason to need it, make it optional or
leave it out. The required fields must not exceed max_fields.
- Only `select` lists `options`, and it must list them.
- Everything a person reads (who, wants, about, select options) is in
the intake's language. Everything a machine reads (ids, buckets,
groups, stages, field names) is plain ASCII: no æ, ø, å.
- Write no personas.
# Task
A business filled in this intake. Write its plan.yaml: every need the intake implies, for the public and for each role inside the business, with the stages read off how the business says things progress, the form each need collects, and how much each person will tolerate.
## Intake
# An intake as the owner of a small building contractor would fill it
# in: plain words, no portal vocabulary. These are the fields the
# onboarding site asks for.
business: Nordvik Bygg
contact: { name: Ingrid Nordvik, email: ingrid@nordvikbygg.example }
language: en
what_we_do: >
We are twelve people. We survey old buildings, draw up rehabilitation
plans, handle the permit application, and do the building work with
our own carpenters plus subcontractors for electrical, plumbing and
scaffolding.
who_comes_to_us: >
Housing boards and private owners who have a problem with a building
and want someone to look at it. Owners who already know what they
want built and need drawings, a permit and a price. Trade firms who
want to be on our list of subcontractors. Carpenters and apprentices
asking if we are hiring.
what_we_keep_track_of: >
Enquiries: they come in, we go and look at the building, we send a
quote, and then we either win it or lose it. Sometimes it is simply
not for us and we say so straight away.
Subcontractors: they apply, we check their company registration,
trade approval and insurance. Often papers are missing and we have to
ask again. Then they are approved or turned down.
Job applications: we read them, invite some to an interview, and hire
or decline.
Things that go wrong on site: a hazard, a near miss, wrong materials
delivered. Someone reports it, the safety officer decides what is
done about it, and closes it when it is fixed.
who_is_involved: >
Two project managers handle enquiries, subcontractors and hiring.
One safety officer handles everything reported from site.
The crew, ours and the subcontractors' people, are the ones on site
who report things. They are on their phones, with gloves on, and
will not fill in a long form.
what_slows_us_down: >
Enquiries arrive by phone, email and text and get lost. We never know
which subcontractors have valid papers. Site problems get mentioned
at lunch and forgotten.
Reply with the YAML only, no prose, no code fences.
@@ -0,0 +1,103 @@
```yaml
business: Nordvik Bygg
language: en
contact: { name: Ingrid Nordvik, email: ingrid@nordvikbygg.example }
about: >
We survey old buildings, draw up rehabilitation plans, handle the
permit application, and do the building work with our own carpenters
plus subcontractors for electrical, plumbing and scaffolding.
mail: { from: "Nordvik Bygg <post@nordvikbygg.example>", reply_to: ingrid@nordvikbygg.example }
does:
- id: survey-and-build
for: enquiry
text: We survey old buildings, draw up rehabilitation plans, handle the permit, and build.
- id: subcontractor-network
for: subcontractor-application
text: We work with subcontractors for electrical, plumbing and scaffolding.
doors:
- id: work
who: housing boards and owners with a building problem or a project in mind
- id: join
who: trade firms, carpenters and apprentices who want to work with us
groups:
- id: project_managers
- id: safety_officer
- id: crew
needs:
- id: enquiry
who: a housing board or private owner with a problem with a building
wants: someone to look at the building and quote the work
door: work
lands_in: enquiries
handled_by: project_managers
stages: [open, surveyed, quoted, won, lost]
done_when: [won, lost]
success: [won]
turned_down_from: [open, surveyed]
mails: { open: submitter, won: submitter, lost: submitter }
max_fields: 4
collects:
- { name: name }
- { name: email, type: email }
- { name: phone, type: tel }
- { name: problem, type: textarea }
- id: subcontractor-application
who: a trade firm that wants to be on the subcontractor list
wants: to be approved as a subcontractor for electrical, plumbing or scaffolding work
door: join
lands_in: subcontractor_applications
handled_by: project_managers
stages: [open, checking_papers, approved, turned_down]
done_when: [approved, turned_down]
success: [approved]
skippable: [checking_papers]
turned_down_from: [open, checking_papers]
mails: { open: submitter, approved: submitter, turned_down: submitter }
max_fields: 5
collects:
- { name: company_name }
- { name: email, type: email }
- { name: trade, type: select, options: [Electrical, Plumbing, Scaffolding] }
- { name: registration_number }
- { name: insurance_details, type: textarea }
- id: job-application
who: a carpenter or apprentice asking if we are hiring
wants: to be considered for a job and invited to interview
door: join
lands_in: job_applications
handled_by: project_managers
stages: [open, interview, hired, declined]
done_when: [hired, declined]
success: [hired]
skippable: [interview]
turned_down_from: [open, interview]
mails: { open: submitter, interview: submitter, hired: submitter, declined: submitter }
max_fields: 4
collects:
- { name: name }
- { name: email, type: email }
- { name: role, type: select, options: [Carpenter, Apprentice] }
- { name: experience, type: textarea, optional: true }
- id: site-report
who: crew, ours and the subcontractors' people, on site
wants: to report a hazard, near miss or wrong delivery so it gets fixed
enters_as: crew
lands_in: site_reports
handled_by: safety_officer
stages: [open, in_progress, closed]
done_when: [closed]
success: [closed]
mails: { closed: submitter }
max_fields: 3
collects:
- { name: email, type: email }
- { name: issue_type, type: select, options: [Hazard, Near miss, Wrong materials] }
- { name: description, type: textarea }
```
@@ -0,0 +1,279 @@
# plan.yaml
A plan is a needs spec that also says what each form collects. A
builder turns it into a working site with no further decisions, so
every structural fact about the business has to be in it.
```yaml
business: <name>
language: en # the intake's language code
contact: { name: <who answers for the site>, email: <their email> }
about: >
What the business does, in its own words.
mail: { from: "<name> <post@their-domain>", reply_to: <their inbox> }
# who every mail the site sends comes from; required as soon
# as any need has `mails`
does: # the front door's first card: what the business does, one
- id: kebab-case # or two features, one role each, no button. Say it for the
for: <need id> # role the business exists for and the next one it serves.
text: what it does for them, in the intake's own words
shows: # things a visitor may look at before asking for anything;
- id: kebab-case # each `resource` is portal's resource block, verbatim
text: one line on what it is
resource: { public: true, source: { kind: url, url: https://... } }
doors: # a first question before the forms, only when the offers
- id: kebab-case # are more than one page holds (measured: four doors beat
who: who comes through it # ten offers on one page; six offers on one page beat doors)
groups: # groups with a label the public may see: the committees
- { id: traffic, label: Traffic, invited_by: [board] } # a person can ask to join (optional)
invites_by: [board] # groups whose desk may invite people into every desk
needs:
- id: kebab-case # ASCII only
who: the kind of person, in plain words, in the intake's language
wants: what they want from the business
door: <door id> # the door this public need sits behind; omit to put it on the front door
enters_as: <group> # ONLY for a role inside the business; omit for the public
lands_in: <bucket> # snake_case, ASCII
handled_by: <group> # the role that must see and move the record
stages: [open, <stage>, ..., <end>, <end>]
# EVERY stage the business said a case goes through, first to
# last, read off their own sentences one verb at a time: "they
# come in, we go and look, we send a quote, we win or lose it"
# is [open, surveyed, quoted, won, lost]. A pipeline is not an
# inbox: "answered" is rarely the real outcome.
done_when: [<end>, <end>] # every stage that counts as finished, good or bad
success: [<end>] # the finished stages that are a good outcome for the business
skippable: [<stage>] # stages a case may pass over (papers already complete)
turned_down_from: [open, <stage>]
# 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: { <stage>: [<group>, ...] }
# only where someone other than handled_by moves a case INTO a
# stage: another group, or `submitter` (the person who sent the
# form; the form must then collect a required `email`)
watched_by: [<group>] # groups that see these cases and move nothing; `public` opens a
# read-only page of the bucket to everyone (contact details hidden)
mails: { open: submitter, <end>: submitter, <stage>: <address> }
# who hears when a case enters a stage: `submitter` (the form
# must then collect a required `email`) or one address. Tell the
# person on arrival and on every decision that is about them.
stages_text: { open: <words>, <stage>: <words> }
# each stage in the intake's language, where the ASCII id
# would read wrong to a person: "ikke_lost" is "ikke løst"
events: { open: applied } # ONLY when rebuilding a live site: the event names its
# aggregates.yaml already has, so its records keep replaying
attended_by: > # ONLY for a stage no desk button moves: the automation that does
what moves it, in a sentence
max_fields: 4 # decide this BEFORE writing collects, from what the intake
# says about the person; a budget that equals the form's size
# is counted, not decided, and is reported as such
# how many required fields THIS person will tolerate, from what
# the intake says about them: someone on a phone with gloves
# on gets 3, a first enquiry 4 to 6, a firm's paperwork 5 to 6
collects: # the form; name is snake_case ASCII, type one of
# text (default), email, tel, textarea, date, select
- { name: name }
- { name: phone, type: tel }
- { name: problem, type: textarea }
- { name: urgency, type: select, options: [This week, This month, No rush] }
- { name: can_do, type: select, multiple: true, options: [Drive, Cook, Carry] } # any number
- { name: committee, type: select, options_from: groups } # the labelled groups above
- { name: address, optional: true, private: true } # never on a public page
```
Rules:
- One need per (kind of person, thing they want). Work inside the
business counts: a role recording or posting something is a need
with `enters_as`. The intake's "what slows us down" usually names one.
- Groups are the roles the intake names, as snake_case identifiers.
- The front door opens with `does` (at most two features, one role
each) and `shows`, then holds at most seven offers: doors plus needs
with no door. A door with no need behind it, or a door on a need with
`enters_as`, is refused. A role `does` speaks for must have a move
of its own after sending (`moves_by` with `submitter`) or a public
view, or it is a spectator.
- Every need whose form collects an email should tell the person on
arrival and on the decisions about them (`mails`); the plan then
needs `mail: { from }`.
- Needs that land in the same bucket must have the same stages,
done_when, skippable, moves_by, watched_by and handled_by.
- Every required field is one the business will use to act on the
record. If the intake gives no reason to need it, make it optional or
leave it out. The required fields must not exceed max_fields.
- Only `select` lists `options`, and it must list them.
- Everything a person reads (who, wants, about, select options) is in
the intake's language. Everything a machine reads (ids, buckets,
groups, stages, field names) is plain ASCII: no æ, ø, å.
- Write no personas.
# Task
A business filled in this intake. Write its plan.yaml: every need the intake implies, for the public and for each role inside the business, with the stages read off how the business says things progress, the form each need collects, and how much each person will tolerate.
## Intake
# An intake as the owner of a small building contractor would fill it
# in: plain words, no portal vocabulary. These are the fields the
# onboarding site asks for.
business: Nordvik Bygg
contact: { name: Ingrid Nordvik, email: ingrid@nordvikbygg.example }
language: en
what_we_do: >
We are twelve people. We survey old buildings, draw up rehabilitation
plans, handle the permit application, and do the building work with
our own carpenters plus subcontractors for electrical, plumbing and
scaffolding.
who_comes_to_us: >
Housing boards and private owners who have a problem with a building
and want someone to look at it. Owners who already know what they
want built and need drawings, a permit and a price. Trade firms who
want to be on our list of subcontractors. Carpenters and apprentices
asking if we are hiring.
what_we_keep_track_of: >
Enquiries: they come in, we go and look at the building, we send a
quote, and then we either win it or lose it. Sometimes it is simply
not for us and we say so straight away.
Subcontractors: they apply, we check their company registration,
trade approval and insurance. Often papers are missing and we have to
ask again. Then they are approved or turned down.
Job applications: we read them, invite some to an interview, and hire
or decline.
Things that go wrong on site: a hazard, a near miss, wrong materials
delivered. Someone reports it, the safety officer decides what is
done about it, and closes it when it is fixed.
who_is_involved: >
Two project managers handle enquiries, subcontractors and hiring.
One safety officer handles everything reported from site.
The crew, ours and the subcontractors' people, are the ones on site
who report things. They are on their phones, with gloves on, and
will not fill in a long form.
what_slows_us_down: >
Enquiries arrive by phone, email and text and get lost. We never know
which subcontractors have valid papers. Site problems get mentioned
at lunch and forgotten.
## Your last attempt was refused
does not parse as a plan: groups[0]: missing field `label` at line 25 column 5
Fix that and return the whole plan again.
```yaml
business: Nordvik Bygg
language: en
contact: { name: Ingrid Nordvik, email: ingrid@nordvikbygg.example }
about: >
We survey old buildings, draw up rehabilitation plans, handle the
permit application, and do the building work with our own carpenters
plus subcontractors for electrical, plumbing and scaffolding.
mail: { from: "Nordvik Bygg <post@nordvikbygg.example>", reply_to: ingrid@nordvikbygg.example }
does:
- id: survey-and-build
for: enquiry
text: We survey old buildings, draw up rehabilitation plans, handle the permit, and build.
- id: subcontractor-network
for: subcontractor-application
text: We work with subcontractors for electrical, plumbing and scaffolding.
doors:
- id: work
who: housing boards and owners with a building problem or a project in mind
- id: join
who: trade firms, carpenters and apprentices who want to work with us
groups:
- id: project_managers
- id: safety_officer
- id: crew
needs:
- id: enquiry
who: a housing board or private owner with a problem with a building
wants: someone to look at the building and quote the work
door: work
lands_in: enquiries
handled_by: project_managers
stages: [open, surveyed, quoted, won, lost]
done_when: [won, lost]
success: [won]
turned_down_from: [open, surveyed]
mails: { open: submitter, won: submitter, lost: submitter }
max_fields: 4
collects:
- { name: name }
- { name: email, type: email }
- { name: phone, type: tel }
- { name: problem, type: textarea }
- id: subcontractor-application
who: a trade firm that wants to be on the subcontractor list
wants: to be approved as a subcontractor for electrical, plumbing or scaffolding work
door: join
lands_in: subcontractor_applications
handled_by: project_managers
stages: [open, checking_papers, approved, turned_down]
done_when: [approved, turned_down]
success: [approved]
skippable: [checking_papers]
turned_down_from: [open, checking_papers]
mails: { open: submitter, approved: submitter, turned_down: submitter }
max_fields: 5
collects:
- { name: company_name }
- { name: email, type: email }
- { name: trade, type: select, options: [Electrical, Plumbing, Scaffolding] }
- { name: registration_number }
- { name: insurance_details, type: textarea }
- id: job-application
who: a carpenter or apprentice asking if we are hiring
wants: to be considered for a job and invited to interview
door: join
lands_in: job_applications
handled_by: project_managers
stages: [open, interview, hired, declined]
done_when: [hired, declined]
success: [hired]
skippable: [interview]
turned_down_from: [open, interview]
mails: { open: submitter, interview: submitter, hired: submitter, declined: submitter }
max_fields: 4
collects:
- { name: name }
- { name: email, type: email }
- { name: role, type: select, options: [Carpenter, Apprentice] }
- { name: experience, type: textarea, optional: true }
- id: site-report
who: crew, ours and the subcontractors' people, on site
wants: to report a hazard, near miss or wrong delivery so it gets fixed
enters_as: crew
lands_in: site_reports
handled_by: safety_officer
stages: [open, in_progress, closed]
done_when: [closed]
success: [closed]
mails: { closed: submitter }
max_fields: 3
collects:
- { name: email, type: email }
- { name: issue_type, type: select, options: [Hazard, Near miss, Wrong materials] }
- { name: description, type: textarea }
```
Reply with the YAML only, no prose, no code fences.
@@ -0,0 +1,103 @@
```yaml
business: Nordvik Bygg
language: en
contact: { name: Ingrid Nordvik, email: ingrid@nordvikbygg.example }
about: >
We survey old buildings, draw up rehabilitation plans, handle the
permit application, and do the building work with our own carpenters
plus subcontractors for electrical, plumbing and scaffolding.
mail: { from: "Nordvik Bygg <post@nordvikbygg.example>", reply_to: ingrid@nordvikbygg.example }
does:
- id: survey-and-build
for: enquiry
text: We survey old buildings, draw up rehabilitation plans, handle the permit, and build.
- id: subcontractor-network
for: subcontractor-application
text: We work with subcontractors for electrical, plumbing and scaffolding.
doors:
- id: work
who: housing boards and owners with a building problem or a project in mind
- id: join
who: trade firms, carpenters and apprentices who want to work with us
groups:
- { id: project_managers, label: Project managers }
- { id: safety_officer, label: Safety officer }
- { id: crew, label: Crew }
needs:
- id: enquiry
who: a housing board or private owner with a problem with a building
wants: someone to look at the building and quote the work
door: work
lands_in: enquiries
handled_by: project_managers
stages: [open, surveyed, quoted, won, lost]
done_when: [won, lost]
success: [won]
turned_down_from: [open, surveyed]
mails: { open: submitter, won: submitter, lost: submitter }
max_fields: 4
collects:
- { name: name }
- { name: email, type: email }
- { name: phone, type: tel }
- { name: problem, type: textarea }
- id: subcontractor-application
who: a trade firm that wants to be on the subcontractor list
wants: to be approved as a subcontractor for electrical, plumbing or scaffolding work
door: join
lands_in: subcontractor_applications
handled_by: project_managers
stages: [open, checking_papers, approved, turned_down]
done_when: [approved, turned_down]
success: [approved]
skippable: [checking_papers]
turned_down_from: [open, checking_papers]
mails: { open: submitter, approved: submitter, turned_down: submitter }
max_fields: 5
collects:
- { name: company_name }
- { name: email, type: email }
- { name: trade, type: select, options: [Electrical, Plumbing, Scaffolding] }
- { name: registration_number }
- { name: insurance_details, type: textarea }
- id: job-application
who: a carpenter or apprentice asking if we are hiring
wants: to be considered for a job and invited to interview
door: join
lands_in: job_applications
handled_by: project_managers
stages: [open, interview, hired, declined]
done_when: [hired, declined]
success: [hired]
skippable: [interview]
turned_down_from: [open, interview]
mails: { open: submitter, interview: submitter, hired: submitter, declined: submitter }
max_fields: 4
collects:
- { name: name }
- { name: email, type: email }
- { name: role, type: select, options: [Carpenter, Apprentice] }
- { name: experience, type: textarea, optional: true }
- id: site-report
who: crew, ours and the subcontractors' people, on site
wants: to report a hazard, near miss or wrong delivery so it gets fixed
enters_as: crew
lands_in: site_reports
handled_by: safety_officer
stages: [open, in_progress, closed]
done_when: [closed]
success: [closed]
mails: { closed: submitter }
max_fields: 3
collects:
- { name: email, type: email }
- { name: issue_type, type: select, options: [Hazard, Near miss, Wrong materials] }
- { name: description, type: textarea }
```
@@ -0,0 +1,280 @@
# plan.yaml
A plan is a needs spec that also says what each form collects. A
builder turns it into a working site with no further decisions, so
every structural fact about the business has to be in it.
```yaml
business: <name>
language: en # the intake's language code
contact: { name: <who answers for the site>, email: <their email> }
about: >
What the business does, in its own words.
mail: { from: "<name> <post@their-domain>", reply_to: <their inbox> }
# who every mail the site sends comes from; required as soon
# as any need has `mails`
does: # the front door's first card: what the business does, one
- id: kebab-case # or two features, one role each, no button. Say it for the
for: <need id> # role the business exists for and the next one it serves.
text: what it does for them, in the intake's own words
shows: # things a visitor may look at before asking for anything;
- id: kebab-case # each `resource` is portal's resource block, verbatim
text: one line on what it is
resource: { public: true, source: { kind: url, url: https://... } }
doors: # a first question before the forms, only when the offers
- id: kebab-case # are more than one page holds (measured: four doors beat
who: who comes through it # ten offers on one page; six offers on one page beat doors)
groups: # groups with a label the public may see: the committees
- { id: traffic, label: Traffic, invited_by: [board] } # a person can ask to join (optional)
invites_by: [board] # groups whose desk may invite people into every desk
needs:
- id: kebab-case # ASCII only
who: the kind of person, in plain words, in the intake's language
wants: what they want from the business
door: <door id> # the door this public need sits behind; omit to put it on the front door
enters_as: <group> # ONLY for a role inside the business; omit for the public
lands_in: <bucket> # snake_case, ASCII
handled_by: <group> # the role that must see and move the record
stages: [open, <stage>, ..., <end>, <end>]
# EVERY stage the business said a case goes through, first to
# last, read off their own sentences one verb at a time: "they
# come in, we go and look, we send a quote, we win or lose it"
# is [open, surveyed, quoted, won, lost]. A pipeline is not an
# inbox: "answered" is rarely the real outcome.
done_when: [<end>, <end>] # every stage that counts as finished, good or bad
success: [<end>] # the finished stages that are a good outcome for the business
skippable: [<stage>] # stages a case may pass over (papers already complete)
turned_down_from: [open, <stage>]
# 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: { <stage>: [<group>, ...] }
# only where someone other than handled_by moves a case INTO a
# stage: another group, or `submitter` (the person who sent the
# form; the form must then collect a required `email`)
watched_by: [<group>] # groups that see these cases and move nothing; `public` opens a
# read-only page of the bucket to everyone (contact details hidden)
mails: { open: submitter, <end>: submitter, <stage>: <address> }
# who hears when a case enters a stage: `submitter` (the form
# must then collect a required `email`) or one address. Tell the
# person on arrival and on every decision that is about them.
stages_text: { open: <words>, <stage>: <words> }
# each stage in the intake's language, where the ASCII id
# would read wrong to a person: "ikke_lost" is "ikke løst"
events: { open: applied } # ONLY when rebuilding a live site: the event names its
# aggregates.yaml already has, so its records keep replaying
attended_by: > # ONLY for a stage no desk button moves: the automation that does
what moves it, in a sentence
max_fields: 4 # decide this BEFORE writing collects, from what the intake
# says about the person; a budget that equals the form's size
# is counted, not decided, and is reported as such
# how many required fields THIS person will tolerate, from what
# the intake says about them: someone on a phone with gloves
# on gets 3, a first enquiry 4 to 6, a firm's paperwork 5 to 6
collects: # the form; name is snake_case ASCII, type one of
# text (default), email, tel, textarea, date, select
- { name: name }
- { name: phone, type: tel }
- { name: problem, type: textarea }
- { name: urgency, type: select, options: [This week, This month, No rush] }
- { name: can_do, type: select, multiple: true, options: [Drive, Cook, Carry] } # any number
- { name: committee, type: select, options_from: groups } # the labelled groups above
- { name: address, optional: true, private: true } # never on a public page
```
Rules:
- One need per (kind of person, thing they want). Work inside the
business counts: a role recording or posting something is a need
with `enters_as`. The intake's "what slows us down" usually names one.
- Groups are the roles the intake names, as snake_case identifiers.
- The front door opens with `does` (at most two features, one role
each) and `shows`, then holds at most seven offers: doors plus needs
with no door. A door with no need behind it, or a door on a need with
`enters_as`, is refused. A role `does` speaks for must have a move
of its own after sending (`moves_by` with `submitter`) or a public
view, or it is a spectator.
- Every need whose form collects an email should tell the person on
arrival and on the decisions about them (`mails`); the plan then
needs `mail: { from }`.
- Needs that land in the same bucket must have the same stages,
done_when, skippable, moves_by, watched_by and handled_by.
- Every required field is one the business will use to act on the
record. If the intake gives no reason to need it, make it optional or
leave it out. The required fields must not exceed max_fields.
- Only `select` lists `options`, and it must list them.
- Everything a person reads (who, wants, about, select options) is in
the intake's language. Everything a machine reads (ids, buckets,
groups, stages, field names) is plain ASCII: no æ, ø, å.
- Write no personas.
# Task
A business filled in this intake. Write its plan.yaml: every need the intake implies, for the public and for each role inside the business, with the stages read off how the business says things progress, the form each need collects, and how much each person will tolerate.
## Intake
# An intake as the owner of a small building contractor would fill it
# in: plain words, no portal vocabulary. These are the fields the
# onboarding site asks for.
business: Nordvik Bygg
contact: { name: Ingrid Nordvik, email: ingrid@nordvikbygg.example }
language: en
what_we_do: >
We are twelve people. We survey old buildings, draw up rehabilitation
plans, handle the permit application, and do the building work with
our own carpenters plus subcontractors for electrical, plumbing and
scaffolding.
who_comes_to_us: >
Housing boards and private owners who have a problem with a building
and want someone to look at it. Owners who already know what they
want built and need drawings, a permit and a price. Trade firms who
want to be on our list of subcontractors. Carpenters and apprentices
asking if we are hiring.
what_we_keep_track_of: >
Enquiries: they come in, we go and look at the building, we send a
quote, and then we either win it or lose it. Sometimes it is simply
not for us and we say so straight away.
Subcontractors: they apply, we check their company registration,
trade approval and insurance. Often papers are missing and we have to
ask again. Then they are approved or turned down.
Job applications: we read them, invite some to an interview, and hire
or decline.
Things that go wrong on site: a hazard, a near miss, wrong materials
delivered. Someone reports it, the safety officer decides what is
done about it, and closes it when it is fixed.
who_is_involved: >
Two project managers handle enquiries, subcontractors and hiring.
One safety officer handles everything reported from site.
The crew, ours and the subcontractors' people, are the ones on site
who report things. They are on their phones, with gloves on, and
will not fill in a long form.
what_slows_us_down: >
Enquiries arrive by phone, email and text and get lost. We never know
which subcontractors have valid papers. Site problems get mentioned
at lunch and forgotten.
## Your last attempt parsed, with one thing to fix
- the opener invites a housing board or private owner with a problem with a building in, and after sending they play nothing: give need "enquiry" a move of theirs (moves_by: {<stage>: [submitter, ...]}) or a public view (watched_by: [public])
- the opener invites a trade firm that wants to be on the subcontractor list in, and after sending they play nothing: give need "subcontractor-application" a move of theirs (moves_by: {<stage>: [submitter, ...]}) or a public view (watched_by: [public])
Give each such need a move the person makes themselves (`moves_by: {<stage>: [submitter, <group>]}`, with a required `email` in its form) or `watched_by: [public]`, and return the whole plan again.
```yaml
business: Nordvik Bygg
language: en
contact: { name: Ingrid Nordvik, email: ingrid@nordvikbygg.example }
about: >
We survey old buildings, draw up rehabilitation plans, handle the
permit application, and do the building work with our own carpenters
plus subcontractors for electrical, plumbing and scaffolding.
mail: { from: "Nordvik Bygg <post@nordvikbygg.example>", reply_to: ingrid@nordvikbygg.example }
does:
- id: survey-and-build
for: enquiry
text: We survey old buildings, draw up rehabilitation plans, handle the permit, and build.
- id: subcontractor-network
for: subcontractor-application
text: We work with subcontractors for electrical, plumbing and scaffolding.
doors:
- id: work
who: housing boards and owners with a building problem or a project in mind
- id: join
who: trade firms, carpenters and apprentices who want to work with us
groups:
- { id: project_managers, label: Project managers }
- { id: safety_officer, label: Safety officer }
- { id: crew, label: Crew }
needs:
- id: enquiry
who: a housing board or private owner with a problem with a building
wants: someone to look at the building and quote the work
door: work
lands_in: enquiries
handled_by: project_managers
stages: [open, surveyed, quoted, won, lost]
done_when: [won, lost]
success: [won]
turned_down_from: [open, surveyed]
mails: { open: submitter, won: submitter, lost: submitter }
max_fields: 4
collects:
- { name: name }
- { name: email, type: email }
- { name: phone, type: tel }
- { name: problem, type: textarea }
- id: subcontractor-application
who: a trade firm that wants to be on the subcontractor list
wants: to be approved as a subcontractor for electrical, plumbing or scaffolding work
door: join
lands_in: subcontractor_applications
handled_by: project_managers
stages: [open, checking_papers, approved, turned_down]
done_when: [approved, turned_down]
success: [approved]
skippable: [checking_papers]
turned_down_from: [open, checking_papers]
mails: { open: submitter, approved: submitter, turned_down: submitter }
max_fields: 5
collects:
- { name: company_name }
- { name: email, type: email }
- { name: trade, type: select, options: [Electrical, Plumbing, Scaffolding] }
- { name: registration_number }
- { name: insurance_details, type: textarea }
- id: job-application
who: a carpenter or apprentice asking if we are hiring
wants: to be considered for a job and invited to interview
door: join
lands_in: job_applications
handled_by: project_managers
stages: [open, interview, hired, declined]
done_when: [hired, declined]
success: [hired]
skippable: [interview]
turned_down_from: [open, interview]
mails: { open: submitter, interview: submitter, hired: submitter, declined: submitter }
max_fields: 4
collects:
- { name: name }
- { name: email, type: email }
- { name: role, type: select, options: [Carpenter, Apprentice] }
- { name: experience, type: textarea, optional: true }
- id: site-report
who: crew, ours and the subcontractors' people, on site
wants: to report a hazard, near miss or wrong delivery so it gets fixed
enters_as: crew
lands_in: site_reports
handled_by: safety_officer
stages: [open, in_progress, closed]
done_when: [closed]
success: [closed]
mails: { closed: submitter }
max_fields: 3
collects:
- { name: email, type: email }
- { name: issue_type, type: select, options: [Hazard, Near miss, Wrong materials] }
- { name: description, type: textarea }
```
Reply with the YAML only, no prose, no code fences.
@@ -0,0 +1,105 @@
```yaml
business: Nordvik Bygg
language: en
contact: { name: Ingrid Nordvik, email: ingrid@nordvikbygg.example }
about: >
We survey old buildings, draw up rehabilitation plans, handle the
permit application, and do the building work with our own carpenters
plus subcontractors for electrical, plumbing and scaffolding.
mail: { from: "Nordvik Bygg <post@nordvikbygg.example>", reply_to: ingrid@nordvikbygg.example }
does:
- id: survey-and-build
for: enquiry
text: We survey old buildings, draw up rehabilitation plans, handle the permit, and build.
- id: subcontractor-network
for: subcontractor-application
text: We work with subcontractors for electrical, plumbing and scaffolding.
doors:
- id: work
who: housing boards and owners with a building problem or a project in mind
- id: join
who: trade firms, carpenters and apprentices who want to work with us
groups:
- { id: project_managers, label: Project managers }
- { id: safety_officer, label: Safety officer }
- { id: crew, label: Crew }
needs:
- id: enquiry
who: a housing board or private owner with a problem with a building
wants: someone to look at the building and quote the work
door: work
lands_in: enquiries
handled_by: project_managers
stages: [open, surveyed, quoted, won, lost]
done_when: [won, lost]
success: [won]
turned_down_from: [open, surveyed]
moves_by: { lost: [submitter, project_managers] }
mails: { open: submitter, won: submitter, lost: submitter }
max_fields: 4
collects:
- { name: name }
- { name: email, type: email }
- { name: phone, type: tel }
- { name: problem, type: textarea }
- id: subcontractor-application
who: a trade firm that wants to be on the subcontractor list
wants: to be approved as a subcontractor for electrical, plumbing or scaffolding work
door: join
lands_in: subcontractor_applications
handled_by: project_managers
stages: [open, checking_papers, approved, turned_down]
done_when: [approved, turned_down]
success: [approved]
skippable: [checking_papers]
turned_down_from: [open, checking_papers]
moves_by: { turned_down: [submitter, project_managers] }
mails: { open: submitter, approved: submitter, turned_down: submitter }
max_fields: 5
collects:
- { name: company_name }
- { name: email, type: email }
- { name: trade, type: select, options: [Electrical, Plumbing, Scaffolding] }
- { name: registration_number }
- { name: insurance_details, type: textarea }
- id: job-application
who: a carpenter or apprentice asking if we are hiring
wants: to be considered for a job and invited to interview
door: join
lands_in: job_applications
handled_by: project_managers
stages: [open, interview, hired, declined]
done_when: [hired, declined]
success: [hired]
skippable: [interview]
turned_down_from: [open, interview]
mails: { open: submitter, interview: submitter, hired: submitter, declined: submitter }
max_fields: 4
collects:
- { name: name }
- { name: email, type: email }
- { name: role, type: select, options: [Carpenter, Apprentice] }
- { name: experience, type: textarea, optional: true }
- id: site-report
who: crew, ours and the subcontractors' people, on site
wants: to report a hazard, near miss or wrong delivery so it gets fixed
enters_as: crew
lands_in: site_reports
handled_by: safety_officer
stages: [open, in_progress, closed]
done_when: [closed]
success: [closed]
mails: { closed: submitter }
max_fields: 3
collects:
- { name: email, type: email }
- { name: issue_type, type: select, options: [Hazard, Near miss, Wrong materials] }
- { name: description, type: textarea }
```
@@ -0,0 +1,146 @@
business: Nordvik Bygg
language: en
contact: { name: Ingrid Nordvik, email: ingrid@nordvikbygg.example }
about: >
We survey old buildings, draw up rehabilitation plans, handle the
permit application, and do the building work with our own carpenters
plus subcontractors for electrical, plumbing and scaffolding.
mail: { from: "Nordvik Bygg <post@nordvikbygg.example>", reply_to: ingrid@nordvikbygg.example }
does:
- id: survey-and-build
for: enquiry
text: We survey old buildings, draw up rehabilitation plans, handle the permit, and build.
- id: subcontractor-network
for: subcontractor-application
text: We work with subcontractors for electrical, plumbing and scaffolding.
doors:
- id: work
who: housing boards and owners with a building problem or a project in mind
- id: join
who: trade firms, carpenters and apprentices who want to work with us
groups:
- { id: project_managers, label: Project managers }
- { id: safety_officer, label: Safety officer }
- { id: crew, label: Crew }
needs:
- id: enquiry
who: a housing board or private owner with a problem with a building
wants: someone to look at the building and quote the work
door: work
lands_in: enquiries
handled_by: project_managers
stages: [open, surveyed, quoted, won, lost]
done_when: [won, lost]
success: [won]
turned_down_from: [open, surveyed]
moves_by: { lost: [submitter, project_managers] }
mails: { open: submitter, won: submitter, lost: submitter }
max_fields: 4
collects:
- { name: name }
- { name: email, type: email }
- { name: phone, type: tel }
- { name: problem, type: textarea }
- id: subcontractor-application
who: a trade firm that wants to be on the subcontractor list
wants: to be approved as a subcontractor for electrical, plumbing or scaffolding work
door: join
lands_in: subcontractor_applications
handled_by: project_managers
stages: [open, checking_papers, approved, turned_down]
done_when: [approved, turned_down]
success: [approved]
skippable: [checking_papers]
turned_down_from: [open, checking_papers]
moves_by: { turned_down: [submitter, project_managers] }
mails: { open: submitter, approved: submitter, turned_down: submitter }
max_fields: 5
collects:
- { name: company_name }
- { name: email, type: email }
- { name: trade, type: select, options: [Electrical, Plumbing, Scaffolding] }
- { name: registration_number }
- { name: insurance_details, type: textarea }
- id: job-application
who: a carpenter or apprentice asking if we are hiring
wants: to be considered for a job and invited to interview
door: join
lands_in: job_applications
handled_by: project_managers
stages: [open, interview, hired, declined]
done_when: [hired, declined]
success: [hired]
skippable: [interview]
turned_down_from: [open, interview]
mails: { open: submitter, interview: submitter, hired: submitter, declined: submitter }
max_fields: 4
collects:
- { name: name }
- { name: email, type: email }
- { name: role, type: select, options: [Carpenter, Apprentice] }
- { name: experience, type: textarea, optional: true }
- id: site-report
who: crew, ours and the subcontractors' people, on site
wants: to report a hazard, near miss or wrong delivery so it gets fixed
enters_as: crew
lands_in: site_reports
handled_by: safety_officer
stages: [open, in_progress, closed]
done_when: [closed]
success: [closed]
mails: { closed: submitter }
max_fields: 3
collects:
- { name: email, type: email }
- { name: issue_type, type: select, options: [Hazard, Near miss, Wrong materials] }
- { name: description, type: textarea }
# Held out from every model that writes words.
personas:
- id: housing-board-damp
need: enquiry
text: |
I chair the board of a housing cooperative from the seventies. Two ground-floor flats have damp in the walls and we don't know if it's drainage or the facade. We need someone to tell us.
- id: buyer-before-purchase
need: enquiry
text: |
We are about to buy an old timber house. Before we sign we want a professional to go through it and tell us what is wrong with it.
- id: attic-conversion
need: enquiry
text: |
We want to turn our attic into two bedrooms and a bathroom. We need drawings and someone who handles the application to the municipality.
- id: shop-to-flat
need: enquiry
text: |
I own a former shop on the ground floor and want to make it a flat. I have no idea what is allowed. Can you sort that out and give me a price?
- id: electrician-firm
need: subcontractor-application
text: |
We are a four-person electrical firm, fully certified. We would like to get on your list so you call us when you need electricians.
- id: scaffolder
need: subcontractor-application
text: |
I run a scaffolding company. How do I get to do jobs for you?
- id: carpenter-jobseeker
need: job-application
text: |
I'm a certified carpenter with eight years on rehabilitation jobs. Are you hiring?
- id: apprentice
need: job-application
text: |
I finish vocational school this spring and need an apprenticeship as a bricklayer.
- id: loose-railing
need: site-report
text: |
The railing on the third level of the scaffold on the north side is loose. Nobody got hurt but someone will.
- id: wrong-membrane
need: site-report
text: |
The bathroom membrane delivered today is not the one in the specification. We stopped work on it.
@@ -0,0 +1,178 @@
aggregates:
- bucket: enquiries
initial: open
states:
lost:
event: lost
mail:
body: |-
Hello {name},
What you sent about someone to look at the building and quote the work is now lost.
Ingrid Nordvik at ingrid@nordvikbygg.example
subject: 'Nordvik Bygg: Lost'
to: submitter
open:
event: received
mail:
body: |-
Hello {name},
We have received what you sent about someone to look at the building and quote the work.
What you can do next: {site}/decide/enquiry?chain={chain}&email={email}
Ingrid Nordvik at ingrid@nordvikbygg.example
subject: 'Nordvik Bygg: Open'
to: submitter
quoted:
event: quoted
surveyed:
event: surveyed
won:
event: won
mail:
body: |-
Hello {name},
What you sent about someone to look at the building and quote the work is now won.
Ingrid Nordvik at ingrid@nordvikbygg.example
subject: 'Nordvik Bygg: Won'
to: submitter
transitions:
open:
- surveyed
- lost
quoted:
- won
surveyed:
- quoted
- lost
- bucket: job_applications
initial: open
states:
declined:
event: declined
mail:
body: |-
Hello {name},
What you sent about to be considered for a job and invited to interview is now declined.
Ingrid Nordvik at ingrid@nordvikbygg.example
subject: 'Nordvik Bygg: Declined'
to: submitter
hired:
event: hired
mail:
body: |-
Hello {name},
What you sent about to be considered for a job and invited to interview is now hired.
Ingrid Nordvik at ingrid@nordvikbygg.example
subject: 'Nordvik Bygg: Hired'
to: submitter
interview:
event: interview
mail:
body: |-
Hello {name},
What you sent about to be considered for a job and invited to interview is now interview.
Ingrid Nordvik at ingrid@nordvikbygg.example
subject: 'Nordvik Bygg: Interview'
to: submitter
open:
event: received
mail:
body: |-
Hello {name},
We have received what you sent about to be considered for a job and invited to interview.
Ingrid Nordvik at ingrid@nordvikbygg.example
subject: 'Nordvik Bygg: Open'
to: submitter
transitions:
interview:
- hired
- declined
open:
- interview
- hired
- declined
- bucket: site_reports
initial: open
states:
closed:
event: closed
mail:
body: |-
Hello,
What you sent about to report a hazard, near miss or wrong delivery so it gets fixed is now closed.
Ingrid Nordvik at ingrid@nordvikbygg.example
subject: 'Nordvik Bygg: Closed'
to: submitter
in_progress:
event: in_progress
open:
event: received
transitions:
in_progress:
- closed
open:
- in_progress
- bucket: subcontractor_applications
initial: open
states:
approved:
event: approved
mail:
body: |-
Hello,
What you sent about to be approved as a subcontractor for electrical, plumbing or scaffolding work is now approved.
Ingrid Nordvik at ingrid@nordvikbygg.example
subject: 'Nordvik Bygg: Approved'
to: submitter
checking_papers:
event: checking_papers
open:
event: received
mail:
body: |-
Hello,
We have received what you sent about to be approved as a subcontractor for electrical, plumbing or scaffolding work.
What you can do next: {site}/decide/subcontractor-application?chain={chain}&email={email}
Ingrid Nordvik at ingrid@nordvikbygg.example
subject: 'Nordvik Bygg: Open'
to: submitter
turned_down:
event: turned_down
mail:
body: |-
Hello,
What you sent about to be approved as a subcontractor for electrical, plumbing or scaffolding work is now turned down.
Ingrid Nordvik at ingrid@nordvikbygg.example
subject: 'Nordvik Bygg: Turned down'
to: submitter
transitions:
checking_papers:
- approved
- turned_down
open:
- checking_papers
- approved
- turned_down
@@ -0,0 +1,138 @@
needs:
- done_when:
- won
- lost
enters: /
handled_by: project_managers
id: enquiry
lands_in: enquiries
max_fields: 4
max_steps: 2
stage_words:
lost: lost
open: open
quoted: quoted
surveyed: surveyed
won: won
stages:
- open
- surveyed
- quoted
- won
- lost
success:
- won
via: Work
wants: someone to look at the building and quote the work
who: a housing board or private owner with a problem with a building
- done_when:
- approved
- turned_down
enters: /
handled_by: project_managers
id: subcontractor-application
lands_in: subcontractor_applications
max_fields: 5
max_steps: 2
stage_words:
approved: approved
checking_papers: checking papers
open: open
turned_down: turned down
stages:
- open
- checking_papers
- approved
- turned_down
success:
- approved
via: Join
wants: to be approved as a subcontractor for electrical, plumbing or scaffolding work
who: a trade firm that wants to be on the subcontractor list
- done_when:
- hired
- declined
enters: /
handled_by: project_managers
id: job-application
lands_in: job_applications
max_fields: 4
max_steps: 2
stage_words:
declined: declined
hired: hired
interview: interview
open: open
stages:
- open
- interview
- hired
- declined
success:
- hired
via: Join
wants: to be considered for a job and invited to interview
who: a carpenter or apprentice asking if we are hiring
- done_when:
- closed
enters: /crew/site-report
enters_as: crew
handled_by: safety_officer
id: site-report
lands_in: site_reports
max_fields: 3
max_steps: 1
stage_words:
closed: closed
in_progress: in progress
open: open
stages:
- open
- in_progress
- closed
success:
- closed
via: to report a hazard, near miss or wrong delivery so it gets fixed
wants: to report a hazard, near miss or wrong delivery so it gets fixed
who: crew, ours and the subcontractors' people, on site
personas:
- id: housing-board-damp
need: enquiry
text: |
I chair the board of a housing cooperative from the seventies. Two ground-floor flats have damp in the walls and we don't know if it's drainage or the facade. We need someone to tell us.
- id: buyer-before-purchase
need: enquiry
text: |
We are about to buy an old timber house. Before we sign we want a professional to go through it and tell us what is wrong with it.
- id: attic-conversion
need: enquiry
text: |
We want to turn our attic into two bedrooms and a bathroom. We need drawings and someone who handles the application to the municipality.
- id: shop-to-flat
need: enquiry
text: |
I own a former shop on the ground floor and want to make it a flat. I have no idea what is allowed. Can you sort that out and give me a price?
- id: electrician-firm
need: subcontractor-application
text: |
We are a four-person electrical firm, fully certified. We would like to get on your list so you call us when you need electricians.
- id: scaffolder
need: subcontractor-application
text: |
I run a scaffolding company. How do I get to do jobs for you?
- id: carpenter-jobseeker
need: job-application
text: |
I'm a certified carpenter with eight years on rehabilitation jobs. Are you hiring?
- id: apprentice
need: job-application
text: |
I finish vocational school this spring and need an apprenticeship as a bricklayer.
- id: loose-railing
need: site-report
text: |
The railing on the third level of the scaffold on the north side is loose. Nobody got hurt but someone will.
- id: wrong-membrane
need: site-report
text: |
The bathroom membrane delivered today is not the one in the specification. We stopped work on it.
@@ -0,0 +1 @@
qualifies: crew
@@ -0,0 +1,44 @@
alternatives:
- action: /crew/site-report
consequence:
- Open
description: ''
name: to report a hazard, near miss or wrong delivery so it gets fixed
- action: /crew
consequence:
- Invite
description: They get an account, a place in Crew, and a one-time link to set their own password or passkey.
features:
- description: ''
name: Who
requirements:
- label: Name
name: name
- label: Email
name: email
type: email
invite:
group: crew
name: Invite someone into Crew
- action: /crew
consequence:
- Confirm
description: Each invite with the link it made. Mark it done when the person is in.
features:
- description: ''
name: ''
resource:
requires_group: crew
source:
bucket: portal_invites
kind: kv
transitions:
- from: open
label: Is in
to: done
name: Invitations sent
description: ''
name: What needs doing?
responsible:
contact: ingrid@nordvikbygg.example
name: Ingrid Nordvik
@@ -0,0 +1,10 @@
alternatives:
- description: Questions in the meantime go to Ingrid Nordvik at ingrid@nordvikbygg.example.
name: We read it and come back to you
- action: /crew
consequence:
- Back
description: ''
name: Back
description: It has reached our safety officer.
name: What happens now?
@@ -0,0 +1,35 @@
alternatives:
- action: /crew/site-report-sent
consequence:
- Send
description: For crew, ours and the subcontractors' people, on site.
features:
- description: It is open, then in progress, and ends closed.
icon: lucide:route
name: What happens next
- description: Safety officer. Questions go to Ingrid Nordvik at ingrid@nordvikbygg.example.
icon: lucide:users
name: Who reads it
- name: Details
requirements:
- label: Email
name: email
type: email
- label: Issue type
name: issue_type
options:
- Hazard
- Near miss
- Wrong materials
type: select
- label: Description
name: description
type: textarea
name: to report a hazard, near miss or wrong delivery so it gets fixed
record_as: site_reports
description: ''
followup: false
name: Site report?
responsible:
contact: ingrid@nordvikbygg.example
name: Ingrid Nordvik
@@ -0,0 +1,9 @@
alternatives:
- description: ''
name: Lost
self_transition:
bucket: enquiries
label: Lost
to: lost
description: ''
name: What would you like to do?
@@ -0,0 +1,9 @@
alternatives:
- description: ''
name: Turned down
self_transition:
bucket: subcontractor_applications
label: Turned down
to: turned_down
description: ''
name: What would you like to do?
@@ -0,0 +1,24 @@
alternatives:
- description: ''
features:
- description: We work with subcontractors for electrical, plumbing and scaffolding.
name: To be approved as a subcontractor for electrical, plumbing or scaffolding work
- description: We survey old buildings, draw up rehabilitation plans, handle the permit, and build.
name: Someone to look at the building and quote the work
name: What we do
- action: /work
consequence:
- Continue
description: For housing boards and owners with a building problem or a project in mind.
name: Work
- action: /join
consequence:
- Continue
description: For trade firms, carpenters and apprentices who want to work with us.
name: Join
description: |
We survey old buildings, draw up rehabilitation plans, handle the permit application, and do the building work with our own carpenters plus subcontractors for electrical, plumbing and scaffolding.
name: What brings you here?
responsible:
contact: ingrid@nordvikbygg.example
name: Ingrid Nordvik
@@ -0,0 +1,71 @@
alternatives:
- action: /received/subcontractor-application
consequence:
- Send
description: For a trade firm that wants to be on the subcontractor list.
features:
- description: It is open, then checking papers, and ends approved or turned down.
icon: lucide:route
name: What happens next
- description: Project managers. Questions go to Ingrid Nordvik at ingrid@nordvikbygg.example.
icon: lucide:users
name: Who reads it
- name: Details
requirements:
- label: Company name
name: company_name
type: text
- label: Email
name: email
type: email
- label: Trade
name: trade
options:
- Electrical
- Plumbing
- Scaffolding
type: select
- label: Registration number
name: registration_number
type: text
- label: Insurance details
name: insurance_details
type: textarea
name: to be approved as a subcontractor for electrical, plumbing or scaffolding work
record_as: subcontractor_applications
- action: /received/job-application
consequence:
- Send
description: For a carpenter or apprentice asking if we are hiring.
features:
- description: It is open, then interview, and ends hired or declined.
icon: lucide:route
name: What happens next
- description: Project managers. Questions go to Ingrid Nordvik at ingrid@nordvikbygg.example.
icon: lucide:users
name: Who reads it
- name: Details
requirements:
- label: Name
name: name
type: text
- label: Email
name: email
type: email
- label: Role
name: role
options:
- Carpenter
- Apprentice
type: select
- label: Experience
name: experience
optional: true
type: textarea
name: to be considered for a job and invited to interview
record_as: job_applications
description: For trade firms, carpenters and apprentices who want to work with us.
name: What do you need?
responsible:
contact: ingrid@nordvikbygg.example
name: Ingrid Nordvik
@@ -0,0 +1 @@
qualifies: project_managers
@@ -0,0 +1,126 @@
alternatives:
- action: /project_managers
consequence:
- Confirm
description: ''
features:
- description: ''
name: ''
resource:
requires_group: project_managers
source:
bucket: enquiries
kind: kv
transitions:
- from: open
label: Surveyed
to: surveyed
- from: open
label: Lost
to: lost
- from: quoted
label: Won
to: won
- from: surveyed
label: Quoted
to: quoted
- from: surveyed
label: Lost
to: lost
name: Enquiries
- action: /project_managers
consequence:
- Confirm
description: ''
features:
- description: ''
name: ''
resource:
requires_group: project_managers
source:
bucket: job_applications
kind: kv
transitions:
- from: interview
label: Hired
to: hired
- from: interview
label: Declined
to: declined
- from: open
label: Interview
to: interview
- from: open
label: Hired
to: hired
- from: open
label: Declined
to: declined
name: Job applications
- action: /project_managers
consequence:
- Confirm
description: ''
features:
- description: ''
name: ''
resource:
requires_group: project_managers
source:
bucket: subcontractor_applications
kind: kv
transitions:
- from: checking_papers
label: Approved
to: approved
- from: checking_papers
label: Turned down
to: turned_down
- from: open
label: Checking papers
to: checking_papers
- from: open
label: Approved
to: approved
- from: open
label: Turned down
to: turned_down
name: Subcontractor applications
- action: /project_managers
consequence:
- Invite
description: They get an account, a place in Project managers, and a one-time link to set their own password or passkey.
features:
- description: ''
name: Who
requirements:
- label: Name
name: name
- label: Email
name: email
type: email
invite:
group: project_managers
name: Invite someone into Project managers
- action: /project_managers
consequence:
- Confirm
description: Each invite with the link it made. Mark it done when the person is in.
features:
- description: ''
name: ''
resource:
requires_group: project_managers
source:
bucket: portal_invites
kind: kv
transitions:
- from: open
label: Is in
to: done
name: Invitations sent
description: ''
name: What needs doing?
responsible:
contact: ingrid@nordvikbygg.example
name: Ingrid Nordvik
@@ -0,0 +1,10 @@
alternatives:
- description: Questions in the meantime go to Ingrid Nordvik at ingrid@nordvikbygg.example.
name: We read it and come back to you
- action: /
consequence:
- Back
description: ''
name: Back
description: It has reached our project managers.
name: What happens now?
@@ -0,0 +1,10 @@
alternatives:
- description: Questions in the meantime go to Ingrid Nordvik at ingrid@nordvikbygg.example.
name: We read it and come back to you
- action: /
consequence:
- Back
description: ''
name: Back
description: It has reached our project managers.
name: What happens now?
@@ -0,0 +1,10 @@
alternatives:
- description: Questions in the meantime go to Ingrid Nordvik at ingrid@nordvikbygg.example.
name: We read it and come back to you
- action: /
consequence:
- Back
description: ''
name: Back
description: It has reached our project managers.
name: What happens now?
@@ -0,0 +1 @@
qualifies: safety_officer
@@ -0,0 +1,59 @@
alternatives:
- action: /safety_officer
consequence:
- Confirm
description: ''
features:
- description: ''
name: ''
resource:
requires_group: safety_officer
source:
bucket: site_reports
kind: kv
transitions:
- from: in_progress
label: Closed
to: closed
- from: open
label: In progress
to: in_progress
name: Site reports
- action: /safety_officer
consequence:
- Invite
description: They get an account, a place in Safety officer, and a one-time link to set their own password or passkey.
features:
- description: ''
name: Who
requirements:
- label: Name
name: name
- label: Email
name: email
type: email
invite:
group: safety_officer
name: Invite someone into Safety officer
- action: /safety_officer
consequence:
- Confirm
description: Each invite with the link it made. Mark it done when the person is in.
features:
- description: ''
name: ''
resource:
requires_group: safety_officer
source:
bucket: portal_invites
kind: kv
transitions:
- from: open
label: Is in
to: done
name: Invitations sent
description: ''
name: What needs doing?
responsible:
contact: ingrid@nordvikbygg.example
name: Ingrid Nordvik
@@ -0,0 +1,33 @@
alternatives:
- action: /received/enquiry
consequence:
- Send
description: For a housing board or private owner with a problem with a building.
features:
- description: It is open, then surveyed, then quoted, and ends won or lost.
icon: lucide:route
name: What happens next
- description: Project managers. Questions go to Ingrid Nordvik at ingrid@nordvikbygg.example.
icon: lucide:users
name: Who reads it
- name: Details
requirements:
- label: Name
name: name
type: text
- label: Email
name: email
type: email
- label: Phone
name: phone
type: tel
- label: Problem
name: problem
type: textarea
name: someone to look at the building and quote the work
record_as: enquiries
description: For housing boards and owners with a building problem or a project in mind.
name: What do you need?
responsible:
contact: ingrid@nordvikbygg.example
name: Ingrid Nordvik
@@ -0,0 +1,5 @@
lang: en
mail:
from: Nordvik Bygg <post@nordvikbygg.example>
reply_to: ingrid@nordvikbygg.example
title: Nordvik Bygg
@@ -0,0 +1,391 @@
{
"aggregates.yaml": {
"enquiries": {
"lost": {
"body": "Hello {name},\n\nWhat you sent about someone to look at the building and quote the work is now lost.\n\nIngrid Nordvik at ingrid@nordvikbygg.example",
"subject": "Nordvik Bygg: Lost"
},
"open": {
"body": "Hello {name},\n\nWe have received what you sent about someone to look at the building and quote the work.\n\nWhat you can do next: {site}/decide/enquiry?chain={chain}&email={email}\n\nIngrid Nordvik at ingrid@nordvikbygg.example",
"subject": "Nordvik Bygg: Open"
},
"won": {
"body": "Hello {name},\n\nWhat you sent about someone to look at the building and quote the work is now won.\n\nIngrid Nordvik at ingrid@nordvikbygg.example",
"subject": "Nordvik Bygg: Won"
}
},
"job_applications": {
"declined": {
"body": "Hello {name},\n\nWhat you sent about to be considered for a job and invited to interview is now declined.\n\nIngrid Nordvik at ingrid@nordvikbygg.example",
"subject": "Nordvik Bygg: Declined"
},
"hired": {
"body": "Hello {name},\n\nWhat you sent about to be considered for a job and invited to interview is now hired.\n\nIngrid Nordvik at ingrid@nordvikbygg.example",
"subject": "Nordvik Bygg: Hired"
},
"interview": {
"body": "Hello {name},\n\nWhat you sent about to be considered for a job and invited to interview is now interview.\n\nIngrid Nordvik at ingrid@nordvikbygg.example",
"subject": "Nordvik Bygg: Interview"
},
"open": {
"body": "Hello {name},\n\nWe have received what you sent about to be considered for a job and invited to interview.\n\nIngrid Nordvik at ingrid@nordvikbygg.example",
"subject": "Nordvik Bygg: Open"
}
},
"site_reports": {
"closed": {
"body": "Hello,\n\nWhat you sent about to report a hazard, near miss or wrong delivery so it gets fixed is now closed.\n\nIngrid Nordvik at ingrid@nordvikbygg.example",
"subject": "Nordvik Bygg: Closed"
}
},
"subcontractor_applications": {
"approved": {
"body": "Hello,\n\nWhat you sent about to be approved as a subcontractor for electrical, plumbing or scaffolding work is now approved.\n\nIngrid Nordvik at ingrid@nordvikbygg.example",
"subject": "Nordvik Bygg: Approved"
},
"open": {
"body": "Hello,\n\nWe have received what you sent about to be approved as a subcontractor for electrical, plumbing or scaffolding work.\n\nWhat you can do next: {site}/decide/subcontractor-application?chain={chain}&email={email}\n\nIngrid Nordvik at ingrid@nordvikbygg.example",
"subject": "Nordvik Bygg: Open"
},
"turned_down": {
"body": "Hello,\n\nWhat you sent about to be approved as a subcontractor for electrical, plumbing or scaffolding work is now turned down.\n\nIngrid Nordvik at ingrid@nordvikbygg.example",
"subject": "Nordvik Bygg: Turned down"
}
}
},
"questions/crew/index.yaml": {
"buckets": {},
"confirm": "Confirm",
"description": "",
"invited": {
"description": "Each invite with the link it made. Mark it done when the person is in.",
"done": "Is in",
"heading": "Invitations sent"
},
"invites": {
"crew": {
"button": "Invite",
"description": "They get an account, a place in Crew, and a one-time link to set their own password or passkey.",
"form_title": "Who",
"heading": "Invite someone into Crew",
"labels": {
"email": "Email",
"name": "Name"
}
}
},
"links": {
"site-report": {
"button": "Open",
"description": "",
"heading": "to report a hazard, near miss or wrong delivery so it gets fixed"
}
},
"question": "What needs doing?"
},
"questions/crew/site-report-sent.yaml": {
"back": "Back",
"description": "It has reached our safety officer.",
"next_description": "Questions in the meantime go to Ingrid Nordvik at ingrid@nordvikbygg.example.",
"next_heading": "We read it and come back to you",
"question": "What happens now?"
},
"questions/crew/site-report.yaml": {
"button": "Send",
"description": "For crew, ours and the subcontractors' people, on site.",
"encouragement": "",
"form_description": "",
"form_title": "Details",
"heading": "to report a hazard, near miss or wrong delivery so it gets fixed",
"hints": {
"description": "",
"email": "",
"issue_type": ""
},
"icon": "",
"labels": {
"description": "Description",
"email": "Email",
"issue_type": "Issue type"
},
"page_description": "",
"question": "Site report?",
"road_description": "It is open, then in progress, and ends closed.",
"road_icon": "route",
"road_title": "What happens next",
"who_description": "Safety officer. Questions go to Ingrid Nordvik at ingrid@nordvikbygg.example.",
"who_icon": "users",
"who_title": "Who reads it"
},
"questions/decide/enquiry.yaml": {
"description": "",
"moves": {
"lost": {
"button": "Lost",
"description": "",
"heading": "Lost"
}
},
"question": "What would you like to do?"
},
"questions/decide/subcontractor-application.yaml": {
"description": "",
"moves": {
"turned_down": {
"button": "Turned down",
"description": "",
"heading": "Turned down"
}
},
"question": "What would you like to do?"
},
"questions/index.yaml": {
"description": "We survey old buildings, draw up rehabilitation plans, handle the permit application, and do the building work with our own carpenters plus subcontractors for electrical, plumbing and scaffolding.\n",
"doors": {
"join": {
"button": "Continue",
"description": "For trade firms, carpenters and apprentices who want to work with us.",
"for_whom": "",
"heading": "Join",
"icon": ""
},
"work": {
"button": "Continue",
"description": "For housing boards and owners with a building problem or a project in mind.",
"for_whom": "",
"heading": "Work",
"icon": ""
}
},
"needs": {},
"opener": {
"description": "",
"features": {
"subcontractor-network": {
"description": "We work with subcontractors for electrical, plumbing and scaffolding.",
"heading": "To be approved as a subcontractor for electrical, plumbing or scaffolding work",
"icon": ""
},
"survey-and-build": {
"description": "We survey old buildings, draw up rehabilitation plans, handle the permit, and build.",
"heading": "Someone to look at the building and quote the work",
"icon": ""
}
},
"heading": "What we do"
},
"question": "What brings you here?",
"shopping": {
"description": "",
"heading": "Have a look around",
"items": {}
}
},
"questions/join/index.yaml": {
"description": "For trade firms, carpenters and apprentices who want to work with us.",
"needs": {
"job-application": {
"button": "Send",
"description": "For a carpenter or apprentice asking if we are hiring.",
"encouragement": "",
"form_description": "",
"form_title": "Details",
"heading": "to be considered for a job and invited to interview",
"hints": {
"email": "",
"experience": "",
"name": "",
"role": ""
},
"icon": "",
"labels": {
"email": "Email",
"experience": "Experience",
"name": "Name",
"role": "Role"
},
"road_description": "It is open, then interview, and ends hired or declined.",
"road_icon": "route",
"road_title": "What happens next",
"who_description": "Project managers. Questions go to Ingrid Nordvik at ingrid@nordvikbygg.example.",
"who_icon": "users",
"who_title": "Who reads it"
},
"subcontractor-application": {
"button": "Send",
"description": "For a trade firm that wants to be on the subcontractor list.",
"encouragement": "",
"form_description": "",
"form_title": "Details",
"heading": "to be approved as a subcontractor for electrical, plumbing or scaffolding work",
"hints": {
"company_name": "",
"email": "",
"insurance_details": "",
"registration_number": "",
"trade": ""
},
"icon": "",
"labels": {
"company_name": "Company name",
"email": "Email",
"insurance_details": "Insurance details",
"registration_number": "Registration number",
"trade": "Trade"
},
"road_description": "It is open, then checking papers, and ends approved or turned down.",
"road_icon": "route",
"road_title": "What happens next",
"who_description": "Project managers. Questions go to Ingrid Nordvik at ingrid@nordvikbygg.example.",
"who_icon": "users",
"who_title": "Who reads it"
}
},
"question": "What do you need?"
},
"questions/project_managers/index.yaml": {
"buckets": {
"enquiries": {
"description": "",
"heading": "Enquiries",
"moves": {
"open->lost": "Lost",
"open->surveyed": "Surveyed",
"quoted->won": "Won",
"surveyed->lost": "Lost",
"surveyed->quoted": "Quoted"
}
},
"job_applications": {
"description": "",
"heading": "Job applications",
"moves": {
"interview->declined": "Declined",
"interview->hired": "Hired",
"open->declined": "Declined",
"open->hired": "Hired",
"open->interview": "Interview"
}
},
"subcontractor_applications": {
"description": "",
"heading": "Subcontractor applications",
"moves": {
"checking_papers->approved": "Approved",
"checking_papers->turned_down": "Turned down",
"open->approved": "Approved",
"open->checking_papers": "Checking papers",
"open->turned_down": "Turned down"
}
}
},
"confirm": "Confirm",
"description": "",
"invited": {
"description": "Each invite with the link it made. Mark it done when the person is in.",
"done": "Is in",
"heading": "Invitations sent"
},
"invites": {
"project_managers": {
"button": "Invite",
"description": "They get an account, a place in Project managers, and a one-time link to set their own password or passkey.",
"form_title": "Who",
"heading": "Invite someone into Project managers",
"labels": {
"email": "Email",
"name": "Name"
}
}
},
"links": {},
"question": "What needs doing?"
},
"questions/received/enquiry.yaml": {
"back": "Back",
"description": "It has reached our project managers.",
"next_description": "Questions in the meantime go to Ingrid Nordvik at ingrid@nordvikbygg.example.",
"next_heading": "We read it and come back to you",
"question": "What happens now?"
},
"questions/received/job-application.yaml": {
"back": "Back",
"description": "It has reached our project managers.",
"next_description": "Questions in the meantime go to Ingrid Nordvik at ingrid@nordvikbygg.example.",
"next_heading": "We read it and come back to you",
"question": "What happens now?"
},
"questions/received/subcontractor-application.yaml": {
"back": "Back",
"description": "It has reached our project managers.",
"next_description": "Questions in the meantime go to Ingrid Nordvik at ingrid@nordvikbygg.example.",
"next_heading": "We read it and come back to you",
"question": "What happens now?"
},
"questions/safety_officer/index.yaml": {
"buckets": {
"site_reports": {
"description": "",
"heading": "Site reports",
"moves": {
"in_progress->closed": "Closed",
"open->in_progress": "In progress"
}
}
},
"confirm": "Confirm",
"description": "",
"invited": {
"description": "Each invite with the link it made. Mark it done when the person is in.",
"done": "Is in",
"heading": "Invitations sent"
},
"invites": {
"safety_officer": {
"button": "Invite",
"description": "They get an account, a place in Safety officer, and a one-time link to set their own password or passkey.",
"form_title": "Who",
"heading": "Invite someone into Safety officer",
"labels": {
"email": "Email",
"name": "Name"
}
}
},
"links": {},
"question": "What needs doing?"
},
"questions/work/index.yaml": {
"description": "For housing boards and owners with a building problem or a project in mind.",
"needs": {
"enquiry": {
"button": "Send",
"description": "For a housing board or private owner with a problem with a building.",
"encouragement": "",
"form_description": "",
"form_title": "Details",
"heading": "someone to look at the building and quote the work",
"hints": {
"email": "",
"name": "",
"phone": "",
"problem": ""
},
"icon": "",
"labels": {
"email": "Email",
"name": "Name",
"phone": "Phone",
"problem": "Problem"
},
"road_description": "It is open, then surveyed, then quoted, and ends won or lost.",
"road_icon": "route",
"road_title": "What happens next",
"who_description": "Project managers. Questions go to Ingrid Nordvik at ingrid@nordvikbygg.example.",
"who_icon": "users",
"who_title": "Who reads it"
}
},
"question": "What do you need?"
}
}
+27 -6
View File
@@ -112,10 +112,30 @@ fn english() -> String {
"en".to_string()
}
/// `moves_by: { checking: submitter }` is what a model writes for a
/// single mover, and means the same as `[submitter]`; a bare word or
/// a list are both read.
fn one_or_many_by_key<'de, D: serde::Deserializer<'de>>(d: D) -> Result<BTreeMap<String, Vec<String>>, D::Error> {
#[derive(Deserialize)]
#[serde(untagged)]
enum OneOrMany {
One(String),
Many(Vec<String>),
}
let raw: BTreeMap<String, OneOrMany> = BTreeMap::deserialize(d)?;
Ok(raw
.into_iter()
.map(|(k, v)| (k, match v { OneOrMany::One(s) => vec![s], OneOrMany::Many(l) => l }))
.collect())
}
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct GroupDef {
pub id: String,
/// The name the public knows the group by; empty means the id
/// made readable.
#[serde(default)]
pub label: String,
/// Groups that may invite people into this one, besides itself and
/// the plan's `invites_by`.
@@ -183,6 +203,7 @@ pub struct PlanNeed {
/// how roles overlap on one aggregate: a project manager prices a
/// change, the client approves it, a site manager completes it.
#[serde(default)]
#[serde(deserialize_with = "one_or_many_by_key")]
pub moves_by: BTreeMap<String, Vec<String>>,
/// Groups that see these cases on their desk and move nothing.
#[serde(default)]
@@ -306,8 +327,8 @@ impl Plan {
}
let mut labelled = BTreeSet::new();
for g in &self.groups {
if !is_id(&g.id) || !labelled.insert(&g.id) || g.label.trim().is_empty() {
bail!("group {:?}: id must be unique ASCII lowercase with - or _, and it needs a label", g.id);
if !is_id(&g.id) || !labelled.insert(&g.id) {
bail!("group {:?}: id must be unique ASCII lowercase with - or _", g.id);
}
}
for g in self.invites_by.iter().chain(self.groups.iter().flat_map(|g| g.invited_by.iter())) {
@@ -938,7 +959,7 @@ fn inviters(plan: &Plan, group: &str) -> BTreeSet<String> {
}
fn label_of(plan: &Plan, group: &str) -> String {
plan.groups.iter().find(|g| g.id == group).map(|g| g.label.clone()).unwrap_or_else(|| humanize(group))
plan.groups.iter().find(|g| g.id == group).map(|g| g.label.trim().to_string()).filter(|l| !l.is_empty()).unwrap_or_else(|| humanize(group))
}
/// Buckets the public may read, with the needs that fill them.
@@ -951,10 +972,10 @@ fn public_view_path(bucket: &str) -> String {
}
/// The options of a select: its own list, or the plan's labelled groups.
fn select_options<'a>(plan: &'a Plan, field: &'a Field) -> Vec<&'a str> {
fn select_options(plan: &Plan, field: &Field) -> Vec<String> {
match field.options_from.as_deref() {
Some("groups") => plan.groups.iter().map(|g| g.label.as_str()).collect(),
_ => field.options.iter().map(String::as_str).collect(),
Some("groups") => plan.groups.iter().map(|g| label_of(plan, &g.id)).collect(),
_ => field.options.clone(),
}
}
+12 -1
View File
@@ -50,7 +50,7 @@ doors: # a first question before the forms, only when the
who: who comes through it # ten offers on one page; six offers on one page beat doors)
groups: # groups with a label the public may see: the committees
- { id: traffic, label: Traffic, invited_by: [board] } # a person can ask to join (optional)
- { id: traffic, label: Traffic, invited_by: [board] } # a person can ask to join (optional; label defaults to the id)
invites_by: [board] # groups whose desk may invite people into every desk
needs:
@@ -163,6 +163,10 @@ pub fn run(argv: &[String]) -> anyhow::Result<()> {
std::fs::create_dir_all(out.join("llm"))?;
let mut feedback = String::new();
// A structural finding the builder would only warn about (a role
// the opener speaks for with nothing to play) goes back to the
// model once; a second time it is the reader's, not another round.
let mut spectators_sent = false;
for round in 1..=rounds {
let prompt = format!(
"{REFERENCE}\n\n# Task\n\nA business filled in this intake. Write its plan.yaml: every need the intake implies, for the public and for each role inside the business, with the stages read off how the business says things progress, the form each need collects, and how much each person will tolerate.\n\n## Intake\n\n{intake}\n{feedback}\nReply with the YAML only, no prose, no code fences."
@@ -172,6 +176,13 @@ pub fn run(argv: &[String]) -> anyhow::Result<()> {
std::fs::write(out.join(format!("llm/{round:02}-reply.md")), &reply)?;
let yaml = extract_yaml(&reply);
match check(&yaml) {
Ok(plan) if !plan.spectators().is_empty() && !spectators_sent && round < rounds => {
spectators_sent = true;
let findings = plan.spectators().join("\n- ");
eprintln!("plan round {round}: the plan parses, and a role the opener speaks for has no move after sending; asking once more");
feedback = format!("\n## Your last attempt parsed, with one thing to fix\n\n- {findings}\n\nGive each such need a move the person makes themselves (`moves_by: {{<stage>: [submitter, <group>]}}`, with a required `email` in its form) or `watched_by: [public]`, and return the whole plan again.\n\n```yaml\n{yaml}\n```\n");
continue;
}
Ok(plan) => {
let mut text = yaml.trim_end().to_string();
text.push('\n');