One binary: the lint and the trainer are commands of portal
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:
Bendik Aagaard Lynghaug
2026-09-30 15:29:30 +02:00
co-authored by Claude Fable 5.1
parent d0d1cbf23d
commit 96b3746643
1733 changed files with 183 additions and 150652 deletions
+113
View File
@@ -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." }