diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 86b0a8b..904fae0 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -18,7 +18,7 @@ on: - .gitea/workflows/deploy.yml env: - PORTAL_RELEASE: v0.3.36 + PORTAL_RELEASE: v0.3.42 INSTANCE: uhhm-portal jobs: diff --git a/.gitea/workflows/lint-and-reload.yml b/.gitea/workflows/lint-and-reload.yml index 23a0af7..2cdd713 100644 --- a/.gitea/workflows/lint-and-reload.yml +++ b/.gitea/workflows/lint-and-reload.yml @@ -20,7 +20,7 @@ jobs: # this site runs (PORTAL_RELEASE in deploy.yml). - name: Lint questions env: - IRIS_RELEASE: v0.3.39 + IRIS_RELEASE: v0.3.42 run: | # The checkout is on a noexec mount for this runner; the cargo # target dir is where it may run what it fetched. diff --git a/aggregates.yaml b/aggregates.yaml index 33dc046..cccafd2 100644 --- a/aggregates.yaml +++ b/aggregates.yaml @@ -1,68 +1,113 @@ -# The full state graph for every bucket that wants event-sourced, -# CAS-protected transitions - see portal's src/aggregates/mod.rs. A -# bucket with no entry here still works (plain KV mutate-in-place, no -# event log). `bucket` doubles as both the KV bucket name and the -# JetStream event-subject segment (events..) - one name, -# not two. -# -# Pages (review.yaml, subscribed.yaml, index.yaml) reference these -# bucket names and `to:` state names by string; they never re-declare -# the graph itself. Add a new aggregate here alone - no portal code -# change needed. aggregates: -# One continuous graph per person, applicant through employee - the -# bucket name stays "applicants" even as a record progresses past the -# applying stage (a deliberate trade: one chain of events per person, -# no identity-linking between separate buckets). - - bucket: applicants - initial: open - states: - open: { event: applied } - invited: { event: invited } - declined: { event: declined } - active: { event: onboarded } - departed: { event: departed } - transitions: - open: [invited, declined] - invited: [active, declined] - active: [departed] - - - bucket: subscribers - initial: open - states: - open: { event: subscribed } - unsubscribed: { event: unsubscribed } - transitions: - open: [unsubscribed] - - - bucket: projects - initial: open - states: - open: { event: submitted } - accepted: { event: accepted } - declined: { event: declined } - transitions: - open: [accepted, declined] - - - bucket: organizations - initial: prospect - states: - prospect: { event: identified } - client: { event: became_client } - past_client: { event: churned } - transitions: - prospect: [client, past_client] - client: [past_client] - -# Proposals to change this very repo - the question architecture -# dogfooding its own development. Approval hands off to an n8n -# automation that opens a PR against this repo (see the infrastructure -# repo's development-commit workflow); the lint CI gates the merge. - - bucket: development_proposals - initial: open - states: - open: { event: proposed } - approved: { event: approved } - rejected: { event: rejected } - transitions: - open: [approved, rejected] +- bucket: applicants + initial: open + states: + active: + event: onboarded + declined: + event: declined + departed: + event: departed + invited: + event: invited + open: + event: applied + transitions: + active: + - departed + - declined + invited: + - active + - declined + open: + - invited + - declined +- bucket: custody_requests + initial: open + states: + declined: + event: declined + in_dialogue: + event: in_dialogue + open: + event: received + resolved: + event: resolved + transitions: + in_dialogue: + - resolved + - declined + open: + - in_dialogue + - declined +- bucket: development_proposals + initial: open + states: + approved: + event: approved + open: + event: proposed + rejected: + event: rejected + transitions: + open: + - approved + - rejected +- bucket: organizations + initial: prospect + states: + client: + event: became_client + past_client: + event: churned + prospect: + event: identified + transitions: + client: + - past_client + prospect: + - client + - past_client +- bucket: projects + initial: open + states: + accepted: + event: accepted + declined: + event: declined + open: + event: submitted + transitions: + open: + - accepted + - declined +- bucket: subscribers + initial: open + states: + open: + event: subscribed + unsubscribed: + event: unsubscribed + transitions: + open: + - unsubscribed +- 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. + bucket: trials + initial: open + states: + approved: + event: approved + declined: + event: declined + open: + event: received + previewed: + event: previewed + transitions: + approved: + - previewed + - declined + open: + - approved + - declined diff --git a/needs.yaml b/needs.yaml new file mode 100644 index 0000000..39df5ec --- /dev/null +++ b/needs.yaml @@ -0,0 +1,173 @@ +needs: +- done_when: + - accepted + - declined + enters: / + handled_by: owners + id: commission + lands_in: projects + max_fields: 4 + max_steps: 2 + stages: + - open + - accepted + - declined + success: + - accepted + via: You run an organization that needs software built + wants: a considered reply to a brief, from people who read the whole thing + who: an organization with a problem it wants software to solve +- done_when: + - previewed + - declined + enters: / + handled_by: owners + id: trial + lands_in: trials + max_fields: 6 + max_steps: 2 + stages: + - open + - approved + - previewed + - declined + success: + - previewed + via: You run an organization that needs software built + wants: a first draft of a site for their own roles, built from their own words and approved by a person first + 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 +- done_when: + - departed + - declined + enters: / + handled_by: owners + id: collaborate + lands_in: applicants + max_fields: 3 + max_steps: 2 + stages: + - open + - invited + - active + - departed + - declined + success: + - departed + via: You want to join us, follow us, or fix a page on this site + wants: to be read as a person, and invited in if there is a fit + who: someone who wants to work with us on what we build +- done_when: + - unsubscribed + enters: / + handled_by: owners + id: subscribe + lands_in: subscribers + max_fields: 2 + max_steps: 2 + stages: + - open + - unsubscribed + success: [] + via: You want to join us, follow us, or fix a page on this site + wants: a note now and then, and an easy way out + who: someone who wants to hear from us when there is something to say +- done_when: + - approved + - rejected + enters: / + handled_by: owners + id: propose + lands_in: development_proposals + max_fields: 3 + max_steps: 2 + stages: + - open + - approved + - rejected + success: + - approved + via: You want to join us, follow us, or fix a page on this site + wants: the change read by a person and, if approved, made + who: a visitor who sees how a page on this site should change +- done_when: + - resolved + - declined + enters: / + handled_by: custodians + id: custody + lands_in: custody_requests + max_fields: 4 + max_steps: 1 + stages: + - open + - in_dialogue + - resolved + - declined + success: + - resolved + via: 'You are on the redoal network: a node, an app, or a recording that concerns you' + wants: an answer from a custodian, and action where the network keeps something it should not + who: someone who runs, builds on, or is affected by the redoal network and needs the people who keep its first node +- done_when: + - past_client + enters: /owners/prospect + enters_as: owners + handled_by: owners + id: prospect + lands_in: organizations + max_fields: 2 + max_steps: 1 + stages: + - prospect + - client + - past_client + success: [] + via: Keep an organization on your radar + wants: it written down and walked through its states from the desk + who: an owner who has met an organization worth following up +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? diff --git a/questions/applied.yaml b/questions/applied.yaml deleted file mode 100644 index 92e63aa..0000000 --- a/questions/applied.yaml +++ /dev/null @@ -1,25 +0,0 @@ -followup: true -name: What happens now that you've applied? -description: > - We're not filling a seat — we're looking for exactly what you bring - that we don't already have. We read every one of these ourselves, in - full, and write back. -alternatives: - - name: Thanks — it's in front of us - description: "" - features: - - name: We need your specific angle - description: > - Not a generic fit — the particular thing you're good at, the - problem you'd actually notice, the take nobody else here - has. That's what we're reading for. - - name: Work happens through dialogue, not tickets - description: > - What you bring shapes what gets built, in an ongoing back - -and-forth — not a queue of pre-decided tasks handed down to - execute. - - name: You receive a personal invitation - description: > - A match earns you a real key — access, a measure of - responsibility, and eventually, the standing to invite - someone else in. diff --git a/questions/build/index.yaml b/questions/build/index.yaml new file mode 100644 index 0000000..2439592 --- /dev/null +++ b/questions/build/index.yaml @@ -0,0 +1,94 @@ +alternatives: +- action: /received/commission + consequence: + - Send the brief + description: You know the problem and want it solved, whether it is infrastructure, a tool, or the first version of a product. Write it up for people who read the whole thing. + encouragements: + - Someone here reads every brief in full before writing back. + features: + - description: We read your brief and either take it on or tell you honestly if it's not a fit. + icon: lucide:route + name: What happens next + - description: One of the owners reads it; write to bl@uhhm.no if you'd rather reach us directly. + icon: lucide:user-check + name: Who reads this + - description: The more of the problem you put in, the better the reply. + icon: lucide:briefcase + name: Tell us the problem + requirements: + - label: Your name + name: name + placeholder: Sara Nilsen + type: text + - label: Your email + name: email + placeholder: sara@company.com + type: email + - label: The problem you want solved + name: project + placeholder: Our drivers log hours on paper and it takes a day to get them into payroll. + type: textarea + - label: Your organization + name: organization + placeholder: Nilsen Transport AS + type: text + - label: Who we should talk to + name: org_contact + optional: true + placeholder: Sara Nilsen, ops manager + type: text + name: 'Have us build it: send the brief' + record_as: projects +- action: /received/trial + consequence: + - Start the draft + description: Describe how requests, approvals and handovers move through your business today. We approve it by hand and build a working draft of a site for your own roles, for you to click through. + encouragements: + - A person looks at what you send before anything gets built. + features: + - description: We look at what you sent and, if it's a fit, build a draft from your own roles and words for you to see before anything moves further. + icon: lucide:route + name: What happens next + - description: An owner looks at what you send; write to bl@uhhm.no if you'd rather reach us directly. + icon: lucide:user-check + name: Who reads this + - description: Answer in your own words - there's no wrong way to describe your business. + icon: lucide:layers + name: Describe your business + requirements: + - label: Your business + name: business + placeholder: Nilsen Transport AS + type: text + - label: Your email + name: email + placeholder: sara@company.com + type: email + - label: What you do + name: what_we_do + placeholder: We move freight for local businesses and handle the paperwork for them. + type: textarea + - label: Who comes to you + name: who_comes_to_us + placeholder: Local businesses that need something moved on short notice. + type: textarea + - label: What you keep track of + name: what_we_keep_track_of + placeholder: Bookings, driver hours, and which loads are still waiting on a signature. + type: textarea + - label: Who's involved + name: who_is_involved + placeholder: Drivers, dispatchers, and the customers waiting on their delivery. + type: textarea + - label: What slows you down + name: what_slows_us_down + optional: true + placeholder: Approvals sit in someone's inbox for days before anyone acts on them. + type: textarea + name: See your own business as a site, before you buy anything + record_as: trials +description: Have a problem software should solve, or want to see your own way of working as a site before you pay anyone? +name: Do you have a brief, or do you want to see your business as a site first? +responsible: + contact: bl@uhhm.no + name: Bendik Aagaard Lynghaug diff --git a/questions/custodians/_section.yaml b/questions/custodians/_section.yaml new file mode 100644 index 0000000..de40ef5 --- /dev/null +++ b/questions/custodians/_section.yaml @@ -0,0 +1 @@ +qualifies: custodians diff --git a/questions/custodians/index.yaml b/questions/custodians/index.yaml new file mode 100644 index 0000000..6a0b293 --- /dev/null +++ b/questions/custodians/index.yaml @@ -0,0 +1,65 @@ +alternatives: +- action: /custodians + consequence: + - Confirm + description: Cases from people who use, build on, or answer for something the redoal network touches, and need a custodian's word on it. + features: + - description: '' + name: '' + resource: + requires_group: custodians + source: + bucket: custody_requests + kind: kv + transitions: + - from: in_dialogue + label: Mark resolved + to: resolved + - from: in_dialogue + label: Close, no action + to: declined + - from: open + label: Open dialogue + to: in_dialogue + - from: open + label: Decline case + to: declined + name: Custody cases waiting on you +- action: /custodians + consequence: + - Invite custodian + description: Give someone an account and a one-time link to join as a custodian. + features: + - description: '' + name: Invite a custodian + requirements: + - label: Name + name: name + - label: Email + name: email + type: email + invite: + group: custodians + name: Bring in another custodian +- action: /custodians + consequence: + - Confirm + description: Custodians invited but not yet signed in. + features: + - description: '' + name: '' + resource: + requires_group: custodians + source: + bucket: portal_invites + kind: kv + transitions: + - from: open + label: Signed in + to: done + name: Invited custodians +description: 'The desk for custodians: open custody cases, and the people invited to answer them.' +name: What is waiting on the custodians? +responsible: + contact: bl@uhhm.no + name: Bendik Aagaard Lynghaug diff --git a/questions/decide/subscribe.yaml b/questions/decide/subscribe.yaml new file mode 100644 index 0000000..45114ca --- /dev/null +++ b/questions/decide/subscribe.yaml @@ -0,0 +1,9 @@ +alternatives: +- description: You'll stop hearing from us. Nothing else changes. + name: Stop getting notes from us + self_transition: + bucket: subscribers + label: Unsubscribe + to: unsubscribed +description: You're on our list. Change your mind whenever you like. +name: Do you want to keep hearing from us? diff --git a/questions/develop/index.yaml b/questions/develop/index.yaml deleted file mode 100644 index c7d8180..0000000 --- a/questions/develop/index.yaml +++ /dev/null @@ -1,24 +0,0 @@ -name: What's been proposed? -qualifies: owners -description: > - Proposed changes to the questions repo, awaiting a decision. - Approval hands off to the automation that opens the pull request — - lint gates the merge, not this page. -alternatives: - - name: Proposals - description: "" - action: /develop - consequence: [Confirm] - features: - - name: "" - description: "" - resource: - source: - kind: kv - bucket: development_proposals - requires_group: owners - transitions: - - to: approved - label: Approve - - to: rejected - label: Reject diff --git a/questions/develop/proposal.yaml b/questions/develop/proposal.yaml deleted file mode 100644 index 2913ad0..0000000 --- a/questions/develop/proposal.yaml +++ /dev/null @@ -1,90 +0,0 @@ -# Nested for the flow, but a public entry point - without this it -# would infer followup and drop out of the nav. -followup: false -name: How can this website improve? -description: > - This site is built on the same question architecture you're using - right now — and changes to it are proposed through it. Tell us what - should change and why; an owner reads every proposal, and approved - ones become real commits. -alternatives: - - name: The change you propose - description: > - The proposed content replaces (or creates) one content file — - a page under questions/, or aggregates.yaml — verbatim, so it - has to be complete, valid YAML for that file, not a diff. - Nothing outside those files is in scope. - action: proposed - # Announced, not yet open: the automation that turns a proposal - # into a pull request is still being built. - disabled: true - consequence: [Propose it] - record_as: development_proposals - encouragements: - - Every proposal gets read by a person before anything moves. - features: - - name: The reference - icon: lucide:book-open - description: > - Every page this site serves, verbatim — check what exists - before proposing against it. - resource: - public: true - source: - kind: url - url: https://project.uhhm.no/api/v1/repos/uhhm/questions - jq: > - {name: .full_name, description: .description, url: .html_url} - - name: Sign it - icon: lucide:pen-line - description: > - A proposal is a stance — it carries a name, and an address - if you want the outcome to reach you. - requirements: - - name: submitted_by - label: Name (or what to call you) - - name: email - label: Email - type: email - optional: true - - name: The change - icon: lucide:git-branch - description: > - Pick the page you're changing and its current content loads - below, ready to edit in place — or name a new file's path if - the proposal creates one. - requirements: - - name: target_path - label: The file to change - type: select - optional: true - id_field: path - resource: - public: true - source: - kind: url - # The whole tree, not one directory: since the tree became - # the router, pages live in subdirectories too. - url: https://project.uhhm.no/api/v1/repos/uhhm/questions/git/trees/main?recursive=true - jq: > - .tree[] | select(.type == "blob" and (.path | startswith("questions/")) and (.path | endswith(".yaml"))) - | {path: .path, name: .path} - - name: new_path - label: Or a new file's path - placeholder: questions/example.yaml - optional: true - - name: rationale - label: The rationale - type: textarea - - name: proposed_yaml - label: Proposed file content (complete YAML) - type: textarea - bind: - field: target_path - param: path - resource: - public: true - source: - kind: url - url: https://project.uhhm.no/api/v1/repos/uhhm/questions/contents/{path}?ref=main - jq: ".content | @base64d" diff --git a/questions/develop/proposed.yaml b/questions/develop/proposed.yaml deleted file mode 100644 index 879a9cf..0000000 --- a/questions/develop/proposed.yaml +++ /dev/null @@ -1,18 +0,0 @@ -# Reached from the proposal form only - and provably so: the visitor -# must hold a ?chain= lineage ending at an answer to proposal.yaml. -requires_chain: proposal -name: What happens to your proposal? -description: > - It's in the queue. An owner reads every proposal in full — approved - ones open a real pull request, and the same checks every human - commit passes gate the merge. -alternatives: - - name: The path from here - description: "" - features: - - name: A person decides - description: Nothing merges on autopilot — approval is a deliberate act. - - name: The linter has no favorites - description: > - Approved or not, your YAML faces the same validation as - everything else in the repo. diff --git a/questions/index.yaml b/questions/index.yaml index 431ace4..43b5b34 100644 --- a/questions/index.yaml +++ b/questions/index.yaml @@ -1,113 +1,63 @@ -name: What are you here for? -description: > - Strategies that address problems — applied in software. -responsible: - name: Bendik Aagaard Lynghaug - contact: bl@uhhm.no alternatives: - - name: Good software begins with a clear argument - description: > - Infrastructure, tools, or a product — bring the problem, and we - read the whole brief before answering. - action: /project - consequence: [Tell us what you're after] - record_as: projects - encouragements: - - We read every word before breaking ground. - features: - - name: What we've built - icon: lucide:layers - description: A few things already out in the world. - resource: - source: - kind: gitea_starred - username: bl - public: true - # The link prefers the repo's Website setting so private - # repos can point at something the public can actually - # open; a private repo with no website gets no link at all - # (the card renders an unlinked heading) instead of a 404. - # TODO: star counts (Gitea's .stars_count) are left out until - # they're worth showing - add `stars: .stars_count` back here. - jq: > - .[] | {name: .name, description: .description, - url: (if .website != null and .website != "" then .website - elif .private then null - else .html_url end)} - - name: Your brief - icon: lucide:file-text - description: > - A few lines are enough to begin a conversation — we need a - name and an address because the reply is personal, and the - organization because software always lands somewhere. - requirements: - - name: name - label: Name - - name: email - label: Email - type: email - - name: project - label: The build - type: textarea - placeholder: Just the shape of it — we'll ask the right questions. - - name: organization - label: Organization - - name: org_contact - label: Their contact, if not you - optional: true - - - name: Attention is earned - description: > - Considered notes on what we're building, and why — sent only when - there's something worth saying. - action: /subscribed - consequence: [Subscribe] - record_as: subscribers - encouragements: - - Consider yourself posted. - features: - - name: Where to send it - icon: lucide:mail - description: > - Notes go to a person, not a list — a name and an address is - all it takes. - requirements: - - name: name - label: Name - - name: email - label: Email - type: email - - - name: Work happens through dialogue, not tickets - description: > - What you bring shapes what gets built. We onboard collaborators - the same deliberate way we build — by hand, through a real - conversation. - action: /applied - consequence: [Apply] - record_as: applicants - encouragements: - - Knock — we actually answer. - features: - - name: A way to reach you - icon: lucide:at-sign - description: The reply is personal — it needs a name and an address. - requirements: - - name: name - label: Name - - name: email - label: Email - type: email - - name: Your angle - icon: lucide:compass - description: > - We build around what each person actually brings — say what - that is, in your own words. - requirements: - - name: interest - label: The work you're drawn to - type: textarea - - name: note - label: Anything else worth knowing - type: textarea - optional: true +- action: /build + consequence: + - Start here + description: Send us a brief, or first describe how work flows through your business and see it as a working site before you pay for anything. + features: + - description: '' + icon: lucide:hammer + name: For a business or an organization with a job for us + name: You run an organization that needs software built +- action: /join + consequence: + - Start here + description: A developer, designer or student who wants in; someone who wants a note now and then; or a reader who would reword a page on uhhm.no. + features: + - description: '' + icon: lucide:handshake + name: For a person, on their own behalf + name: You want to join us, follow us, or fix a page on this site +- action: /received/custody + consequence: + - Send to a custodian + description: UHHM hosts the redoal network's first node and answers for what it keeps. Write here if you run a node, build on the network, or want something it holds about you looked at. + encouragements: + - A custodian reads every message that comes in here. + features: + - description: A custodian opens a dialogue with you. A node gets its peer, a builder gets an answer, and something the network should not keep gets removed. Either way you hear what was decided. + icon: lucide:route + name: What happens next + - description: A custodian reads it directly; if it's urgent, write to bl@uhhm.no in the meantime. + icon: lucide:user-check + name: Who reads this + - description: Say which of the four it is, and what you need, in a few lines. + icon: lucide:shield + name: Reach a custodian + requirements: + - label: Name + name: name + placeholder: Your name + type: text + - label: Email + name: email + placeholder: you@yourcompany.com + type: email + - label: What's going on + name: about + options: + - Running a node of my own + - Building on the network + - Something the network keeps about me + - Something else + type: select + - label: In short + name: what + placeholder: A relay node is misbehaving + type: textarea + name: 'You are on the redoal network: a node, an app, or a recording that concerns you' + record_as: custody_requests +description: UHHM builds software for organisations that can say what problem they have, and keeps the redoal network underneath. +name: Who are you, and what do you need from UHHM? +responsible: + contact: bl@uhhm.no + name: Bendik Aagaard Lynghaug diff --git a/questions/join/index.yaml b/questions/join/index.yaml new file mode 100644 index 0000000..450e4c6 --- /dev/null +++ b/questions/join/index.yaml @@ -0,0 +1,110 @@ +alternatives: +- action: /received/collaborate + consequence: + - Get in touch + description: 'A developer, a designer, a student: say what you would want to build with us, and we read it as a person and invite you in if there is a fit.' + encouragements: + - A person reads this, not a form filter — worth the ten minutes. + features: + - description: We read what you send and get back to you. If there's a fit, we invite you in and you start working with us — and if things ever change, that's fine too. + icon: lucide:route + name: What happens next + - description: One of the owners reads this directly — for anything urgent, write to bl@uhhm.no. + icon: lucide:user-check + name: Who reads this + - description: Tell us who you are and what kind of work you're after. + icon: lucide:handshake + name: Say hello + requirements: + - label: Name + name: name + placeholder: Your name + type: text + - label: Email + name: email + placeholder: you@example.com + type: email + - label: What kind of work + name: interest + placeholder: Backend work, or something design-heavy + type: textarea + - label: Tell us more + name: note + optional: true + placeholder: A bit about what you'd want to work on, or a link to your work + type: textarea + name: Work with us + record_as: applicants +- action: /received/subscribe + consequence: + - Sign up + description: You'd rather hear from us now and then than check back on your own. + encouragements: + - One click unsubscribes you later, no questions asked. + features: + - description: You're on the list. We write when there's something worth saying, and you can leave the list whenever you like. + icon: lucide:bell + name: What happens next + - description: An owner sends these notes directly — write to bl@uhhm.no if you'd rather ask something first. + icon: lucide:user-check + name: Who reads this + - description: Just your email, or your name too if you'd like. + icon: lucide:mail + name: Stay in the loop + requirements: + - label: Name + name: name + placeholder: Your name + type: text + - label: Email + name: email + placeholder: you@example.com + type: email + name: Hear from us now and then + record_as: subscribers +- action: /received/propose + consequence: + - Send change + description: You have spotted a page on uhhm.no that should read differently, and you know roughly how. + encouragements: + - Even a rough version helps — we can tidy the wording ourselves. + features: + - description: Someone here reads your proposal and either makes the change or explains why not. Either way, you'll hear back. + icon: lucide:route + name: What happens next + - description: An owner reads every proposal before anything changes — reach us at bl@uhhm.no if it's time-sensitive. + icon: lucide:user-check + name: Who reads this + - description: Point us to the page and show us the change. + icon: lucide:pencil + name: Propose a change + requirements: + - label: Your name + name: submitted_by + placeholder: Your name + type: text + - label: Email + name: email + optional: true + placeholder: you@example.com + type: email + - label: Which page + name: page + optional: true + placeholder: questions/join/index.yaml + type: text + - label: Why this is better + name: rationale + placeholder: The current heading doesn't say what you actually get + type: textarea + - label: Your proposed text + name: proposed_yaml + placeholder: 'heading: "A clearer way to say this"' + type: textarea + name: Suggest a change to this website + record_as: development_proposals +description: 'Three ways to be part of what UHHM builds: work with us, hear from us, or fix something on this site.' +name: How do you want to be part of this? +responsible: + contact: bl@uhhm.no + name: Bendik Aagaard Lynghaug diff --git a/questions/owners/_section.yaml b/questions/owners/_section.yaml new file mode 100644 index 0000000..670fca7 --- /dev/null +++ b/questions/owners/_section.yaml @@ -0,0 +1 @@ +qualifies: owners diff --git a/questions/owners/index.yaml b/questions/owners/index.yaml new file mode 100644 index 0000000..a59e0b0 --- /dev/null +++ b/questions/owners/index.yaml @@ -0,0 +1,212 @@ +alternatives: +- action: /owners + consequence: + - Confirm + description: People who've asked to build with us, waiting on a decision or already working alongside us. + features: + - description: '' + name: '' + resource: + requires_group: owners + source: + bucket: applicants + kind: kv + transitions: + - from: active + label: Mark departed + to: departed + - from: active + label: End collaboration + to: declined + - from: invited + label: Confirm active + to: active + - from: invited + label: Withdraw invite + to: declined + - from: open + label: Send invite + to: invited + - from: open + label: Decline applicant + to: declined + name: People who want to build with us +- action: /owners + consequence: + - Confirm + description: Questions and issues raised with the people who keep the first node - custodians handle these, but you can see where each one stands. + features: + - description: '' + name: '' + resource: + requires_group: owners + source: + bucket: custody_requests + kind: kv + transitions: [] + name: What's been raised with custodians +- action: /owners + consequence: + - Confirm + description: Changes visitors have proposed for pages on this site, waiting on a yes or no. + features: + - description: '' + name: '' + resource: + requires_group: owners + source: + bucket: development_proposals + kind: kv + transitions: + - from: open + label: Approve change + to: approved + - from: open + label: Reject change + to: rejected + name: Proposed changes to the site +- action: /owners + consequence: + - Confirm + description: Organizations you've met, tracked from first contact through to client and beyond. + features: + - description: '' + name: '' + resource: + requires_group: owners + source: + bucket: organizations + kind: kv + transitions: + - from: client + label: Mark past client + to: past_client + - from: prospect + label: Mark as client + to: client + - from: prospect + label: Mark not pursued + to: past_client + name: Organizations you're tracking +- action: /owners + consequence: + - Confirm + description: Briefs sent in by organizations with a problem they want solved. + features: + - description: '' + name: '' + resource: + requires_group: owners + source: + bucket: projects + kind: kv + transitions: + - from: open + label: Accept brief + to: accepted + - from: open + label: Decline brief + to: declined + name: Briefs waiting on a reply +- action: /owners + consequence: + - Confirm + description: People who want to hear from you now and then. + features: + - description: '' + name: '' + resource: + requires_group: owners + source: + bucket: subscribers + kind: kv + transitions: + - from: open + label: Unsubscribe them + to: unsubscribed + name: Who's hearing from you +- action: /owners + consequence: + - Confirm + description: Businesses who want to see their own workflow as a working site before committing to anything. + features: + - description: '' + name: '' + resource: + requires_group: owners + source: + bucket: trials + kind: kv + transitions: + - from: approved + label: Mark previewed + to: previewed + - from: approved + label: Decline trial + to: declined + - from: open + label: Approve trial + to: approved + - from: open + label: Decline trial + to: declined + name: Trial sites in progress +- action: /owners/prospect + consequence: + - Add prospect + description: Write down an organization worth following up, and track it from first contact onward. + name: Track a new organization +- action: /owners + consequence: + - Send invite + description: Bring someone in to help keep the first node and answer for the network. + features: + - description: '' + name: Invite a custodian + requirements: + - label: Their name + name: name + - label: Their email + name: email + type: email + invite: + group: custodians + name: Bring in a custodian +- action: /owners + consequence: + - Send invite + description: Bring someone in to work this desk alongside you. + features: + - description: '' + name: Invite an owner + requirements: + - label: Their name + name: name + - label: Their email + name: email + type: email + invite: + group: owners + name: Bring in another owner +- action: /owners + consequence: + - Confirm + description: People you've invited in, and whether they've taken it up yet. + features: + - description: '' + name: '' + resource: + requires_group: owners + source: + bucket: portal_invites + kind: kv + transitions: + - from: open + label: Taken up + to: done + name: Invites you've sent +description: 'Everything waiting on you: briefs, trials, applicants, subscribers, and proposals, all in one place.' +name: What's waiting on me today? +responsible: + contact: bl@uhhm.no + name: Bendik Aagaard Lynghaug diff --git a/questions/owners/prospect-sent.yaml b/questions/owners/prospect-sent.yaml new file mode 100644 index 0000000..d49a1d4 --- /dev/null +++ b/questions/owners/prospect-sent.yaml @@ -0,0 +1,10 @@ +alternatives: +- description: An owner walks it from prospect to client, and on to past client, as the relationship moves. + name: It's on the desk now +- action: /owners + consequence: + - Back to site + description: '' + name: Back to site +description: Thanks — we've got your prospect written down, and it now moves through the desk with everything else we're tracking. +name: What happens to this now? diff --git a/questions/owners/prospect.yaml b/questions/owners/prospect.yaml new file mode 100644 index 0000000..581bd11 --- /dev/null +++ b/questions/owners/prospect.yaml @@ -0,0 +1,39 @@ +alternatives: +- action: /owners/prospect-sent + consequence: + - Add to prospects + description: One line per organization worth watching, moved through the stages as things develop. + encouragements: + - Takes a minute, and it's yours to update later. + features: + - description: It sits as a prospect until something moves - then you mark it client, and later past client if the work ends. + icon: lucide:route + name: Moves as you update it + - description: Only you see this, so keep it wherever you check next. + icon: lucide:user + name: Just for you + - description: Name the organization and where things stand. + icon: lucide:briefcase + name: New prospect + requirements: + - label: Organization + name: name + placeholder: Nordvik Bakery + type: text + - label: Contact + name: contact + placeholder: met at the bakers' conference, [email protected] + type: text + - label: Note + name: note + optional: true + placeholder: wants a booking flow, follow up after harvest season + type: textarea + name: Keep an organization on your radar + record_as: organizations +description: Write down an organization worth following up, and move it through client and past client as things develop. +followup: false +name: Is there an organization I should be keeping track of? +responsible: + contact: bl@uhhm.no + name: Bendik Aagaard Lynghaug diff --git a/questions/project.yaml b/questions/project.yaml deleted file mode 100644 index 36f25ae..0000000 --- a/questions/project.yaml +++ /dev/null @@ -1,15 +0,0 @@ -followup: true -name: So, what happens now? -description: > - That's on its way to a real inbox. Expect a considered reply within a - couple of days. -alternatives: - - name: In short - description: "" - features: - - name: We read it - description: In full, before we write back. - - name: We ask questions - description: > - Usually more than you'd expect. Good software begins with a - clear argument for what it's actually for. diff --git a/questions/received/collaborate.yaml b/questions/received/collaborate.yaml new file mode 100644 index 0000000..c612fe8 --- /dev/null +++ b/questions/received/collaborate.yaml @@ -0,0 +1,10 @@ +alternatives: +- description: If it looks like a fit, we'll reach out to talk about where you could help. + name: We'll write if there's a fit +- action: / + consequence: + - Back to site + description: '' + name: Back to site +description: Your note is in. An owner reads every one and will get back to you if there's a fit worth exploring. +name: What happens to what I just sent? diff --git a/questions/received/commission.yaml b/questions/received/commission.yaml new file mode 100644 index 0000000..59e77bc --- /dev/null +++ b/questions/received/commission.yaml @@ -0,0 +1,10 @@ +alternatives: +- description: Expect a reply within a few working days, either way, straight to your inbox. + name: A real reply from someone who read it +- action: / + consequence: + - Back to home + description: '' + name: Back to home +description: Your brief is in. An owner reads every commission end to end before replying. +name: What happens now that we've sent our brief? diff --git a/questions/received/custody.yaml b/questions/received/custody.yaml new file mode 100644 index 0000000..150966f --- /dev/null +++ b/questions/received/custody.yaml @@ -0,0 +1,10 @@ +alternatives: +- description: Expect a reply once your report has been looked at and the network checked. + name: A custodian is on it +- action: / + consequence: + - Back to the site + description: '' + name: Back to the site +description: A custodian reads every message about the redoal network and what it keeps. +name: Who looks after what the network keeps? diff --git a/questions/received/propose.yaml b/questions/received/propose.yaml new file mode 100644 index 0000000..1ae02a9 --- /dev/null +++ b/questions/received/propose.yaml @@ -0,0 +1,10 @@ +alternatives: +- description: We'll write back once it's been read — approved and made live, or turned down with a reason. + name: A read, then a decision +- action: / + consequence: + - Back home + description: '' + name: Back home +description: Your proposal is in. Someone here will read it and decide whether it fits before anything changes on the page. +name: What happens to my proposal now? diff --git a/questions/received/subscribe.yaml b/questions/received/subscribe.yaml new file mode 100644 index 0000000..c2910bb --- /dev/null +++ b/questions/received/subscribe.yaml @@ -0,0 +1,10 @@ +alternatives: +- description: You're on the list. Every note has an unsubscribe link if it stops being useful. + name: You're on the list +- action: / + consequence: + - Back to site + description: '' + name: Back to site +description: You'll hear from us when there's something worth saying, not on a schedule. +name: What happens now that I've subscribed? diff --git a/questions/received/trial.yaml b/questions/received/trial.yaml new file mode 100644 index 0000000..586bcd3 --- /dev/null +++ b/questions/received/trial.yaml @@ -0,0 +1,10 @@ +alternatives: +- description: If it looks like a fit, we'll draft your site from your own words and send it back for your say before anything goes live. + name: A first draft, built from your own words +- action: / + consequence: + - Back to site + description: '' + name: Back to site +description: We read every trial request ourselves before anything gets built. +name: What happens now that you've sent this? diff --git a/questions/review/[record].yaml b/questions/review/[record].yaml deleted file mode 100644 index dfac076..0000000 --- a/questions/review/[record].yaml +++ /dev/null @@ -1,20 +0,0 @@ -# Dynamic page: /review/ shows one development proposal, -# addressed by its record key. The {record} placeholder receives the -# URL segment; the section's qualifies (plus the resource's own -# requires_group) keeps it owner-only. -name: Which proposal is this? -description: > - A single development proposal, addressed by its record key — link or - paste a chain hash from the desk. -alternatives: - - name: Record - description: "" - features: - - name: "" - description: "" - resource: - source: - kind: kv - bucket: development_proposals - key: "{record}" - requires_group: owners diff --git a/questions/review/_section.yaml b/questions/review/_section.yaml deleted file mode 100644 index fbdabae..0000000 --- a/questions/review/_section.yaml +++ /dev/null @@ -1,3 +0,0 @@ -# Everything under /review is the owners' area - one gate for the -# whole directory instead of a qualifies flag per file. -qualifies: owners diff --git a/questions/review/index.yaml b/questions/review/index.yaml deleted file mode 100644 index 2eb597c..0000000 --- a/questions/review/index.yaml +++ /dev/null @@ -1,141 +0,0 @@ -name: What came in? -description: > - Sign in as an organizational owner to see what's come in. -alternatives: - - name: Applicants - description: "" - action: /review - consequence: [Confirm] - features: - - name: "" - description: "" - resource: - source: - kind: kv - bucket: applicants - requires_group: owners - transitions: - - to: invited - label: Invite - - to: declined - label: Decline - - from: invited - to: active - label: Onboard - - from: invited - to: declined - label: Decline - - from: active - to: departed - label: Depart - - - name: Organizations - description: > - Client relationships as tracked state — advanced as the - relationship becomes real. - action: /review - consequence: [Confirm] - features: - - name: "" - description: "" - resource: - source: - kind: kv - bucket: organizations - requires_group: owners - transitions: - - from: prospect - to: client - label: Becomes client - - from: prospect - to: past_client - label: Archive - - from: client - to: past_client - label: Churn - - - name: Record a prospect - description: > - A relationship worth tracking before it's a project — enter it - here and walk it through its states from the desk. - action: /review - consequence: [Record] - record_as: organizations - encouragements: - - Written down is halfway to followed up. - features: - - name: "" - description: The organization and one person there to reach. - requirements: - - name: name - label: Organization - - name: contact - label: Contact - - name: note - label: Anything worth remembering - type: textarea - optional: true - - - name: Projects - description: "" - action: /review - consequence: [Confirm] - features: - - name: "" - description: "" - resource: - source: - kind: kv - bucket: projects - requires_group: owners - transitions: - - to: accepted - label: Accept - - to: declined - label: Decline - - - name: Subscribers - description: "" - action: /review - consequence: [Send] - features: - - name: "" - description: "" - resource: - source: - kind: kv - bucket: subscribers - requires_group: owners - - name: Compose newsletter - description: > - Sends to everyone above - picked up by an automation watching - this question, not portal itself. - requirements: - - name: subject - label: Subject - - name: body - label: Body - type: prosekit - - - name: Announced pages - description: > - Pages with an `event:` window, kept here by portal itself. When a - window closes the record becomes a task: post what happened, - then mark it done. - action: /review - consequence: [Confirm] - features: - - name: "" - description: "" - resource: - source: - kind: kv - bucket: portal_events - requires_group: owners - transitions: - - from: awaiting_summary - to: summarized - label: Posted what happened - - from: announced - to: summarized - label: Needs no summary diff --git a/questions/subscribed.yaml b/questions/subscribed.yaml deleted file mode 100644 index 9059184..0000000 --- a/questions/subscribed.yaml +++ /dev/null @@ -1,22 +0,0 @@ -followup: true -name: What should you expect? -description: > - We'll write when there's something worth your time — that's the - whole promise. -alternatives: - - name: You're on the list - description: "" - features: - - name: Rare, by design - description: A few considered notes a year — nothing more. - - name: Easy to leave - description: Every note carries a genuine, one-click unsubscribe. - - - name: Unsubscribe - description: > - Using the link from one of our emails? Confirm below and we'll - stop writing. - self_transition: - bucket: subscribers - to: unsubscribed - label: Unsubscribe me