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
295 lines
14 KiB
YAML
295 lines
14 KiB
YAML
# 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." }
|