Icons on every input feature; why-lines; question titles; voice rule
Lint and reload / lint (push) Successful in 3s
Lint and reload / reload (push) Successful in 0s

Desk/receipt titles become questions (What's been proposed? / What
happens to your proposal? / What came in? / Which proposal is this?),
Where to send it and Record a prospect explain why before the field,
lucide icons on input-bearing features, README's alternatives rule
reworded (draw people in with a real path, not necessarily a slogan)
and its stale /develop-proposal + followup references fixed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Bendik Aagaard Lynghaug
2026-08-25 17:04:45 +02:00
co-authored by Claude Fable 5
parent bd19da9959
commit 981a448b30
7 changed files with 29 additions and 13 deletions
+8 -6
View File
@@ -44,7 +44,7 @@ itself. `.gitea/workflows/deploy.yml` pins the portal version:
```yaml ```yaml
env: env:
PORTAL_RELEASE: v0.1.0 # a release tag on uhhm/portal PORTAL_RELEASE: v0.2.3 # a release tag on uhhm/portal
``` ```
Upgrading (or downgrading) portal is bumping that line and pushing — Upgrading (or downgrading) portal is bumping that line and pushing —
@@ -132,7 +132,7 @@ entirely in this repo, in this order:
infrastructure repo's `n8n-workflows/`). Rename a page or a label infrastructure repo's `n8n-workflows/`). Rename a page or a label
and its workflow must be updated in lockstep. and its workflow must be updated in lockstep.
5. **Change the system through itself.** `/develop-proposal` takes a 5. **Change the system through itself.** `/develop/proposal` takes a
proposed replacement for any content file (`questions/*.yaml` or proposed replacement for any content file (`questions/*.yaml` or
`aggregates.yaml` — nothing else is in scope) — from a person, or `aggregates.yaml` — nothing else is in scope) — from a person, or
eventually a locally-run model, through the same public form. An eventually a locally-run model, through the same public form. An
@@ -147,9 +147,11 @@ The rules every page here follows:
- **The title is the only question.** A page asks one thing — its - **The title is the only question.** A page asks one thing — its
`name`. Nothing below it asks anything. `name`. Nothing below it asks anything.
- **Alternatives are bold statements**, argued against each other — - **Alternatives draw people in.** Each one suggests a real path a
positions we'd like a visitor to get behind, in our voice, never visitor could take — a position to get behind or a thing to do next,
the visitor's presumed voice and never a question. in our voice, never the visitor's presumed voice and never a
question. A category label ("Our Composition") is not an
alternative; "Follow the build as it lands" is.
- **Explain why, then hand over the field.** Where input is needed, - **Explain why, then hand over the field.** Where input is needed,
the feature's name and description state the reason ("the reply is the feature's name and description state the reason ("the reply is
personal — it needs an address"), and labels are nouns, not personal — it needs an address"), and labels are nouns, not
@@ -159,7 +161,7 @@ The rules every page here follows:
that gives them an answer — not segments to self-select into. The that gives them an answer — not segments to self-select into. The
question nav (every page's footer) surfaces all questions the question nav (every page's footer) surfaces all questions the
current visitor qualifies for, so nothing depends on front-page current visitor qualifies for, so nothing depends on front-page
space; post-submission pages carry `followup: true` and only join space; post-submission pages (nested non-index files, or `followup: true`) only join
the nav once the visitor holds an answer chain. the nav once the visitor holds an answer chain.
## Schema quick reference ## Schema quick reference
+1 -1
View File
@@ -1,4 +1,4 @@
name: Development proposals name: What's been proposed?
qualifies: owners qualifies: owners
description: > description: >
Proposed changes to the questions repo, awaiting a decision. Proposed changes to the questions repo, awaiting a decision.
+3
View File
@@ -21,6 +21,7 @@ alternatives:
- Every proposal gets read by a person before anything moves. - Every proposal gets read by a person before anything moves.
features: features:
- name: The reference - name: The reference
icon: lucide:book-open
description: > description: >
Every page this site serves, verbatim — check what exists Every page this site serves, verbatim — check what exists
before proposing against it. before proposing against it.
@@ -32,6 +33,7 @@ alternatives:
jq: > jq: >
{name: .full_name, description: .description, url: .html_url} {name: .full_name, description: .description, url: .html_url}
- name: Sign it - name: Sign it
icon: lucide:pen-line
description: > description: >
A proposal is a stance — it carries a name, and an address A proposal is a stance — it carries a name, and an address
if you want the outcome to reach you. if you want the outcome to reach you.
@@ -43,6 +45,7 @@ alternatives:
type: email type: email
optional: true optional: true
- name: The change - name: The change
icon: lucide:git-branch
description: > description: >
Pick the page you're changing and its current content loads 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 below, ready to edit in place — or name a new file's path if
+1 -1
View File
@@ -1,7 +1,7 @@
# Reached from the proposal form only - and provably so: the visitor # Reached from the proposal form only - and provably so: the visitor
# must hold a ?chain= lineage ending at an answer to proposal.yaml. # must hold a ?chain= lineage ending at an answer to proposal.yaml.
requires_chain: proposal requires_chain: proposal
name: Proposal received name: What happens to your proposal?
description: > description: >
It's in the queue. An owner reads every proposal in full — approved 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 ones open a real pull request, and the same checks every human
+8 -1
View File
@@ -16,6 +16,7 @@ alternatives:
- We read every word before breaking ground. - We read every word before breaking ground.
features: features:
- name: What we've built - name: What we've built
icon: lucide:layers
description: A few things already out in the world. description: A few things already out in the world.
resource: resource:
source: source:
@@ -35,6 +36,7 @@ alternatives:
elif .private then null elif .private then null
else .html_url end)} else .html_url end)}
- name: Your brief - name: Your brief
icon: lucide:file-text
description: > description: >
A few lines are enough to begin a conversation — we need a A few lines are enough to begin a conversation — we need a
name and an address because the reply is personal, and the name and an address because the reply is personal, and the
@@ -66,7 +68,10 @@ alternatives:
- Consider yourself posted. - Consider yourself posted.
features: features:
- name: Where to send it - name: Where to send it
description: "" icon: lucide:mail
description: >
Notes go to a person, not a list — a name and an address is
all it takes.
requirements: requirements:
- name: name - name: name
label: Name label: Name
@@ -86,6 +91,7 @@ alternatives:
- Knock — we actually answer. - Knock — we actually answer.
features: features:
- name: A way to reach you - name: A way to reach you
icon: lucide:at-sign
description: The reply is personal — it needs a name and an address. description: The reply is personal — it needs a name and an address.
requirements: requirements:
- name: name - name: name
@@ -94,6 +100,7 @@ alternatives:
label: Email label: Email
type: email type: email
- name: Your angle - name: Your angle
icon: lucide:compass
description: > description: >
We build around what each person actually brings — say what We build around what each person actually brings — say what
that is, in your own words. that is, in your own words.
+1 -1
View File
@@ -2,7 +2,7 @@
# addressed by its record key. The {record} placeholder receives the # addressed by its record key. The {record} placeholder receives the
# URL segment; the section's qualifies (plus the resource's own # URL segment; the section's qualifies (plus the resource's own
# requires_group) keeps it owner-only. # requires_group) keeps it owner-only.
name: One proposal name: Which proposal is this?
description: > description: >
A single development proposal, addressed by its record key — link or A single development proposal, addressed by its record key — link or
paste a chain hash from the desk. paste a chain hash from the desk.
+7 -3
View File
@@ -1,4 +1,4 @@
name: The review desk name: What came in?
description: > description: >
Sign in as an organizational owner to see what's come in. Sign in as an organizational owner to see what's come in.
alternatives: alternatives:
@@ -55,13 +55,17 @@ alternatives:
label: Churn label: Churn
- name: Record a prospect - name: Record a prospect
description: "" description: >
A relationship worth tracking before it's a project — enter it
here and walk it through its states from the desk.
action: /review action: /review
consequence: [Record] consequence: [Record]
record_as: organizations record_as: organizations
encouragements:
- Written down is halfway to followed up.
features: features:
- name: "" - name: ""
description: "" description: The organization and one person there to reach.
requirements: requirements:
- name: name - name: name
label: Organization label: Organization