One binary: the lint and the trainer are commands of portal
Test / test (pull_request) Successful in 1m7s
Test / test (pull_request) Successful in 1m7s
portal serve runs a site; portal lint is what iris was; portal plan, build, report and the rest are what portal-trainer was. One source at one version, so the three never disagree about what content means. Both histories are merged in (subtree). The release attaches the bare binary for a content repo's CI. The trainer's plans, intakes, engines and reference prompt come along; its runs stay in the old repo. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GT37Z1Xtfd9pUuQtMTg6Yt
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
d0d1cbf23d
commit
96b3746643
@@ -0,0 +1,487 @@
|
||||
# A building authority, written by hand to find where the plan language
|
||||
# stops. 14 needs, 7 roles, 12 buckets, two needs sharing a bucket in
|
||||
# each direction (public and internal), a decision no case officer may
|
||||
# sign, a group outside the building that moves records and nothing
|
||||
# else, a stage a deadline moves rather than a person, and four things
|
||||
# the plan language cannot say at all - each named in the run's README
|
||||
# rather than smuggled past.
|
||||
business: Vestmark kommune - Building and Planning
|
||||
language: en
|
||||
contact: { name: Solveig Hauge, email: byggesak@vestmark.example }
|
||||
mail: { from: "Vestmark Building and Planning <byggesak@vestmark.example>", reply_to: byggesak@vestmark.example }
|
||||
about: >
|
||||
The building authority for twenty-one thousand people. Anything built,
|
||||
demolished, divided or changed in use is decided here: the drawings are
|
||||
checked against the zoning map, the neighbours and the other
|
||||
authorities have their say, and then the head of section signs. The
|
||||
same office inspects what is actually built, signs off finished work,
|
||||
and deals with building nobody applied for.
|
||||
|
||||
# Two features, two roles, no button. The authority exists for the
|
||||
# person who wants to build; the next one it serves is the neighbour
|
||||
# who finds out what is planned next door. Both have a move of their
|
||||
# own after sending: the applicant withdraws, the objector withdraws,
|
||||
# and both buckets are open to read.
|
||||
does:
|
||||
- id: we-decide-what-may-be-built
|
||||
for: small-works-application
|
||||
text: >
|
||||
Everything built in Vestmark is decided here. You send the drawings,
|
||||
the neighbours and the road, fire and heritage authorities are heard,
|
||||
and you get a written decision you can hold us to.
|
||||
- id: your-neighbours-case-is-readable
|
||||
for: objection
|
||||
text: >
|
||||
If a notice landed in your postbox, the case behind it is open to
|
||||
read here, and what you write to us has to be answered by name in
|
||||
the decision.
|
||||
|
||||
doors:
|
||||
- id: want-to-build
|
||||
who: an owner or a firm with something to build, divide or change
|
||||
- id: building-near-me
|
||||
who: someone who lives next to what is being built
|
||||
|
||||
groups:
|
||||
- { id: front_desk, label: Front desk }
|
||||
- { id: case_officers, label: Case officers }
|
||||
- { id: section_head, label: Head of section }
|
||||
- { id: inspectors, label: Inspectors }
|
||||
- { id: appeals_board, label: Appeals board, invited_by: [section_head] }
|
||||
- { id: agencies, label: Hearing bodies, invited_by: [case_officers, section_head] }
|
||||
- { id: approved_firms, label: Approved firms, invited_by: [section_head] }
|
||||
invites_by: [section_head, case_officers]
|
||||
|
||||
needs:
|
||||
# --- the public: something they want to build ----------------------
|
||||
- id: pre-application-meeting
|
||||
door: want-to-build
|
||||
who: someone with an idea for a building and no drawings yet
|
||||
wants: to hear what we will allow before paying an architect
|
||||
lands_in: meetings
|
||||
handled_by: case_officers
|
||||
stages: [open, scheduled, met, advised, cancelled]
|
||||
done_when: [advised, cancelled]
|
||||
success: [advised]
|
||||
# They can call it off themselves; the front desk does it when they ring.
|
||||
moves_by: { cancelled: [submitter, front_desk] }
|
||||
watched_by: [front_desk]
|
||||
mails: { open: submitter, scheduled: submitter, advised: submitter }
|
||||
stages_text: { met: we have met, advised: written advice sent }
|
||||
max_fields: 5
|
||||
collects:
|
||||
- { name: name }
|
||||
- { name: email, type: email }
|
||||
- { name: property_address }
|
||||
- { name: what_you_want, type: textarea }
|
||||
- { name: phone, type: tel, optional: true }
|
||||
- { name: site, type: location, optional: true }
|
||||
|
||||
- id: small-works-application
|
||||
door: want-to-build
|
||||
who: an owner applying for something small enough to file themselves
|
||||
wants: permission to build it, and to know where the case stands
|
||||
lands_in: applications
|
||||
handled_by: case_officers
|
||||
stages: [open, incomplete, complete, on_hearing, recommended, approved, approved_with_conditions, refused, withdrawn]
|
||||
done_when: [approved, approved_with_conditions, refused, withdrawn]
|
||||
success: [approved, approved_with_conditions]
|
||||
skippable: [incomplete]
|
||||
# A case is sent back for the drawing it was missing, and the
|
||||
# hearing turns things up that send it back again. It may be
|
||||
# withdrawn from anywhere, and refused only once we have seen
|
||||
# everything: not on the day it arrives.
|
||||
reached_from:
|
||||
incomplete: [open, complete, on_hearing]
|
||||
refused: [complete, on_hearing, recommended]
|
||||
# No case officer signs their own case: only the head of section
|
||||
# moves one into a decision. The applicant answers a request for
|
||||
# missing papers, and may withdraw right up to the signature.
|
||||
moves_by:
|
||||
complete: [submitter, case_officers]
|
||||
approved: [section_head]
|
||||
approved_with_conditions: [section_head]
|
||||
refused: [section_head]
|
||||
withdrawn: [submitter]
|
||||
watched_by: [public, front_desk, inspectors, section_head]
|
||||
mails:
|
||||
open: submitter
|
||||
incomplete: submitter
|
||||
on_hearing: submitter
|
||||
approved: submitter
|
||||
approved_with_conditions: submitter
|
||||
refused: submitter
|
||||
stages_text:
|
||||
incomplete: papers missing
|
||||
complete: papers complete
|
||||
on_hearing: out to the neighbours and the agencies
|
||||
recommended: recommendation written
|
||||
approved_with_conditions: approved on conditions
|
||||
max_fields: 6
|
||||
collects:
|
||||
- { name: email, type: email }
|
||||
- { name: property_address }
|
||||
- { name: property_number }
|
||||
- { name: what_is_planned, type: textarea }
|
||||
- { name: neighbours_notified, type: select, options: ["Yes", Not yet] }
|
||||
- { name: site, type: location, optional: true }
|
||||
- { name: drawings_url, optional: true }
|
||||
|
||||
- id: exemption-request
|
||||
door: want-to-build
|
||||
who: someone whose plans break a rule in the zoning map
|
||||
wants: to be allowed it anyway, with reasons on the record
|
||||
requires: An application already with us, and the case number from its receipt.
|
||||
lands_in: exemptions
|
||||
handled_by: case_officers
|
||||
stages: [open, county_asked, county_answered, recommended, granted, denied, withdrawn]
|
||||
done_when: [granted, denied, withdrawn]
|
||||
success: [granted]
|
||||
# The county answers from outside the building; nothing may be
|
||||
# granted before it has. The head of section signs, as for a permit.
|
||||
moves_by:
|
||||
county_answered: [agencies]
|
||||
granted: [section_head]
|
||||
denied: [section_head]
|
||||
withdrawn: [submitter]
|
||||
watched_by: [public, section_head]
|
||||
mails: { open: submitter, county_asked: submitter, granted: submitter, denied: submitter }
|
||||
stages_text: { county_asked: with the county, county_answered: the county has answered }
|
||||
max_fields: 4
|
||||
collects:
|
||||
- { name: email, type: email }
|
||||
- { name: case_number }
|
||||
- { name: which_rule }
|
||||
- { name: why_it_should_be_allowed, type: textarea }
|
||||
- { name: site, type: location, optional: true }
|
||||
|
||||
- id: completion-request
|
||||
door: want-to-build
|
||||
who: an owner or firm whose building work is finished
|
||||
wants: the certificate that lets the building be used
|
||||
requires: A case number, and the work finished or close enough to move in.
|
||||
lands_in: completions
|
||||
handled_by: case_officers
|
||||
stages: [open, checked, temporary_permission, certificate_issued, refused]
|
||||
done_when: [certificate_issued, refused]
|
||||
success: [certificate_issued]
|
||||
# Most go straight through; the ones who want to move in before the
|
||||
# last of it is done detour past a temporary permission.
|
||||
skippable: [temporary_permission]
|
||||
moves_by:
|
||||
temporary_permission: [section_head]
|
||||
certificate_issued: [section_head]
|
||||
refused: [section_head]
|
||||
watched_by: [public, inspectors]
|
||||
turned_down_from: [open, checked]
|
||||
mails: { open: submitter, temporary_permission: submitter, certificate_issued: submitter, refused: submitter }
|
||||
stages_text: { checked: checked against the inspections, temporary_permission: temporary permission to use, certificate_issued: certificate issued }
|
||||
max_fields: 5
|
||||
collects:
|
||||
- { name: email, type: email }
|
||||
- { name: case_number }
|
||||
- { name: property_address }
|
||||
- { name: work_finished, type: date }
|
||||
- { name: asking_for, type: select, options: [Completion certificate, Temporary permission to move in] }
|
||||
- { name: what_is_outstanding, type: textarea, optional: true }
|
||||
|
||||
# --- the public: someone else is building ---------------------------
|
||||
- id: objection
|
||||
door: building-near-me
|
||||
who: a neighbour who got a notice about what is planned next door
|
||||
wants: what they think on the record, and answered in the decision
|
||||
requires: The notice that came in the post, which has the case number on it.
|
||||
lands_in: objections
|
||||
handled_by: case_officers
|
||||
stages: [open, registered, answered_by_applicant, addressed, withdrawn]
|
||||
done_when: [addressed, withdrawn]
|
||||
success: [addressed]
|
||||
# The firm that filed the case answers the objection; the neighbour
|
||||
# can take it back if they are satisfied.
|
||||
moves_by: { answered_by_applicant: [approved_firms, case_officers], withdrawn: [submitter] }
|
||||
watched_by: [public, section_head]
|
||||
mails: { open: submitter, answered_by_applicant: submitter, addressed: submitter }
|
||||
stages_text: { registered: on the case file, answered_by_applicant: the applicant has answered, addressed: answered in the decision }
|
||||
max_fields: 4
|
||||
collects:
|
||||
- { name: email, type: email }
|
||||
- { name: case_number }
|
||||
- { name: your_property }
|
||||
- { name: your_objection, type: textarea }
|
||||
|
||||
- id: case-status-enquiry
|
||||
door: building-near-me
|
||||
who: someone waiting on a case and getting nothing back
|
||||
wants: to be told where it actually stands
|
||||
lands_in: enquiries
|
||||
handled_by: front_desk
|
||||
stages: [open, in_hand, answered]
|
||||
done_when: [answered]
|
||||
success: [answered]
|
||||
moves_by: { in_hand: [front_desk, case_officers] }
|
||||
watched_by: [case_officers]
|
||||
mails: { open: submitter, answered: submitter }
|
||||
max_fields: 3
|
||||
collects:
|
||||
- { name: email, type: email }
|
||||
- { name: case_number_or_address }
|
||||
- { name: what_you_want_to_know, type: textarea }
|
||||
|
||||
- id: illegal-building-report
|
||||
door: building-near-me
|
||||
who: someone who can see building going up that nobody was told about
|
||||
wants: us to go and look, and to hear that we did
|
||||
lands_in: illegal_building
|
||||
handled_by: inspectors
|
||||
stages: [open, inspected, owner_asked, explained, resolved, fined, dropped]
|
||||
done_when: [resolved, fined, dropped]
|
||||
success: [resolved, dropped]
|
||||
# An inspector goes and looks; the letter to the owner is a case
|
||||
# officer's, and so is the fine.
|
||||
moves_by: { owner_asked: [case_officers], explained: [case_officers], fined: [section_head] }
|
||||
watched_by: [case_officers, section_head]
|
||||
turned_down_from: [open, inspected]
|
||||
mails: { open: submitter, inspected: submitter, resolved: submitter, dropped: submitter }
|
||||
stages_text: { owner_asked: the owner has been written to, explained: the owner has answered, dropped: nothing to pursue }
|
||||
max_fields: 3
|
||||
collects:
|
||||
- { name: email, type: email }
|
||||
- { name: address }
|
||||
- { name: what_you_saw, type: textarea }
|
||||
- { name: where, type: location, optional: true }
|
||||
|
||||
# --- the public: after a decision ------------------------------------
|
||||
- id: appeal
|
||||
who: anyone with an interest who will not accept a decision of ours
|
||||
wants: the decision looked at again, by us and then by the board
|
||||
requires: A decision from us less than three weeks old, and its case number.
|
||||
lands_in: appeals
|
||||
handled_by: case_officers
|
||||
stages: [open, reconsidered, changed_by_us, before_the_board, upheld, overturned, withdrawn]
|
||||
done_when: [changed_by_us, upheld, overturned, withdrawn]
|
||||
success: [changed_by_us, upheld]
|
||||
# If we change our own mind that is the end of it, and it happens
|
||||
# while we are looking - not after the board has the case. The
|
||||
# board can only decide what has reached it.
|
||||
reached_from:
|
||||
changed_by_us: [reconsidered]
|
||||
upheld: [before_the_board]
|
||||
overturned: [before_the_board]
|
||||
# We look at ourselves first and the head of section decides whether
|
||||
# we were wrong. What we will not change goes to seven elected
|
||||
# people who meet once a month and move nothing else in this office.
|
||||
moves_by:
|
||||
changed_by_us: [section_head]
|
||||
before_the_board: [section_head]
|
||||
upheld: [appeals_board]
|
||||
overturned: [appeals_board]
|
||||
withdrawn: [submitter]
|
||||
watched_by: [public, front_desk, section_head]
|
||||
mails:
|
||||
open: submitter
|
||||
reconsidered: submitter
|
||||
changed_by_us: submitter
|
||||
before_the_board: submitter
|
||||
upheld: submitter
|
||||
overturned: submitter
|
||||
stages_text:
|
||||
reconsidered: we are looking at it again
|
||||
changed_by_us: we changed our own decision
|
||||
before_the_board: with the appeals board
|
||||
upheld: the board kept our decision
|
||||
overturned: the board overturned us
|
||||
max_fields: 5
|
||||
collects:
|
||||
- { name: email, type: email }
|
||||
- { name: case_number }
|
||||
- { name: property }
|
||||
- { name: what_you_disagree_with, type: textarea }
|
||||
- { name: your_interest, type: select, options: [I applied, I am a neighbour, Other] }
|
||||
|
||||
# --- the public: a firm that wants to file here -----------------------
|
||||
- id: firm-approval
|
||||
who: an architect or engineering firm that files for its clients
|
||||
wants: to be on the list, and then to file without asking again
|
||||
lands_in: firms
|
||||
handled_by: case_officers
|
||||
stages: [open, papers_missing, checked, approved, declined]
|
||||
done_when: [approved, declined]
|
||||
success: [approved]
|
||||
skippable: [papers_missing]
|
||||
moves_by: { checked: [submitter, case_officers], approved: [section_head], declined: [section_head] }
|
||||
watched_by: [section_head, front_desk]
|
||||
turned_down_from: [open, papers_missing, checked]
|
||||
mails: { open: submitter, papers_missing: submitter, approved: submitter, declined: submitter }
|
||||
stages_text: { papers_missing: waiting on papers, checked: papers checked }
|
||||
max_fields: 6
|
||||
collects:
|
||||
- { name: firm_name }
|
||||
- { name: org_number }
|
||||
- { name: email, type: email }
|
||||
- { name: qualified_for, type: select, multiple: true, options: [Design, Execution, Independent control, Site management] }
|
||||
- { name: insurance_expires, type: date }
|
||||
- { name: insurance_document, optional: true }
|
||||
|
||||
# --- approved firms ----------------------------------------------------
|
||||
- id: permit-application
|
||||
who: an approved firm filing on behalf of an owner
|
||||
wants: a decision inside twelve weeks, and to know when the clock stops
|
||||
enters_as: approved_firms
|
||||
lands_in: applications
|
||||
handled_by: case_officers
|
||||
stages: [open, incomplete, complete, on_hearing, recommended, approved, approved_with_conditions, refused, withdrawn]
|
||||
done_when: [approved, approved_with_conditions, refused, withdrawn]
|
||||
success: [approved, approved_with_conditions]
|
||||
skippable: [incomplete]
|
||||
# A case is sent back for the drawing it was missing, and the
|
||||
# hearing turns things up that send it back again. It may be
|
||||
# withdrawn from anywhere, and refused only once we have seen
|
||||
# everything: not on the day it arrives.
|
||||
reached_from:
|
||||
incomplete: [open, complete, on_hearing]
|
||||
refused: [complete, on_hearing, recommended]
|
||||
moves_by:
|
||||
complete: [submitter, case_officers]
|
||||
approved: [section_head]
|
||||
approved_with_conditions: [section_head]
|
||||
refused: [section_head]
|
||||
withdrawn: [submitter]
|
||||
watched_by: [public, front_desk, inspectors, section_head]
|
||||
mails:
|
||||
open: submitter
|
||||
incomplete: submitter
|
||||
on_hearing: submitter
|
||||
approved: submitter
|
||||
approved_with_conditions: submitter
|
||||
refused: submitter
|
||||
stages_text:
|
||||
incomplete: papers missing
|
||||
complete: papers complete
|
||||
on_hearing: out to the neighbours and the agencies
|
||||
recommended: recommendation written
|
||||
approved_with_conditions: approved on conditions
|
||||
max_fields: 6
|
||||
collects:
|
||||
- { name: email, type: email }
|
||||
- { name: property_address }
|
||||
- { name: property_number }
|
||||
- { name: what_is_planned, type: textarea }
|
||||
- { name: neighbours_notified, type: select, options: ["Yes", Not yet] }
|
||||
- { name: site, type: location, optional: true }
|
||||
- { name: drawings_url, optional: true }
|
||||
|
||||
# --- the front desk -----------------------------------------------------
|
||||
- id: log-counter-enquiry
|
||||
who: whoever is on the counter with a queue and a caller on the line
|
||||
wants: the question written down and sent to whoever has the case
|
||||
enters_as: front_desk
|
||||
lands_in: enquiries
|
||||
handled_by: front_desk
|
||||
stages: [open, in_hand, answered]
|
||||
done_when: [answered]
|
||||
success: [answered]
|
||||
moves_by: { in_hand: [front_desk, case_officers] }
|
||||
watched_by: [case_officers]
|
||||
mails: { open: submitter, answered: submitter }
|
||||
max_fields: 3
|
||||
collects:
|
||||
- { name: email, type: email }
|
||||
- { name: case_number_or_address }
|
||||
- { name: what_you_want_to_know, type: textarea }
|
||||
|
||||
# --- case officers ------------------------------------------------------
|
||||
- id: request-hearing
|
||||
who: a case officer sending a case out to the authority that must see it
|
||||
wants: an answer by the deadline, or a record that none came
|
||||
enters_as: case_officers
|
||||
lands_in: hearings
|
||||
handled_by: case_officers
|
||||
stages: [open, answered, no_answer, cancelled]
|
||||
done_when: [answered, no_answer, cancelled]
|
||||
success: [answered]
|
||||
# This is how an agency sees what it has been asked. Nobody presses
|
||||
# a button for the deadline passing.
|
||||
moves_by: { answered: [agencies] }
|
||||
watched_by: [agencies, section_head, front_desk]
|
||||
attended_by: >
|
||||
the six-week deadline: when it passes with nothing from the agency
|
||||
the case goes to no_answer and the application goes on without one
|
||||
stages_text: { no_answer: the deadline passed with no answer }
|
||||
max_fields: 4
|
||||
collects:
|
||||
- { name: case_number }
|
||||
- { name: which_authority, type: select, options: [Road authority, Fire service, Heritage office, The county] }
|
||||
- { name: what_we_ask, type: textarea }
|
||||
- { name: answer_by, type: date }
|
||||
- { name: site, type: location, optional: true }
|
||||
|
||||
# --- the authorities outside the building --------------------------------
|
||||
- id: hearing-answer
|
||||
who: a road, fire, heritage or county officer who has been asked about a case
|
||||
wants: their answer on the file, in their own words
|
||||
requires: The hearing letter we sent you, which has the case number on it.
|
||||
enters_as: agencies
|
||||
lands_in: hearing_answers
|
||||
handled_by: case_officers
|
||||
stages: [open, read, followed, set_aside]
|
||||
done_when: [followed, set_aside]
|
||||
success: [followed]
|
||||
watched_by: [section_head, front_desk]
|
||||
stages_text: { read: read by the case officer, followed: followed in the decision, set_aside: set aside with reasons }
|
||||
max_fields: 4
|
||||
collects:
|
||||
- { name: case_number }
|
||||
- { name: which_authority, type: select, options: [Road authority, Fire service, Heritage office, The county] }
|
||||
- { name: our_answer, type: select, options: [No objection, Objection, Conditions required] }
|
||||
- { name: what_we_say, type: textarea }
|
||||
|
||||
# --- inspectors -----------------------------------------------------------
|
||||
- id: inspection-finding
|
||||
who: an inspector on a scaffold with gloves on and the wind blowing
|
||||
wants: what they found logged before they climb down
|
||||
enters_as: inspectors
|
||||
lands_in: inspections
|
||||
handled_by: inspectors
|
||||
stages: [open, as_approved, deviation, put_right, escalated]
|
||||
done_when: [as_approved, put_right, escalated]
|
||||
success: [as_approved, put_right]
|
||||
# Anything that has to become a fine or a refusal leaves the
|
||||
# inspectors and becomes a case officer's problem.
|
||||
moves_by: { escalated: [case_officers, section_head] }
|
||||
watched_by: [case_officers, section_head]
|
||||
stages_text: { as_approved: built as approved, deviation: built differently, put_right: put right, escalated: handed to a case officer }
|
||||
max_fields: 3
|
||||
collects:
|
||||
- { name: case_number }
|
||||
- { name: site_address }
|
||||
- { name: what_i_found, type: textarea }
|
||||
- { name: where, type: location, optional: true }
|
||||
|
||||
# Held out from every model that writes words.
|
||||
# arriving with one real thing, in their own register: a homeowner who
|
||||
# has never read a regulation, a firm that files for a living, an
|
||||
# inspector on a roof in the wind, a politician reading a bundle.
|
||||
personas:
|
||||
- { id: garage-owner, need: pre-application-meeting, text: "We want to put a double garage where the old shed is. Before I pay an architect I'd like to know whether you'll even allow it, because the neighbour says it's too close to the road." }
|
||||
- { id: shop-to-flats, need: pre-application-meeting, text: "I own the old grocery on Torget and want to make four flats out of it. There are a lot of questions - parking, fire, the facade is listed. Can we sit down with someone?" }
|
||||
- { id: firm-files-extension, need: permit-application, text: "We are the responsible applicant for an extension at Bjorkeveien 14. Drawings, site plan and the neighbour notices are all ready. We file maybe forty of these a year." }
|
||||
- { id: owner-small-window, need: permit-application, text: "I want to put a dormer window in my roof. The man at the counter said I have to apply even though it's small." }
|
||||
- { id: missing-drawings, need: permit-application, text: "You wrote that our section drawing is missing and the case is on hold. I've got it here now, where do I send it so the clock starts again?" }
|
||||
- { id: too-close-to-boundary, need: exemption-request, text: "The garage ends up one and a half metres from the boundary and the plan says four. We are asking you to allow it anyway - the ground falls away steeply everywhere else on the plot." }
|
||||
- { id: neighbour-view, need: objection, text: "I got a notice about the extension at number 14. It takes the afternoon sun off my entire terrace and I want that on the record before you decide anything." }
|
||||
- { id: neighbour-parking, need: objection, text: "The flats they want to build in the old grocery have four parking places for four flats and everyone here already parks on the street. I object." }
|
||||
- { id: road-authority, need: hearing-answer, text: "Regarding your case 2026/418: the access onto the county road is within the sight line and we cannot accept it as drawn. Our formal reply is attached." }
|
||||
- { id: fire-service, need: hearing-answer, text: "We have looked at the conversion on Torget. We have no objection provided the stairwell is upgraded. Reference your case 2026/501." }
|
||||
- { id: refused-applicant, need: appeal, text: "You refused our exemption on the ninth. We don't accept the reasoning - you've allowed the same thing twice in the same street. We're appealing." }
|
||||
- { id: neighbour-appeals, need: appeal, text: "You approved the extension next door even though I objected. I'm appealing the decision. What do I need to send?" }
|
||||
- { id: board-member, need: appeal, text: "I sit on the appeals board. I need the case, our own decision, and what the neighbours wrote, before Tuesday." }
|
||||
- { id: inspector-on-roof, need: inspection-finding, text: "I'm up on the scaffold at Bjorkeveien. The fire wall isn't built to the drawing at all. I've got gloves on and it's blowing. I want this logged before I climb down." }
|
||||
- { id: inspector-all-good, need: inspection-finding, text: "Been out at Torget. Everything is as approved. Nothing to report but I have to record that I was there." }
|
||||
- { id: firm-wants-certificate, need: completion-request, text: "The work at Bjorkeveien 14 is finished and inspected. We're asking for the completion certificate so the owners can get their mortgage released." }
|
||||
- { id: wants-to-move-in, need: completion-request, text: "The house is done apart from the outside steps, which we can't pour until spring. The family has sold their flat. Can we get a temporary permission to move in?" }
|
||||
- { id: something-behind-fence, need: illegal-building-report, text: "There's a large structure going up behind the fence at Solbakken 7. Nobody has had a notice about it and it's been going on for two weeks." }
|
||||
- { id: owner-explains, need: illegal-building-report, text: "I got your letter about the terrace. I genuinely did not know it needed an application, it's been there since we moved in. What happens now?" }
|
||||
- { id: new-firm, need: firm-approval, text: "We are a three-person architecture firm that has just started. We want to be approved so we can file applications for our clients in your municipality." }
|
||||
- { id: firm-insurance-lapsed, need: firm-approval, text: "You wrote that our liability insurance certificate has expired. We have renewed it, here it is." }
|
||||
- { id: counter-phone-call, need: log-counter-enquiry, text: "A woman rang about the case behind her house. Wants to know where it stands. I'm at the counter and there's a queue - I need to write it down and send it to whoever has that case." }
|
||||
- { id: where-is-my-case, need: case-status-enquiry, text: "I sent in my application five weeks ago and I've heard nothing. Is anyone looking at it? Is it in the six weeks with the neighbours or is it waiting on me?" }
|
||||
@@ -0,0 +1,294 @@
|
||||
# A contractor's whole operation, to stress the builder and the
|
||||
# harness: 13 needs, 6 roles, 9 buckets, needs sharing a bucket,
|
||||
# skippable stages, and a front door that had to be tiered.
|
||||
business: Nordvik Bygg
|
||||
language: en
|
||||
contact: { name: Ingrid Nordvik, email: ingrid@nordvikbygg.example }
|
||||
about: >
|
||||
Twelve people who survey old buildings, draw up rehabilitation plans,
|
||||
handle the permit application, and do the building work with their
|
||||
own carpenters plus subcontractors for electrical, plumbing and
|
||||
scaffolding. Project managers win and run the jobs, site managers run
|
||||
each building site day to day, and one safety officer owns everything
|
||||
reported from site.
|
||||
|
||||
# Six public needs is one over what a front door carries, so the
|
||||
# public comes through three doors: a building of theirs, work with
|
||||
# us, and a site of ours next to them (that one is a need on its own).
|
||||
doors:
|
||||
- id: your-building
|
||||
who: an owner, a housing board or a past customer with a building
|
||||
- id: work-with-us
|
||||
who: a trade firm, a carpenter or an apprentice who wants work from us
|
||||
|
||||
needs:
|
||||
# --- the public ---------------------------------------------------
|
||||
- id: survey-enquiry
|
||||
door: your-building
|
||||
who: a housing board or private owner with a problem in a building
|
||||
wants: someone qualified to come and look at it and say what it takes
|
||||
lands_in: enquiries
|
||||
handled_by: project_managers
|
||||
stages: [open, surveyed, quoted, won, lost]
|
||||
done_when: [won, lost]
|
||||
success: [won]
|
||||
# Whoever went to the building marks it surveyed; site managers
|
||||
# see what is coming their way.
|
||||
moves_by: { surveyed: [project_managers, site_managers] }
|
||||
watched_by: [site_managers]
|
||||
max_fields: 4
|
||||
collects:
|
||||
- { name: name }
|
||||
- { name: phone, type: tel }
|
||||
- { name: address }
|
||||
- { name: problem, type: textarea }
|
||||
- { name: access, optional: true }
|
||||
|
||||
- id: build-enquiry
|
||||
door: your-building
|
||||
who: an owner who already knows what they want built or changed
|
||||
wants: drawings, a permit application and a price
|
||||
lands_in: enquiries
|
||||
handled_by: project_managers
|
||||
stages: [open, surveyed, quoted, won, lost]
|
||||
done_when: [won, lost]
|
||||
success: [won]
|
||||
# Whoever went to the building marks it surveyed; site managers
|
||||
# see what is coming their way.
|
||||
moves_by: { surveyed: [project_managers, site_managers] }
|
||||
watched_by: [site_managers]
|
||||
max_fields: 5
|
||||
collects:
|
||||
- { name: name }
|
||||
- { name: phone, type: tel }
|
||||
- { name: address }
|
||||
- { name: project, type: textarea }
|
||||
- { name: timeframe, type: select, options: [This year, Next year, No fixed date] }
|
||||
|
||||
- id: warranty-claim
|
||||
door: your-building
|
||||
who: a past customer who has found a fault in work we handed over
|
||||
wants: us to come back, look at it, and put it right
|
||||
lands_in: claims
|
||||
handled_by: project_managers
|
||||
stages: [open, inspected, accepted, fixed, rejected]
|
||||
done_when: [fixed, rejected]
|
||||
success: [fixed]
|
||||
# The site manager inspects and puts it right; whether it is ours
|
||||
# to fix is the project manager's call.
|
||||
moves_by: { inspected: [site_managers], fixed: [site_managers] }
|
||||
max_fields: 4
|
||||
collects:
|
||||
- { name: name }
|
||||
- { name: phone, type: tel }
|
||||
- { name: address }
|
||||
- { name: fault, type: textarea }
|
||||
- { name: handed_over, type: date, optional: true }
|
||||
|
||||
- id: neighbour-complaint
|
||||
who: someone living or working next to one of our building sites
|
||||
wants: the noise, dust or blocked access dealt with, and an answer
|
||||
lands_in: complaints
|
||||
handled_by: site_managers
|
||||
stages: [open, answered]
|
||||
done_when: [answered]
|
||||
success: [answered]
|
||||
watched_by: [project_managers]
|
||||
max_fields: 3
|
||||
collects:
|
||||
- { name: site_address }
|
||||
- { name: what_is_wrong, type: textarea }
|
||||
- { name: reach_me_on }
|
||||
|
||||
- id: subcontractor-application
|
||||
door: work-with-us
|
||||
who: a trade firm doing electrical, plumbing or scaffolding work
|
||||
wants: to be approved once, and then be called when a job needs them
|
||||
lands_in: subcontractors
|
||||
handled_by: project_managers
|
||||
stages: [open, awaiting_papers, approved, declined]
|
||||
done_when: [approved, declined]
|
||||
success: [approved]
|
||||
watched_by: [site_managers]
|
||||
skippable: [awaiting_papers]
|
||||
max_fields: 5
|
||||
collects:
|
||||
- { name: company }
|
||||
- { name: registration_number }
|
||||
- { name: trade, type: select, options: [Electrical, Plumbing, Scaffolding, Sheet metal, Other] }
|
||||
- { name: contact_email, type: email }
|
||||
- { name: insurance_confirmed, type: select, options: ["Yes", Not yet] }
|
||||
- { name: trade_approval, optional: true }
|
||||
|
||||
- id: job-application
|
||||
door: work-with-us
|
||||
who: a carpenter, bricklayer or apprentice looking for work
|
||||
wants: to be considered, without writing a letter
|
||||
lands_in: applicants
|
||||
handled_by: project_managers
|
||||
stages: [open, interviewing, offered, hired, declined]
|
||||
done_when: [hired, declined]
|
||||
success: [hired]
|
||||
max_fields: 3
|
||||
collects:
|
||||
- { name: name }
|
||||
- { name: phone, type: tel }
|
||||
- { name: trade, type: select, options: [Carpenter, Bricklayer, Apprentice, Other] }
|
||||
- { name: experience, type: textarea, optional: true }
|
||||
|
||||
# --- clients with a job under way ---------------------------------
|
||||
- id: change-request
|
||||
who: a client whose job is under way and who wants something changed
|
||||
wants: a price for the change, and the change done if they accept it
|
||||
enters_as: clients
|
||||
lands_in: change_orders
|
||||
handled_by: project_managers
|
||||
stages: [open, priced, approved, completed, declined]
|
||||
done_when: [completed, declined]
|
||||
success: [completed]
|
||||
# The project manager prices it, the client says yes or no through
|
||||
# their own link, the site manager does the work.
|
||||
moves_by: { approved: [submitter], declined: [submitter, project_managers], completed: [site_managers] }
|
||||
watched_by: [site_managers]
|
||||
max_fields: 3
|
||||
collects:
|
||||
- { name: email, type: email }
|
||||
- { name: job_address }
|
||||
- { name: change, type: textarea }
|
||||
- { name: needed_by, type: date, optional: true }
|
||||
|
||||
# --- project managers ---------------------------------------------
|
||||
- id: log-phone-enquiry
|
||||
who: a project manager who just took a call or a text about a building
|
||||
wants: the enquiry written down before it gets lost
|
||||
enters_as: project_managers
|
||||
lands_in: enquiries
|
||||
handled_by: project_managers
|
||||
stages: [open, surveyed, quoted, won, lost]
|
||||
done_when: [won, lost]
|
||||
success: [won]
|
||||
# Whoever went to the building marks it surveyed; site managers
|
||||
# see what is coming their way.
|
||||
moves_by: { surveyed: [project_managers, site_managers] }
|
||||
watched_by: [site_managers]
|
||||
max_fields: 4
|
||||
collects:
|
||||
- { name: caller }
|
||||
- { name: phone, type: tel }
|
||||
- { name: address }
|
||||
- { name: problem, type: textarea }
|
||||
|
||||
- id: post-job-request
|
||||
who: a project manager with work to hand to a subcontractor
|
||||
wants: approved subcontractors to see the job and answer with a price
|
||||
enters_as: project_managers
|
||||
lands_in: job_requests
|
||||
handled_by: project_managers
|
||||
stages: [open, awarded, withdrawn]
|
||||
done_when: [awarded, withdrawn]
|
||||
success: [awarded]
|
||||
# This is how subcontractors see what there is to bid on.
|
||||
watched_by: [subcontractors, site_managers]
|
||||
max_fields: 4
|
||||
collects:
|
||||
- { name: site_address }
|
||||
- { name: trade, type: select, options: [Electrical, Plumbing, Scaffolding, Sheet metal, Other] }
|
||||
- { name: work, type: textarea }
|
||||
- { name: start, type: date }
|
||||
|
||||
# --- approved subcontractors --------------------------------------
|
||||
- id: submit-bid
|
||||
who: an approved subcontractor who has seen a job they can take
|
||||
wants: to say yes with a price and a start date
|
||||
enters_as: subcontractors
|
||||
lands_in: bids
|
||||
handled_by: project_managers
|
||||
stages: [open, shortlisted, accepted, declined]
|
||||
done_when: [accepted, declined]
|
||||
success: [accepted]
|
||||
watched_by: [site_managers]
|
||||
skippable: [shortlisted]
|
||||
max_fields: 3
|
||||
collects:
|
||||
- { name: job }
|
||||
- { name: price }
|
||||
- { name: can_start, type: date }
|
||||
|
||||
# --- on site --------------------------------------------------------
|
||||
- id: report-site-issue
|
||||
who: anyone on site, ours or a subcontractor's, on a phone with gloves on
|
||||
wants: to report a hazard, a near miss or wrong materials before they forget
|
||||
enters_as: crew
|
||||
lands_in: site_issues
|
||||
handled_by: safety_officer
|
||||
stages: [open, in_hand, closed]
|
||||
done_when: [closed]
|
||||
success: [closed]
|
||||
# A site manager can take an issue in hand on the spot; only the
|
||||
# safety officer closes it.
|
||||
moves_by: { in_hand: [safety_officer, site_managers] }
|
||||
watched_by: [project_managers]
|
||||
max_fields: 3
|
||||
collects:
|
||||
- { name: kind, type: select, options: [Hazard, Near miss, Wrong materials, Other] }
|
||||
- { name: where }
|
||||
- { name: what_happened, type: textarea }
|
||||
|
||||
- id: order-materials
|
||||
who: a carpenter or bricklayer who is about to run out of something
|
||||
wants: it ordered and on site before work stops
|
||||
enters_as: crew
|
||||
lands_in: material_orders
|
||||
handled_by: site_managers
|
||||
stages: [open, ordered, delivered, refused]
|
||||
done_when: [delivered, refused]
|
||||
success: [delivered]
|
||||
# The crew confirms it arrived.
|
||||
moves_by: { delivered: [crew, site_managers] }
|
||||
max_fields: 3
|
||||
collects:
|
||||
- { name: site_address }
|
||||
- { name: what, type: textarea }
|
||||
- { name: needed_by, type: date }
|
||||
|
||||
- id: safety-round-finding
|
||||
who: the safety officer walking a site on the weekly round
|
||||
wants: each thing found written down where the reported issues already are
|
||||
enters_as: safety_officer
|
||||
lands_in: site_issues
|
||||
handled_by: safety_officer
|
||||
stages: [open, in_hand, closed]
|
||||
done_when: [closed]
|
||||
success: [closed]
|
||||
# A site manager can take an issue in hand on the spot; only the
|
||||
# safety officer closes it.
|
||||
moves_by: { in_hand: [safety_officer, site_managers] }
|
||||
watched_by: [project_managers]
|
||||
max_fields: 3
|
||||
collects:
|
||||
- { name: kind, type: select, options: [Hazard, Near miss, Wrong materials, Other] }
|
||||
- { name: where }
|
||||
- { name: what_happened, type: textarea }
|
||||
|
||||
# No model that writes words ever sees these.
|
||||
personas:
|
||||
- { id: housing-board-damp, need: survey-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: survey-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: build-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: build-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: leaking-bathroom, need: warranty-claim, text: "You redid our bathroom two years ago. Now water is coming through the ceiling of the flat below. I want you to come and fix it." }
|
||||
- { id: cracked-render, need: warranty-claim, text: "The render you put on our gable wall last summer has cracked right across. That can't be right after one winter." }
|
||||
- { id: noisy-mornings, need: neighbour-complaint, text: "I live next door to the block you are working on. You start drilling at half past six every morning and I have a baby. Who do I talk to?" }
|
||||
- { id: blocked-driveway, need: neighbour-complaint, text: "Your skip and a van have been parked across the entrance to our shop's loading bay for three days. We can't get deliveries." }
|
||||
- { 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: extra-window, need: change-request, text: "You are in the middle of our attic conversion. We have decided we want a roof window over the stairs as well. What would that cost?" }
|
||||
- { id: pm-took-a-call, need: log-phone-enquiry, text: "A woman from a housing board in Storgata just rang about water in their basement. I'm in the van. I need to note it before I forget her number." }
|
||||
- { id: pm-needs-plumber, need: post-job-request, text: "The bathroom job in Kirkeveien needs a plumber for two weeks from the first of next month. I want prices from the ones we have approved." }
|
||||
- { id: plumber-bidding, need: submit-bid, text: "We are an approved plumbing firm. I saw you need someone in Kirkeveien next month. We can do it, I have a price." }
|
||||
- { 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." }
|
||||
- { id: out-of-battens, need: order-materials, text: "We have enough roof battens for today and maybe half of tomorrow. Need another two hundred metres on site by Thursday." }
|
||||
- { id: weekly-round, need: safety-round-finding, text: "On my Friday round I found the first-aid box on level two empty and an extension lead run through a puddle." }
|
||||
@@ -0,0 +1,122 @@
|
||||
# The contractor from intakes/contractor.yaml as a plan: the needs spec
|
||||
# plus what each form collects. Everything structural in the generated
|
||||
# site comes from this file; a model only writes the words.
|
||||
business: Nordvik Bygg
|
||||
language: en
|
||||
contact: { name: Ingrid Nordvik, email: ingrid@nordvikbygg.example }
|
||||
about: >
|
||||
Twelve people who survey old buildings, draw up rehabilitation plans,
|
||||
handle the permit application, and do the building work with their
|
||||
own carpenters plus subcontractors for electrical, plumbing and
|
||||
scaffolding.
|
||||
|
||||
needs:
|
||||
- id: survey-enquiry
|
||||
who: a housing board or private owner with a problem in a building
|
||||
wants: someone qualified to come and look at it and say what it takes
|
||||
lands_in: enquiries
|
||||
handled_by: project_managers
|
||||
stages: [open, surveyed, quoted, won, lost]
|
||||
done_when: [won, lost]
|
||||
success: [won]
|
||||
max_fields: 4
|
||||
collects:
|
||||
- { name: name }
|
||||
- { name: phone, type: tel }
|
||||
- { name: address }
|
||||
- { name: problem, type: textarea }
|
||||
- { name: access, optional: true }
|
||||
|
||||
- id: build-enquiry
|
||||
who: an owner who already knows what they want built or changed
|
||||
wants: drawings, a permit application and a price
|
||||
lands_in: enquiries
|
||||
handled_by: project_managers
|
||||
stages: [open, surveyed, quoted, won, lost]
|
||||
done_when: [won, lost]
|
||||
success: [won]
|
||||
max_fields: 5
|
||||
collects:
|
||||
- { name: name }
|
||||
- { name: phone, type: tel }
|
||||
- { name: address }
|
||||
- { name: project, type: textarea }
|
||||
- { name: timeframe, type: select, options: [This year, Next year, No fixed date] }
|
||||
|
||||
- id: log-phone-enquiry
|
||||
who: a project manager who just took a call or a text about a building
|
||||
wants: the enquiry written down before it gets lost
|
||||
enters_as: project_managers
|
||||
lands_in: enquiries
|
||||
handled_by: project_managers
|
||||
stages: [open, surveyed, quoted, won, lost]
|
||||
done_when: [won, lost]
|
||||
success: [won]
|
||||
max_fields: 4
|
||||
collects:
|
||||
- { name: caller }
|
||||
- { name: phone, type: tel }
|
||||
- { name: address }
|
||||
- { name: problem, type: textarea }
|
||||
|
||||
- id: subcontractor-application
|
||||
who: a trade firm doing electrical, plumbing or scaffolding work
|
||||
wants: to be approved once, and then be called when a job needs them
|
||||
lands_in: subcontractors
|
||||
handled_by: project_managers
|
||||
stages: [open, awaiting_papers, approved, declined]
|
||||
done_when: [approved, declined]
|
||||
success: [approved]
|
||||
skippable: [awaiting_papers]
|
||||
max_fields: 5
|
||||
collects:
|
||||
- { name: company }
|
||||
- { name: registration_number }
|
||||
- { name: trade, type: select, options: [Electrical, Plumbing, Scaffolding, Sheet metal, Other] }
|
||||
- { name: contact_email, type: email }
|
||||
- { name: insurance_confirmed, type: select, options: ["Yes", Not yet] }
|
||||
- { name: trade_approval, optional: true }
|
||||
|
||||
- id: job-application
|
||||
who: a carpenter, bricklayer or apprentice looking for work
|
||||
wants: to be considered, without writing a letter
|
||||
lands_in: applicants
|
||||
handled_by: project_managers
|
||||
stages: [open, interviewing, hired, declined]
|
||||
done_when: [hired, declined]
|
||||
success: [hired]
|
||||
max_fields: 3
|
||||
collects:
|
||||
- { name: name }
|
||||
- { name: phone, type: tel }
|
||||
- { name: trade, type: select, options: [Carpenter, Bricklayer, Apprentice, Other] }
|
||||
- { name: experience, type: textarea, optional: true }
|
||||
|
||||
- id: report-site-issue
|
||||
who: anyone on site, ours or a subcontractor's, on a phone with gloves on
|
||||
wants: to report a hazard, a near miss or wrong materials before they forget
|
||||
enters_as: crew
|
||||
lands_in: site_issues
|
||||
handled_by: safety_officer
|
||||
stages: [open, in_hand, closed]
|
||||
done_when: [closed]
|
||||
success: [closed]
|
||||
max_fields: 3
|
||||
collects:
|
||||
- { name: kind, type: select, options: [Hazard, Near miss, Wrong materials, Other] }
|
||||
- { name: where }
|
||||
- { name: what_happened, type: textarea }
|
||||
|
||||
# Held out from every model that writes words: these are the personas
|
||||
# from sectors/construction, which no generator has seen.
|
||||
personas:
|
||||
- { id: housing-board-damp, need: survey-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: survey-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: build-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: build-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: 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,119 @@
|
||||
# Fotballgruppa i et idrettslag, på norsk: to dører på forsiden
|
||||
# (foresatte, og de som vil bidra), roller som overlapper på én
|
||||
# innmelding (treneren godkjenner, kassereren ser betalingen), og en
|
||||
# intern melding fra trenerne til styret.
|
||||
business: Lia IL Fotball
|
||||
language: no
|
||||
contact: { name: Kari Lien, email: kari@liail.example }
|
||||
mail: { from: "Liail IL <post@liail.example>", reply_to: kari@liail.example }
|
||||
about: >
|
||||
Fotballgruppa i et idrettslag på bygda. Rundt hundre og tjue unger
|
||||
fra seks til seksten år, fordelt på lag etter årskull, drevet av
|
||||
foreldre på fritida.
|
||||
|
||||
doors:
|
||||
- id: foresatte
|
||||
who: en forelder eller foresatt til et barn som vil spille, eller allerede spiller
|
||||
- id: bidra
|
||||
who: noen som vil hjelpe til i klubben, eller en bedrift som vil støtte den
|
||||
|
||||
needs:
|
||||
- id: innmelding
|
||||
who: en forelder som vil ha ungen med på et lag
|
||||
wants: at barnet får plass på laget for sitt årskull
|
||||
door: foresatte
|
||||
lands_in: innmeldinger
|
||||
handled_by: trenere
|
||||
stages: [open, godkjent, betalt, trukket]
|
||||
stages_text: { open: "mottatt", godkjent: "godkjent av treneren", betalt: "betalt", trukket: "trukket" }
|
||||
mails: { open: submitter, godkjent: submitter, betalt: submitter }
|
||||
done_when: [betalt, trukket]
|
||||
success: [betalt]
|
||||
# Treneren godkjenner plassen, kassereren ser at det er betalt.
|
||||
moves_by: { betalt: [kasserer] }
|
||||
watched_by: [kasserer]
|
||||
max_fields: 5
|
||||
collects:
|
||||
- { name: barnets_navn }
|
||||
- { name: fodselsar, type: select, options: ["2010", "2011", "2012", "2013", "2014", "2015", "2016", "2017", "2018", "2019", "2020"] }
|
||||
- { name: foresatt }
|
||||
- { name: telefon, type: tel }
|
||||
- { name: email, type: email }
|
||||
- { name: spilt_for, optional: true }
|
||||
|
||||
- id: avgiftssak
|
||||
who: en forelder som sliter med treningsavgiften
|
||||
wants: å dele den opp, eller slippe den
|
||||
door: foresatte
|
||||
lands_in: avgiftssaker
|
||||
handled_by: styret
|
||||
stages: [open, vurdert, innvilget, avslatt]
|
||||
stages_text: { open: "mottatt", vurdert: "vurdert", innvilget: "innvilget", avslatt: "avslått" }
|
||||
done_when: [innvilget, avslatt]
|
||||
success: [innvilget]
|
||||
max_fields: 3
|
||||
collects:
|
||||
- { name: barnets_navn }
|
||||
- { name: foresatt }
|
||||
- { name: hva_gjelder_det, type: textarea }
|
||||
- { name: telefon, type: tel, optional: true }
|
||||
|
||||
- id: frivillig
|
||||
who: en som vil hjelpe til, som trener, lagleder, sjåfør eller på dugnad
|
||||
wants: å bli brukt der det trengs
|
||||
door: bidra
|
||||
lands_in: frivillige
|
||||
handled_by: styret
|
||||
stages: [open, kontaktet, plassert, avslatt]
|
||||
stages_text: { open: "mottatt", kontaktet: "kontaktet", plassert: "satt opp", avslatt: "takket nei" }
|
||||
done_when: [plassert, avslatt]
|
||||
success: [plassert]
|
||||
max_fields: 3
|
||||
collects:
|
||||
- { name: navn }
|
||||
- { name: telefon, type: tel }
|
||||
- { name: hva_kan_du, type: select, options: [Trene et lag, Være lagleder, Kjøre til kamper, Dugnad, Annet] }
|
||||
|
||||
- id: sponsor
|
||||
who: en bedrift i bygda som vil støtte klubben
|
||||
wants: en prat om hva de kan bidra med, og en avtale
|
||||
door: bidra
|
||||
lands_in: sponsorer
|
||||
handled_by: styret
|
||||
stages: [open, i_dialog, avtalt, avslatt]
|
||||
stages_text: { open: "mottatt", i_dialog: "i dialog", avtalt: "avtalt", avslatt: "avslått" }
|
||||
done_when: [avtalt, avslatt]
|
||||
success: [avtalt]
|
||||
max_fields: 3
|
||||
collects:
|
||||
- { name: bedrift }
|
||||
- { name: kontaktperson }
|
||||
- { name: telefon, type: tel }
|
||||
- { name: hva_tenker_dere, type: textarea, optional: true }
|
||||
|
||||
- id: baneproblem
|
||||
who: en trener som ser at noe på anlegget ikke er i orden
|
||||
wants: at styret får vite det og ordner det
|
||||
enters_as: trenere
|
||||
lands_in: baneproblemer
|
||||
handled_by: styret
|
||||
stages: [open, tatt_tak_i, ordnet]
|
||||
stages_text: { open: "mottatt", tatt_tak_i: "tatt tak i", ordnet: "ordnet" }
|
||||
done_when: [ordnet]
|
||||
success: [ordnet]
|
||||
max_fields: 2
|
||||
collects:
|
||||
- { name: hvor }
|
||||
- { name: hva, type: textarea }
|
||||
|
||||
# Holdt unna alle modeller som skriver ord.
|
||||
personas:
|
||||
- { id: ny-i-bygda, need: innmelding, text: "Vi flyttet hit i sommer og sønnen vår på ni har spilt fotball før. Hvordan får vi ham med på et lag her?" }
|
||||
- { id: tvillinger, need: innmelding, text: "Jeg har tvillingjenter på sju som maser om å begynne. Er det plass, og hva koster det?" }
|
||||
- { id: alenefar, need: avgiftssak, text: "Jeg er alene med tre unger og treningsavgiften for alle tre blir for mye på en gang. Går det an å dele den opp?" }
|
||||
- { id: permittert, need: avgiftssak, text: "Jeg ble permittert i vinter. Kan dattera mi fortsette å spille selv om vi ikke klarer avgiften akkurat nå?" }
|
||||
- { id: pensjonist, need: frivillig, text: "Jeg er pensjonert og spilte selv i mange år. Har dere bruk for en som kan ta de minste et par kvelder i uka?" }
|
||||
- { id: stor-bil, need: frivillig, text: "Jeg kan ikke trene noen, men jeg har stor bil og kan kjøre til bortekamper." }
|
||||
- { id: rorlegger, need: sponsor, text: "Jeg driver rørleggerfirma her i bygda og vil gjerne ha logoen vår på draktene til et lag. Hvem snakker jeg med?" }
|
||||
- { id: kafeen, need: sponsor, text: "Vi har kafé i sentrum og kan bidra med noe til cupen i mai, kanskje vafler eller litt penger." }
|
||||
- { id: lyset-slukker, need: baneproblem, text: "Lyset på kunstgresset slår seg av halv åtte og vi trener til åtte. Ungene løper i mørket." }
|
||||
@@ -0,0 +1,94 @@
|
||||
# A machine hire yard: two doors (hiring, and an account for firms),
|
||||
# one aggregate walked by two roles (the counter reserves, the yard
|
||||
# hands out and takes back), and the yard's own damage report.
|
||||
business: Fjellstad Hire
|
||||
language: en
|
||||
contact: { name: Ola Fjellstad, email: ola@fjellstadhire.example }
|
||||
about: >
|
||||
Machine hire: mini diggers, lifts, compactors, generators. A counter,
|
||||
a yard that delivers, collects and fixes, and an office.
|
||||
|
||||
doors:
|
||||
- id: hiring
|
||||
who: someone who needs a machine, or has one of ours that has stopped
|
||||
- id: firms
|
||||
who: a firm that hires often and wants an account with us
|
||||
|
||||
needs:
|
||||
- id: hire-request
|
||||
who: a private person or a firm who needs a machine for a while
|
||||
wants: the right machine, from a date, for as long as they need it
|
||||
door: hiring
|
||||
lands_in: hires
|
||||
handled_by: counter
|
||||
stages: [open, reserved, out, returned, cancelled]
|
||||
done_when: [returned, cancelled]
|
||||
success: [returned]
|
||||
# The counter reserves; the yard hands it out and takes it back.
|
||||
moves_by: { out: [yard], returned: [yard] }
|
||||
max_fields: 5
|
||||
collects:
|
||||
- { name: name }
|
||||
- { name: phone, type: tel }
|
||||
- { name: what, type: textarea }
|
||||
- { name: from, type: date }
|
||||
- { name: until, type: date }
|
||||
- { name: deliver_to, optional: true }
|
||||
|
||||
- id: breakdown
|
||||
who: someone whose hired machine has stopped working
|
||||
wants: it running again, or swapped, today
|
||||
door: hiring
|
||||
lands_in: breakdowns
|
||||
handled_by: yard
|
||||
stages: [open, in_hand, resolved]
|
||||
done_when: [resolved]
|
||||
success: [resolved]
|
||||
max_fields: 3
|
||||
collects:
|
||||
- { name: phone, type: tel }
|
||||
- { name: machine }
|
||||
- { name: what_happened, type: textarea }
|
||||
- { name: where, optional: true }
|
||||
|
||||
- id: account-request
|
||||
who: a firm that hires often
|
||||
wants: to send anyone to collect, and get one invoice a month
|
||||
door: firms
|
||||
lands_in: accounts
|
||||
handled_by: office
|
||||
stages: [open, checked, approved, declined]
|
||||
done_when: [approved, declined]
|
||||
success: [approved]
|
||||
max_fields: 4
|
||||
collects:
|
||||
- { name: company }
|
||||
- { name: registration_number }
|
||||
- { name: contact }
|
||||
- { name: phone, type: tel }
|
||||
- { name: invoice_email, type: email, optional: true }
|
||||
|
||||
- id: damage-on-return
|
||||
who: someone in the yard who finds damage when a machine comes back
|
||||
wants: it written down before the customer has left
|
||||
enters_as: yard
|
||||
lands_in: damage_reports
|
||||
handled_by: office
|
||||
stages: [open, invoiced, written_off]
|
||||
done_when: [invoiced, written_off]
|
||||
success: [invoiced]
|
||||
max_fields: 3
|
||||
collects:
|
||||
- { name: machine }
|
||||
- { name: hire_or_customer }
|
||||
- { name: damage, type: textarea }
|
||||
|
||||
# Held out from every model that writes words.
|
||||
personas:
|
||||
- { id: garden-wall, need: hire-request, text: "I'm building a stone wall in the garden and need a small digger for a weekend. I've never driven one." }
|
||||
- { id: roofer-lift, need: hire-request, text: "We're a roofing crew and need a scissor lift on site in Dalen from Monday for about two weeks." }
|
||||
- { id: stuck-compactor, need: breakdown, text: "The plate compactor I picked up this morning won't start. I've got a crew standing around." }
|
||||
- { id: lift-error, need: breakdown, text: "The lift you delivered keeps showing an error and stops halfway up. It's on our site at the school." }
|
||||
- { id: landscaper-account, need: account-request, text: "We're a landscaping firm hiring from you almost every month and I'm tired of paying by card each time. Can we get invoiced monthly?" }
|
||||
- { id: builder-account, need: account-request, text: "Small building firm, six people. We'd like to be able to send whoever is nearest to pick things up and have it on our account." }
|
||||
- { id: yard-window, need: damage-on-return, text: "The mini digger from the Berg job came back with a smashed rear window and hydraulic oil everywhere." }
|
||||
@@ -0,0 +1,113 @@
|
||||
# A physiotherapy clinic: two doors on the front, patients and the
|
||||
# people who send them; the reception handles everything, therapists
|
||||
# write the report back and notice who did not turn up.
|
||||
business: Bakken Fysioterapi
|
||||
language: en
|
||||
contact: { name: Marit Bakken, email: marit@bakkenfysio.example }
|
||||
about: >
|
||||
Six physiotherapists and a receptionist. People come with pain,
|
||||
injuries and after operations, on their own or sent by a doctor,
|
||||
and we treat them over a course of sessions.
|
||||
|
||||
doors:
|
||||
- id: patients
|
||||
who: someone with pain or an injury, or a patient who already has a time here
|
||||
- id: referrers
|
||||
who: a doctor, a hospital ward or an insurer sending someone to us
|
||||
|
||||
needs:
|
||||
- id: new-patient
|
||||
who: someone with something that hurts
|
||||
wants: a first appointment with a therapist who suits them
|
||||
door: patients
|
||||
lands_in: requests
|
||||
handled_by: reception
|
||||
stages: [open, offered, booked, declined]
|
||||
done_when: [booked, declined]
|
||||
success: [booked]
|
||||
max_fields: 4
|
||||
collects:
|
||||
- { name: name }
|
||||
- { name: phone, type: tel }
|
||||
- { name: what_hurts, type: textarea }
|
||||
- { name: when, type: select, options: [Mornings, Afternoons, Any time] }
|
||||
- { name: sent_by_doctor, type: select, options: ["Yes", "No"], optional: true }
|
||||
|
||||
- id: move-or-cancel
|
||||
who: a patient who already has a time and cannot come
|
||||
wants: the time moved, or the slot freed
|
||||
door: patients
|
||||
lands_in: cancellations
|
||||
handled_by: reception
|
||||
stages: [open, rebooked, released]
|
||||
done_when: [rebooked, released]
|
||||
success: [rebooked]
|
||||
max_fields: 3
|
||||
collects:
|
||||
- { name: name }
|
||||
- { name: phone, type: tel }
|
||||
- { name: appointment_day, type: date }
|
||||
- { name: new_time_wish, optional: true }
|
||||
|
||||
- id: referral
|
||||
who: a GP or a hospital ward sending a patient
|
||||
wants: the patient seen, and a report back when the course ends
|
||||
door: referrers
|
||||
lands_in: referrals
|
||||
handled_by: reception
|
||||
stages: [open, accepted, booked, reported, returned]
|
||||
done_when: [reported, returned]
|
||||
success: [reported]
|
||||
# The therapist who treated them writes the report; they see the
|
||||
# referral from the start.
|
||||
moves_by: { reported: [therapists] }
|
||||
watched_by: [therapists]
|
||||
max_fields: 5
|
||||
collects:
|
||||
- { name: patient_name }
|
||||
- { name: patient_phone, type: tel }
|
||||
- { name: referrer }
|
||||
- { name: what_for, type: textarea }
|
||||
- { name: urgency, type: select, options: [This week, Within a month, Routine] }
|
||||
|
||||
- id: insurance-approval
|
||||
who: the receptionist, before a course that an insurer has to approve
|
||||
wants: the request sent and its answer kept where the patient's case is
|
||||
enters_as: reception
|
||||
lands_in: approvals
|
||||
handled_by: reception
|
||||
stages: [open, sent, approved, refused]
|
||||
done_when: [approved, refused]
|
||||
success: [approved]
|
||||
max_fields: 3
|
||||
collects:
|
||||
- { name: patient_name }
|
||||
- { name: insurer }
|
||||
- { name: sessions }
|
||||
- { name: case_number, optional: true }
|
||||
|
||||
- id: no-show
|
||||
who: a therapist whose patient did not turn up and did not call
|
||||
wants: the reception to ring them and get them back in
|
||||
enters_as: therapists
|
||||
lands_in: no_shows
|
||||
handled_by: reception
|
||||
stages: [open, contacted, rebooked, closed]
|
||||
done_when: [rebooked, closed]
|
||||
success: [rebooked]
|
||||
max_fields: 2
|
||||
collects:
|
||||
- { name: patient_name }
|
||||
- { name: appointment_day, type: date }
|
||||
- { name: note, optional: true }
|
||||
|
||||
# Held out from every model that writes words.
|
||||
personas:
|
||||
- { id: runner-knee, need: new-patient, text: "I've been running for years and the outside of my right knee has started hurting after about five kilometres. I'd like someone to have a look, preferably in the mornings." }
|
||||
- { id: post-surgery-mother, need: new-patient, text: "My mother had a hip replacement three weeks ago and the hospital said she needs training. She can't do much on her own and I'll drive her." }
|
||||
- { id: sick-child, need: move-or-cancel, text: "I have a session on Thursday at ten but my daughter has a fever and I can't come. Can I move it?" }
|
||||
- { id: sent-abroad, need: move-or-cancel, text: "Work is sending me abroad next week so my appointment on the 3rd is off. Just free the slot, I'll be in touch when I'm back." }
|
||||
- { id: gp-shoulder, need: referral, text: "I'm a GP. A patient of mine has a frozen shoulder that isn't improving with what I can offer. I'd like her to start with you and hear how it goes." }
|
||||
- { id: hospital-discharge, need: referral, text: "Orthopaedic ward here. We're discharging a man after an ankle fracture on Friday and need follow-up rehab arranged near where he lives. He'll need a report for his sick leave." }
|
||||
- { id: reception-approval, need: insurance-approval, text: "The patient in room two has a policy that covers twelve sessions if the insurer signs off first. I need to send that off and keep track of the answer." }
|
||||
- { id: therapist-no-show, need: no-show, text: "My two o'clock didn't turn up and didn't call. Second time. Someone needs to ring him." }
|
||||
@@ -0,0 +1,331 @@
|
||||
# Tomter Vel, en velforening: fire dører på forsiden (medlemskap,
|
||||
# melde fra, bidra, naboer imellom) og høringsinnspill rett på
|
||||
# forsiden, siden den som har en mening om en sak ikke ser seg selv
|
||||
# som frivillig. Komiteene er gruppene: en søknad om plass i en
|
||||
# komité velger blant de samme sju som har hver sin pult. Trafikk,
|
||||
# anlegg og oppslagstavla kan alle lese; kontaktopplysninger vises
|
||||
# ikke der. Nabohjelp begge veier. Oppslagstavla for deling av utstyr
|
||||
# står ute med en gang: en nabo som kan låne ut, eller vil låne, sier
|
||||
# fra rett fra oppslaget, og den som la det ut hører fra dem. Køen er
|
||||
# innboksen til den som la ut; siden holder ikke rede på hvem som ble
|
||||
# valgt. Redaktøren, de som står som ansvarlige, tar ned det som står
|
||||
# igjen.
|
||||
business: Tomter Vel
|
||||
language: no
|
||||
# Styret svarer for sidene som gruppe: ingen konto for innboksen, og
|
||||
# hvert styremedlem kan foreslå endringer på dem.
|
||||
contact: { name: Styret i Tomter Vel, email: post@tomtervel.no, group: styret }
|
||||
# Portal holder gruppene selv: en invitasjon er en plass som venter til
|
||||
# personen logger inn, og en person er telefonnummeret sitt først. Med
|
||||
# Vipps-innlogging kan alle logge inn med en gang; til da lager Kanidm
|
||||
# kontoen ved siden av.
|
||||
people: memberships
|
||||
# Avsenderen for all e-post fra siden (gdo på kasse leverer); det
|
||||
# kasses postfix kan sende for i dag.
|
||||
mail: { from: "Tomter Vel <tomtervel@klingenbergbygg.no>", reply_to: post@tomtervel.no }
|
||||
about: >
|
||||
Velforeningen for Tomter, stiftet i 1947. Sju komiteer drevet på
|
||||
fritida: arrangementer, elvestien, lekeplassene, høringssvar,
|
||||
miljøgata, pendlerforhold og trafikk. 157 husstander er medlemmer.
|
||||
|
||||
# Komiteene, som gruppene heter, med navnet folk kjenner dem under.
|
||||
groups:
|
||||
- { id: arrangementer, label: Arrangementer }
|
||||
- { id: elvesti, label: Elvesti }
|
||||
- { id: horingsinstans, label: Høringsinstans }
|
||||
- { id: lekeplasser, label: Leke- og møteplasser }
|
||||
- { id: miljogate, label: Miljøgate }
|
||||
- { id: pendlerforhold, label: Pendlerforhold }
|
||||
- { id: trafikk, label: Trafikkforhold }
|
||||
|
||||
# Styret kan invitere inn i alle grupper som har en pult; hver gruppe kan
|
||||
# invitere inn i seg selv. Portal oppretter kontoen og en engangslenke.
|
||||
invites_by: [styret]
|
||||
|
||||
# Forsiden åpner beskjedent: to trekk, én rolle hver, ingen knapp,
|
||||
# før tilbudene lenger ned på samme side. De to velet er til for: en
|
||||
# husstand som blir medlem, og en nabo som trenger en hånd. Begge har
|
||||
# noe å gjøre etterpå: medlemmet melder seg ut selv, den som fikk
|
||||
# hjelp sier selv fra at det er ordnet.
|
||||
does:
|
||||
- id: medlem
|
||||
for: bli-medlem
|
||||
text: >
|
||||
Velet taler for Tomter overfor kommunen, holder lekeplassene og
|
||||
stien i stand og samler naboene på 17. mai og dugnad. 157
|
||||
husstander er med. Du melder deg inn her, og ut igjen selv når
|
||||
du vil.
|
||||
- id: nabo
|
||||
for: trenger-hjelp
|
||||
text: >
|
||||
Trenger du en hånd med snømåking, handling eller skyss, kobler
|
||||
nabohjelpsgruppa deg med en nabo som kan. Du sier selv fra når
|
||||
det er ordnet.
|
||||
|
||||
doors:
|
||||
- id: medlem
|
||||
who: en husstand i Tomter som vil bli medlem, betale kontingent, eller melde flytting, ny e-post eller utmelding
|
||||
- id: melde-fra
|
||||
who: noen som har sett noe farlig i trafikken, eller noe ødelagt på lekeplassen eller stien
|
||||
- id: bidra
|
||||
who: noen som vil stille opp på 17. mai, dugnad eller et arrangement, eller sitte i en komité
|
||||
- id: naboer
|
||||
who: en nabo som trenger en hånd med snømåking, handling eller skyss, kan gi en, eller har noe å låne ut
|
||||
|
||||
|
||||
needs:
|
||||
- id: bli-medlem
|
||||
who: en husstand i Tomter
|
||||
wants: å bli medlem og få betalt kontingenten
|
||||
lands_in: husstander
|
||||
door: medlem
|
||||
handled_by: kasserer
|
||||
stages: [open, betalt, utmeldt]
|
||||
stages_text: { open: "mottatt", betalt: "betalt", utmeldt: "meldt ut" }
|
||||
done_when: [betalt, utmeldt]
|
||||
success: [betalt]
|
||||
# Medlemmet melder seg ut selv, gjennom sin egen lenke.
|
||||
moves_by: { utmeldt: [submitter, kasserer] }
|
||||
mails: { open: submitter, betalt: submitter }
|
||||
# Betalt kontingent er medlemskapet: husstanden får konto og plass
|
||||
# blant medlemmene i samme trekk, og det som er medlemmenes eget
|
||||
# (komitéplass) ligger bak innloggingen.
|
||||
grants: { betalt: medlemmer }
|
||||
max_fields: 3
|
||||
collects:
|
||||
- { name: name }
|
||||
- { name: adresse }
|
||||
- { name: email, type: email }
|
||||
- { name: telefon, type: tel, optional: true }
|
||||
|
||||
- id: endre-medlemskap
|
||||
who: et medlem som har flyttet, byttet e-post eller vil melde seg ut
|
||||
wants: at velet har riktige opplysninger om dem
|
||||
lands_in: medlemsendringer
|
||||
door: medlem
|
||||
handled_by: kasserer
|
||||
stages: [open, oppdatert]
|
||||
stages_text: { open: "mottatt", oppdatert: "oppdatert" }
|
||||
done_when: [oppdatert]
|
||||
success: [oppdatert]
|
||||
max_fields: 3
|
||||
collects:
|
||||
- { name: navn }
|
||||
- { name: adresse }
|
||||
- { name: hva_er_nytt, type: textarea }
|
||||
|
||||
- id: trafikkfare
|
||||
who: en som har sett et farlig sted i trafikken
|
||||
wants: at velet tar det opp med kommunen eller den som eier veien
|
||||
lands_in: trafikkmeldinger
|
||||
door: melde-fra
|
||||
handled_by: trafikk
|
||||
stages: [open, tatt_opp, ordnet, avvist]
|
||||
stages_text: { open: "mottatt", tatt_opp: "tatt opp med kommunen", ordnet: "ordnet", avvist: "avvist" }
|
||||
done_when: [ordnet, avvist]
|
||||
success: [ordnet]
|
||||
# Alle kan se hva som er meldt og hvor det står; e-posten vises ikke.
|
||||
watched_by: [styret, public]
|
||||
max_fields: 2
|
||||
collects:
|
||||
- { name: hvor }
|
||||
- { name: hva, type: textarea }
|
||||
- { name: epost, type: email, optional: true }
|
||||
|
||||
- id: anleggsfeil
|
||||
who: en som ser at noe på lekeplassen eller stien er ødelagt
|
||||
wants: at det blir fikset før noen skader seg
|
||||
lands_in: anleggsfeil
|
||||
door: melde-fra
|
||||
handled_by: lekeplasser
|
||||
stages: [open, tatt_tak_i, utbedret]
|
||||
stages_text: { open: "mottatt", tatt_tak_i: "tatt tak i", utbedret: "utbedret" }
|
||||
done_when: [utbedret]
|
||||
success: [utbedret]
|
||||
watched_by: [public]
|
||||
max_fields: 2
|
||||
collects:
|
||||
- { name: hvor, type: select, options: [Lekeplassen, Elvestien, Skøytebanen, Et annet sted] }
|
||||
- { name: hva, type: textarea }
|
||||
|
||||
# Rett på forsiden: den som har en mening om en sak er ikke frivillig.
|
||||
- id: horingsinnspill
|
||||
who: en beboer som har en mening om en sak kommunen har på høring
|
||||
wants: at velet tar det med i sitt svar
|
||||
lands_in: innspill
|
||||
handled_by: horingsinstans
|
||||
watched_by: [styret]
|
||||
stages: [open, tatt_med, ikke_tatt_med]
|
||||
stages_text: { open: "mottatt", tatt_med: "tatt med i høringssvaret", ikke_tatt_med: "ikke tatt med" }
|
||||
done_when: [tatt_med, ikke_tatt_med]
|
||||
success: [tatt_med]
|
||||
max_fields: 3
|
||||
collects:
|
||||
- { name: navn }
|
||||
- { name: sak }
|
||||
- { name: innspill, type: textarea }
|
||||
|
||||
- id: hjelpe-til
|
||||
who: en som vil hjelpe til på et arrangement eller en dugnad
|
||||
wants: å bli satt opp på noe som passer dem
|
||||
lands_in: frivillige
|
||||
door: bidra
|
||||
handled_by: arrangementer
|
||||
stages: [open, kontaktet, satt_opp, takket_nei]
|
||||
stages_text: { open: "mottatt", kontaktet: "kontaktet", satt_opp: "satt opp", takket_nei: "takket nei" }
|
||||
done_when: [satt_opp, takket_nei]
|
||||
success: [satt_opp]
|
||||
max_fields: 3
|
||||
collects:
|
||||
- { name: navn }
|
||||
- { name: telefon, type: tel }
|
||||
- { name: hva, type: select, multiple: true, options: [17. mai, Juletregranstenning, Skøytedag, Dugnad på elvestien, Det som trengs] }
|
||||
|
||||
# Komitéplass er for medlemmer: døra viser veien, skjemaet ligger på
|
||||
# medlemmenes side bak innloggingen.
|
||||
- id: komite
|
||||
who: et medlem som vil sitte i en komité
|
||||
wants: en plass i den komiteen som driver med det de bryr seg om
|
||||
lands_in: komitesoknader
|
||||
door: bidra
|
||||
enters_as: medlemmer
|
||||
handled_by: styret
|
||||
stages: [open, plassert, avslatt]
|
||||
stages_text: { open: "mottatt", plassert: "plassert i en komité", avslatt: "avslått" }
|
||||
done_when: [plassert, avslatt]
|
||||
success: [plassert]
|
||||
max_fields: 3
|
||||
collects:
|
||||
- { name: navn }
|
||||
- { name: epost, type: email }
|
||||
- { name: komite, type: select, multiple: true, options_from: groups }
|
||||
|
||||
# Nabohjelp: den som trenger en hånd, og den som kan gi en. En
|
||||
# nabohjelpsgruppe kobler dem; den som fikk hjelp sier fra selv at
|
||||
# det er ordnet.
|
||||
- id: trenger-hjelp
|
||||
who: en beboer som ikke får til noe selv for tiden, som snømåking, handling eller en skyss
|
||||
wants: at en nabo stiller opp
|
||||
lands_in: hjelpebehov
|
||||
# Et behov ingen har tatt tak i på en uke går opp til styret: samme
|
||||
# sak, samme knapper, på styrets pult i tillegg.
|
||||
escalate: { open: { days: 7, to: styret } }
|
||||
door: naboer
|
||||
handled_by: nabohjelp
|
||||
stages: [open, formidlet, ordnet, ikke_lost]
|
||||
stages_text: { open: "mottatt", open_escalated: "hos styret, siden ingen tok den på en uke", formidlet: "formidlet til en nabo", ordnet: "ordnet", ikke_lost: "ikke løst" }
|
||||
done_when: [ordnet, ikke_lost]
|
||||
success: [ordnet]
|
||||
moves_by: { ordnet: [submitter, nabohjelp] }
|
||||
mails: { open: submitter, formidlet: submitter, ordnet: submitter }
|
||||
max_fields: 4
|
||||
collects:
|
||||
- { name: navn }
|
||||
- { name: email, type: email }
|
||||
- { name: adresse }
|
||||
- { name: hva, type: textarea }
|
||||
- { name: telefon, type: tel, optional: true }
|
||||
|
||||
- id: kan-hjelpe
|
||||
who: en nabo som kan stille opp når noen trenger det
|
||||
wants: å bli spurt når det passer
|
||||
lands_in: hjelpere
|
||||
door: naboer
|
||||
handled_by: nabohjelp
|
||||
stages: [open, registrert, trukket]
|
||||
stages_text: { open: "mottatt", registrert: "registrert som hjelper", trukket: "trukket" }
|
||||
done_when: [registrert, trukket]
|
||||
success: [registrert]
|
||||
moves_by: { trukket: [submitter, nabohjelp] }
|
||||
max_fields: 4
|
||||
collects:
|
||||
- { name: navn }
|
||||
- { name: email, type: email }
|
||||
- { name: telefon, type: tel }
|
||||
- { name: kan, type: select, multiple: true, options: [Snømåking, Handling, Kjøring, Småfiks i huset, En prat og litt selskap, Det som trengs] }
|
||||
|
||||
# Oppslagstavla: ut med en gang, styret hører om det. Svar går
|
||||
# rett til den som la ut; køen er innboksen deres. Tas ned av den
|
||||
# som la ut, av redaktøren, eller av klokka etter en måned.
|
||||
- id: dele-utstyr
|
||||
who: en nabo som har noe å låne ut, eller trenger å låne noe
|
||||
wants: et oppslag naboene ser med en gang
|
||||
lands_in: oppslag
|
||||
door: naboer
|
||||
handled_by: redaktor
|
||||
stages: [open, tatt_ned]
|
||||
stages_text: { open: "ute", tatt_ned: "tatt ned" }
|
||||
done_when: [tatt_ned]
|
||||
success: [tatt_ned]
|
||||
moves_by: { tatt_ned: [submitter, redaktor] }
|
||||
mails: { open: post@tomtervel.no }
|
||||
deadlines: { open: { days: 30, to: tatt_ned } }
|
||||
# Tavla er offentlig: det er den naboene leser; navn og e-post
|
||||
# vises ikke der.
|
||||
watched_by: [public]
|
||||
public_states: [open]
|
||||
max_fields: 4
|
||||
collects:
|
||||
- { name: navn, private: true }
|
||||
- { name: email, type: email }
|
||||
- { name: har_eller_trenger, type: select, options: [Jeg har noe å låne ut, Jeg trenger å låne noe] }
|
||||
- { name: hva, type: textarea }
|
||||
|
||||
# Svaret på et oppslag: fra oppslagets egen rad på tavla, med
|
||||
# e-post eller innlogget, rett til den som la ut. Redaktøren ser
|
||||
# svarene og merker dem lest.
|
||||
- id: melde-seg
|
||||
who: en nabo som kan låne ut, eller vil låne, det et oppslag gjelder
|
||||
wants: at den som la ut oppslaget får vite det
|
||||
about: dele-utstyr
|
||||
lands_in: svar
|
||||
handled_by: redaktor
|
||||
stages: [open, lest]
|
||||
stages_text: { open: "sendt", lest: "lest" }
|
||||
done_when: [lest]
|
||||
success: [lest]
|
||||
mails: { open: owner }
|
||||
max_fields: 2
|
||||
collects:
|
||||
- { name: email, type: email }
|
||||
- { name: hilsen, type: textarea, optional: true }
|
||||
|
||||
- id: utlegg
|
||||
who: en i en komité som har lagt ut for noe
|
||||
wants: pengene tilbake
|
||||
enters_as: komiteer
|
||||
lands_in: utlegg
|
||||
handled_by: kasserer
|
||||
stages: [open, godkjent, utbetalt, avvist]
|
||||
stages_text: { open: "mottatt", godkjent: "godkjent", utbetalt: "utbetalt", avvist: "avvist" }
|
||||
done_when: [utbetalt, avvist]
|
||||
success: [utbetalt]
|
||||
max_fields: 3
|
||||
collects:
|
||||
- { name: navn }
|
||||
- { name: belop }
|
||||
- { name: hva, type: textarea }
|
||||
|
||||
# Holdt unna alle modeller som skriver ord.
|
||||
personas:
|
||||
- { id: nyinnflyttet, need: bli-medlem, text: "Vi flyttet til Tomter i mars og har hørt at det finnes et vel. Hvordan blir vi med, og hva koster det?" }
|
||||
- { id: glemte-a-betale, need: bli-medlem, text: "Jeg har vært medlem før men fikk ikke betalt i fjor. Kan jeg bare vippse, og til hvem?" }
|
||||
- { id: ny-epost, need: endre-medlemskap, text: "Vi har byttet e-postadresse og får ikke lenger noe fra dere." }
|
||||
- { id: flytter, need: endre-medlemskap, text: "Vi selger huset og flytter til Ski i august, så vi må ut av medlemslista." }
|
||||
- { id: skolevei, need: trafikkfare, text: "Bilene kjører altfor fort i svingen ved skolen om morgenen, og det er ikke fortau der ungene går." }
|
||||
- { id: busslomme, need: trafikkfare, text: "Det er umulig å se om det kommer noen når man skal ut fra busslomma ved butikken, hekken har blitt kjempehøy." }
|
||||
- { id: huske, need: anleggsfeil, text: "Huska på lekeplassen henger i én kjetting. Sønnen min dro i den i går og den holdt så vidt." }
|
||||
- { id: bru, need: anleggsfeil, text: "Planken på den lille brua på stien langs elva er råtten, jeg gikk nesten gjennom." }
|
||||
- { id: blokker, need: horingsinnspill, text: "Kommunen skal bygge blokker på jordet bak stasjonen, og jeg synes det er alt for høyt. Kan velet si ifra?" }
|
||||
- { id: togtider, need: horingsinnspill, text: "Ruter vil kutte det tidlige toget. Jeg pendler til Oslo hver dag og vil at velet skriver til dem om det." }
|
||||
- { id: vafler, need: hjelpe-til, text: "Jeg kan steke vafler på 17. mai-frokosten hvis dere trenger folk." }
|
||||
- { id: motorsag, need: hjelpe-til, text: "Har motorsag og ledig helg, kan ta trær langs elvestien hvis det er en dugnad." }
|
||||
- { id: pendler, need: komite, text: "Jeg pendler og er lei av at ingen snakker med Ruter. Jeg vil gjerne være med i gjengen som jobber med det." }
|
||||
- { id: planlegger, need: komite, text: "Jeg er arealplanlegger av yrke og kunne bidratt når kommunen sender saker på høring." }
|
||||
- { id: tilhenger, need: dele-utstyr, text: "Jeg har en tilhenger som står ubrukt mesteparten av året, naboer kan gjerne låne den for en hundrelapp." }
|
||||
- { id: stige, need: dele-utstyr, text: "Trenger en lang stige en dag i helga for å rense takrenna, er det noen i nærheten som har?" }
|
||||
- { id: snomaking, need: trenger-hjelp, text: "Jeg er 84 og klarer ikke å måke innkjørselen lenger. Er det noen som kan hjelpe meg når det snør?" }
|
||||
- { id: nyoperert, need: trenger-hjelp, text: "Jeg er nyoperert i hofta og kommer meg ikke til butikken på noen uker. Kunne noen handlet for meg?" }
|
||||
- { id: hjemmekontor, need: kan-hjelpe, text: "Jeg jobber hjemmefra og kan godt måke eller handle for noen som ikke får det til selv." }
|
||||
- { id: pensjonert-elektriker, need: kan-hjelpe, text: "Pensjonert elektriker, har bil og god tid. Si fra om noen trenger skyss eller en hånd med småting." }
|
||||
- { id: kvittering, need: utlegg, text: "Jeg kjøpte pølser og kakao til skøytedagen for 1 840 kroner, her er kvitteringa, vil ha det igjen." }
|
||||
+233
@@ -0,0 +1,233 @@
|
||||
# UHHM, uhhm.no: the studio that builds portal and carries redoal.
|
||||
# Rebuilt from the live site (uhhm/questions) on 2026-09-23. The five
|
||||
# buckets the site already holds keep their live state and event
|
||||
# names (`events:`), so every record on the owners' desk stays where
|
||||
# it is. Two are new: a business trying the trainer on its own
|
||||
# workflow (`trials`, approved by a person before anything is built),
|
||||
# and what reaches UHHM as the custodian of the redoal network
|
||||
# (`custody_requests`, a desk of its own).
|
||||
business: UHHM
|
||||
language: en
|
||||
contact: { name: Bendik Aagaard Lynghaug, email: bl@uhhm.no }
|
||||
# Every mail the site sends comes from here (gdo on ergo delivers).
|
||||
mail: { from: "UHHM <portal@uhhm.no>", reply_to: bl@uhhm.no }
|
||||
about: >
|
||||
UHHM builds software around a clear argument: infrastructure, tools
|
||||
and products for organizations that can say what problem they have.
|
||||
It also builds and carries the redoal network - the gesture math,
|
||||
the relay, the first node, and the portal this site runs on - and
|
||||
answers for what that network keeps.
|
||||
|
||||
groups:
|
||||
- { id: owners, label: Owners }
|
||||
# The people who answer for the redoal network: they host its first
|
||||
# node and relay, keep what is left there, and decide what stays.
|
||||
- { id: custodians, label: redoal custodians, invited_by: [owners] }
|
||||
# Owners onboard into every desk; a custodian can vouch for another.
|
||||
invites_by: [owners]
|
||||
|
||||
# The front door opens modestly: one or two features, one role each,
|
||||
# and no button, before the offers further down the same page. The
|
||||
# two roles UHHM exists for: an organization with a problem, and the
|
||||
# people on the redoal network it keeps.
|
||||
does:
|
||||
- id: software
|
||||
for: commission
|
||||
text: >
|
||||
Software built around a clear argument. Infrastructure, tools
|
||||
and products for organizations that can say what problem they
|
||||
have - and a working draft of your own workflow as a site,
|
||||
before you pay for anything.
|
||||
- id: redoal
|
||||
for: custody
|
||||
text: >
|
||||
The redoal network, built and carried here: the gesture math,
|
||||
the relay, the first node, and the portal this site runs on.
|
||||
UHHM answers for what that network keeps.
|
||||
|
||||
# Things to look at before asking for anything.
|
||||
shows:
|
||||
- id: built
|
||||
text: A few things already out in the world.
|
||||
resource:
|
||||
public: true
|
||||
source: { kind: gitea_starred, username: bl }
|
||||
jq: >
|
||||
.[] | {name: .name, description: .description,
|
||||
url: (if .website != null and .website != "" then .website
|
||||
elif .private then null
|
||||
else .html_url end)}
|
||||
- id: voices
|
||||
text: Live from the redoal relay - what has been left at places so far.
|
||||
resource:
|
||||
public: true
|
||||
source: { kind: url, url: https://relay.redoal.com/pulse }
|
||||
jq: >
|
||||
{ "name": (.voices_echoed | tostring),
|
||||
"description": "voices echoed so far" }
|
||||
empty: The first voice has not echoed yet.
|
||||
|
||||
needs:
|
||||
- id: commission
|
||||
who: an organization with a problem it wants software to solve
|
||||
wants: a considered reply to a brief, from people who read the whole thing
|
||||
lands_in: projects
|
||||
handled_by: owners
|
||||
stages: [open, accepted, declined]
|
||||
done_when: [accepted, declined]
|
||||
success: [accepted]
|
||||
events: { open: submitted }
|
||||
stages_text: { open: "received", accepted: "taken on", declined: "not a fit for us" }
|
||||
# The client plays too: they can withdraw the brief themselves.
|
||||
moves_by: { declined: [submitter, owners] }
|
||||
mails: { open: submitter, accepted: submitter, declined: submitter }
|
||||
max_fields: 4
|
||||
collects:
|
||||
- { name: name }
|
||||
- { name: email, type: email }
|
||||
- { name: project, type: textarea }
|
||||
- { name: organization }
|
||||
- { name: org_contact, optional: true }
|
||||
|
||||
# The client feature: the trainer tried on a real business. A person
|
||||
# approves each intake before anything is built from it.
|
||||
- id: trial
|
||||
who: someone who runs a business where requests, approvals and handovers get lost, and wants to see it as a site before committing to anything
|
||||
wants: a first draft of a site for their own roles, built from their own words and approved by a person first
|
||||
lands_in: trials
|
||||
handled_by: owners
|
||||
stages: [open, approved, previewed, declined]
|
||||
done_when: [previewed, declined]
|
||||
success: [previewed]
|
||||
stages_text: { open: "received", approved: "approved for a draft", previewed: "a preview is ready", declined: "not a fit for us" }
|
||||
mails: { open: submitter, approved: submitter, previewed: submitter, declined: submitter }
|
||||
attended_by: >
|
||||
approved -> previewed: the n8n workflow that builds the draft
|
||||
site from an approved intake (docker, rauthy, portal content)
|
||||
and sends the preview link. Until it exists, owners run
|
||||
portal-trainer by hand and press the button.
|
||||
max_fields: 6
|
||||
collects:
|
||||
- { name: business }
|
||||
- { name: email, type: email }
|
||||
- { name: what_we_do, type: textarea }
|
||||
- { name: who_comes_to_us, type: textarea }
|
||||
- { name: what_we_keep_track_of, type: textarea }
|
||||
- { name: who_is_involved, type: textarea }
|
||||
- { name: what_slows_us_down, type: textarea, optional: true }
|
||||
|
||||
- id: collaborate
|
||||
who: someone who wants to work with us on what we build
|
||||
wants: to be read as a person, and invited in if there is a fit
|
||||
lands_in: applicants
|
||||
handled_by: owners
|
||||
stages: [open, invited, active, departed, declined]
|
||||
done_when: [departed, declined]
|
||||
success: [departed]
|
||||
events: { open: applied, active: onboarded }
|
||||
# Declined before starting; after that a person departs.
|
||||
turned_down_from: [open, invited]
|
||||
stages_text: { open: "received", invited: "invited in", active: "working with us", departed: "moved on", declined: "not a fit for us" }
|
||||
max_fields: 3
|
||||
collects:
|
||||
- { name: name }
|
||||
- { name: email, type: email }
|
||||
- { name: interest, type: textarea }
|
||||
- { name: note, type: textarea, optional: true }
|
||||
|
||||
- id: subscribe
|
||||
who: someone who wants to hear from us when there is something to say
|
||||
wants: a note now and then, and an easy way out
|
||||
lands_in: subscribers
|
||||
handled_by: owners
|
||||
stages: [open, unsubscribed]
|
||||
done_when: [unsubscribed]
|
||||
events: { open: subscribed }
|
||||
stages_text: { open: "on the list", unsubscribed: "off the list" }
|
||||
moves_by: { unsubscribed: [submitter, owners] }
|
||||
max_fields: 2
|
||||
collects:
|
||||
- { name: name }
|
||||
- { name: email, type: email }
|
||||
|
||||
# This site proposes its own changes through itself.
|
||||
- id: propose
|
||||
who: a visitor who sees how a page on this site should change
|
||||
wants: the change read by a person and, if approved, made
|
||||
lands_in: development_proposals
|
||||
handled_by: owners
|
||||
stages: [open, approved, rejected]
|
||||
done_when: [approved, rejected]
|
||||
success: [approved]
|
||||
events: { open: proposed }
|
||||
stages_text: { open: "received", approved: "approved", rejected: "turned down" }
|
||||
# The site changes in the open: anyone can read what was approved,
|
||||
# without who proposed it.
|
||||
watched_by: [public]
|
||||
public_states: [approved]
|
||||
max_fields: 3
|
||||
collects:
|
||||
- { name: submitted_by, private: true }
|
||||
- { name: email, type: email, optional: true }
|
||||
- { name: page, optional: true }
|
||||
- { name: rationale, type: textarea }
|
||||
- { name: proposed_yaml, type: textarea }
|
||||
|
||||
# The custodian role, on the front door: UHHM answers for the redoal
|
||||
# network it hosts. Owners see these too, and move nothing.
|
||||
- id: custody
|
||||
who: someone who runs, builds on, or is affected by the redoal network and needs the people who keep its first node
|
||||
wants: an answer from a custodian, and action where the network keeps something it should not
|
||||
lands_in: custody_requests
|
||||
handled_by: custodians
|
||||
watched_by: [owners]
|
||||
stages: [open, in_dialogue, resolved, declined]
|
||||
done_when: [resolved, declined]
|
||||
success: [resolved]
|
||||
stages_text: { open: "received", in_dialogue: "in dialogue with a custodian", resolved: "resolved", declined: "closed without action" }
|
||||
# The person affected says when it is resolved, not only the custodian.
|
||||
moves_by: { resolved: [submitter, custodians] }
|
||||
mails: { open: submitter, resolved: submitter, declined: submitter }
|
||||
max_fields: 4
|
||||
collects:
|
||||
- { name: name }
|
||||
- { name: email, type: email }
|
||||
- { name: about, type: select, options: [Running a node of my own, Building on the network, Something the network keeps about me, Something else] }
|
||||
- { name: what, type: textarea }
|
||||
|
||||
# Internal: a relationship worth tracking before it is a project.
|
||||
- id: prospect
|
||||
who: an owner who has met an organization worth following up
|
||||
wants: it written down and walked through its states from the desk
|
||||
enters_as: owners
|
||||
lands_in: organizations
|
||||
handled_by: owners
|
||||
stages: [prospect, client, past_client]
|
||||
done_when: [past_client]
|
||||
skippable: [client]
|
||||
stages_text: { prospect: "a prospect", client: "a client", past_client: "a past client" }
|
||||
events: { prospect: identified, client: became_client, past_client: churned }
|
||||
max_fields: 2
|
||||
collects:
|
||||
- { name: name }
|
||||
- { name: contact }
|
||||
- { name: note, type: textarea, optional: true }
|
||||
|
||||
# Held out from every model that writes words. The first eight are the
|
||||
# 2026-09-21 set (runs/uhhm-2026-09-21); the rest are for what is new.
|
||||
personas:
|
||||
- { id: architecture-firm, need: commission, text: "We run a small architecture firm and our project files are scattered across three cloud drives. We want to pay someone to set up proper storage and backups we control ourselves." }
|
||||
- { id: climbing-gym-founder, need: commission, text: "I have an idea for a booking app for climbing gyms and I'm looking for a team that can build the first version with me." }
|
||||
- { id: municipality-buyer, need: commission, text: "Our municipality needs an internal tool for tracking building permits. We are looking for a vendor who can scope it and deliver it." }
|
||||
- { id: exhibition-visitor, need: subscribe, text: "I saw your drawing network at an exhibition and liked it. I don't want to get involved, I'd just like to know when there is news." }
|
||||
- { id: tech-journalist, need: subscribe, text: "I write about Nordic technology. Keep me posted on what you release so I can cover it." }
|
||||
- { id: rust-developer, need: collaborate, text: "I'm a Rust developer between jobs and I like that you build in the open. I would love to contribute, and maybe join you." }
|
||||
- { id: interaction-designer, need: collaborate, text: "I'm an interaction designer. I want to work with you on the drawing interface." }
|
||||
- { id: student-intern, need: collaborate, text: "I'm a computer science student looking for a summer internship doing infrastructure work." }
|
||||
- { id: plumber-owner, need: trial, text: "I run a plumbing company with four vans. Jobs come in by phone and text and some get lost. I'd like to see how this would look for us before I spend any money." }
|
||||
- { id: dance-school, need: trial, text: "We are a dance school. Enrolments, cancellations and invoices all go through one email inbox that only I understand. Can you show me a version made for us?" }
|
||||
- { id: node-runner, need: custody, text: "I run a small server in Berlin and want to host a redoal node that peers with yours. Who do I talk to?" }
|
||||
- { id: recorded-name, need: custody, text: "Someone left a recording on redoal that says my full name and where I live. I want it gone, and I want to know who is responsible for that network." }
|
||||
- { id: app-builder, need: custody, text: "I'm building a music app and would like to use redoal's gesture addresses in it. Is that possible, and who decides?" }
|
||||
- { id: wording-fix, need: propose, text: "The subscribe page should say how often you actually write. I'd like to suggest that exact change to the page." }
|
||||
- { id: rewrite, need: propose, text: "One page on this site reads wrong to me and I know exactly how I'd rewrite it. Where do I send the new text?" }
|
||||
Reference in New Issue
Block a user