Commit Graph
47 Commits
Author SHA1 Message Date
Bendik Aagaard Lynghaug 4720397450 Content-owned hero module (WIP branch for local verification)
Lint and reload / lint (push) Skipped
2026-08-25 17:24:14 +02:00
Bendik Aagaard LynghaugandClaude Fable 5 d928fbcbc9 Pin portal v0.2.4 - gates keep the nav
Lint and reload / lint (push) Successful in 3s
Deploy instance / deploy (push) Successful in 0s
Lint and reload / reload (push) Successful in 0s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-25 17:07:26 +02:00
Bendik Aagaard LynghaugandClaude Fable 5 981a448b30 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>
2026-08-25 17:04:45 +02:00
Bendik Aagaard LynghaugandClaude Fable 5 bd19da9959 Pin portal v0.2.3 - attended-bucket lint, first-nav fix; document the rule
Deploy instance / deploy (push) Successful in 1s
Lint and reload / lint (push) Successful in 3s
Lint and reload / reload (push) Successful in 1s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-25 15:38:08 +02:00
Bendik Aagaard LynghaugandClaude Fable 5 1ff3c971ad Pin portal v0.2.2 - single app-level title
Deploy instance / deploy (push) Successful in 1s
Lint and reload / lint (push) Successful in 3s
Lint and reload / reload (push) Successful in 0s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 22:49:14 +02:00
Bendik Aagaard LynghaugandClaude Fable 5 f25dedd742 Pin portal v0.2.1 - asset cache revalidation, stable title on nav
Deploy instance / deploy (push) Successful in 0s
Lint and reload / lint (push) Successful in 2s
Lint and reload / reload (push) Successful in 1s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 22:43:14 +02:00
Bendik Aagaard LynghaugandClaude Fable 5 f243494499 Pin portal v0.2.0; document tree routing in the schema reference
Deploy instance / deploy (push) Successful in 1s
Lint and reload / lint (push) Successful in 2s
Lint and reload / reload (push) Successful in 0s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 22:33:16 +02:00
Bendik Aagaard LynghaugandClaude Fable 5 ad9dd1762b The tree is the router: nest the develop flow, section-gate review
develop/ holds the whole proposal flow (index = owners desk,
proposal = public form, proposed = chain-gated receipt) with relative
actions; review/ gains _section.yaml (one owners gate for the
directory) and a [record].yaml dynamic page - /review/<chain-hash>
shows a single proposal. Top-level files drop their now-derived id:
and fossil route: fields. URLs /develop-proposal and /proposed become
/develop/proposal and /develop/proposed (no automation references
them - n8n filters checked).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 22:32:01 +02:00
Bendik Aagaard LynghaugandClaude Fable 5 77ff58e524 Docs: this repo runs the uhhm.no instance (PORTAL_RELEASE pin)
Lint and reload / lint (push) Successful in 2s
Lint and reload / reload (push) Successful in 1s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 21:49:32 +02:00
Bendik Aagaard LynghaugandClaude Fable 5 380593a998 Pin portal v0.1.0 - first semver release
Deploy instance / deploy (push) Successful in 1s
Lint and reload / lint (push) Successful in 2s
Lint and reload / reload (push) Successful in 0s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-24 21:45:45 +02:00
Bendik Aagaard Lynghaug bb117c1a1b Amend: GITEA_ is a reserved Actions secret-name prefix
Deploy instance / deploy (push) Successful in 1s
Lint and reload / lint (push) Successful in 3s
Lint and reload / reload (push) Successful in 0s
2026-08-24 21:26:26 +02:00
Bendik Aagaard LynghaugandClaude Fable 5 9b7162f2ca Showcase links prefer repo Website setting; fix star count field
Lint and reload / lint (push) Successful in 3s
Lint and reload / reload (push) Successful in 1s
A private starred repo used to link visitors straight to a 404. The
link now uses the repo's Website setting when set, falls back to the
repo url only for public repos, and renders private repos without a
website as unlinked cards. Also: Gitea serializes the star count as
stars_count, not GitHub's stargazers_count - the stars chip had been
silently null and dropped.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-23 11:27:39 +02:00
Bendik Aagaard LynghaugandClaude Sonnet 5 9dbd30655f Picking a file loads its current content to edit in place
Lint and reload / lint (push) Successful in 2s
Lint and reload / reload (push) Successful in 1s
proposed_yaml binds to the file select: the selected file's content
fetches through Gitea's contents API (base64-decoded by the jq
filter) and lands in the editor. On-site contributions no longer
start from a blank box.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-13 13:50:29 +02:00
Bendik Aagaard LynghaugandClaude Sonnet 5 b2423d64cf More accurately: "How can this website improve?"
Lint and reload / lint (push) Successful in 2s
Lint and reload / reload (push) Successful in 0s
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-13 07:21:33 +02:00
Bendik Aagaard LynghaugandClaude Sonnet 5 3e2abceec8 Ask "How can we improve this website?" instead of commanding "Propose a change"
Lint and reload / lint (push) Successful in 2s
Lint and reload / reload (push) Successful in 0s
The title is the page's one question - and it should actually be one.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-13 07:21:17 +02:00
Bendik Aagaard LynghaugandClaude Sonnet 5 6443b9fdd4 Propose-a-change: link the questions repo as the reference
Lint and reload / lint (push) Successful in 2s
Lint and reload / reload (push) Successful in 0s
A linked card straight from Gitea's repo info - proposers can read
what exists before proposing against it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-13 07:19:34 +02:00
Bendik Aagaard LynghaugandClaude Sonnet 5 1a88212bd9 Propose-a-change: pick the file from the repo itself
Lint and reload / lint (push) Successful in 2s
Lint and reload / reload (push) Successful in 0s
target_path becomes a select whose options are the live listing of
questions/ from Gitea's contents API (the engine's existing
select-from-resource mechanism - no code change), with a free path
field kept for proposals that create a new file. No more hand-typed,
typo-able paths for existing pages.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-13 07:14:56 +02:00
Bendik Aagaard LynghaugandClaude Sonnet 5 c1bd503efd State the proposal scope where proposals are made
Lint and reload / lint (push) Successful in 3s
Lint and reload / reload (push) Successful in 0s
The automation fences commits to questions/*.yaml and aggregates.yaml;
the page and README now say so, instead of promising "any file" and
silently failing after approval.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-13 00:23:05 +02:00
Bendik Aagaard LynghaugandClaude Sonnet 5 627bdb807e Asked by the full name, not the account short name
Lint and reload / lint (push) Successful in 2s
Lint and reload / reload (push) Successful in 0s
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 23:47:14 +02:00
Bendik Aagaard LynghaugandClaude Sonnet 5 bd0ba41281 Voice: join the narrative, don't bucket people into roles
Lint and reload / lint (push) Successful in 2s
Lint and reload / reload (push) Successful in 0s
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 23:35:02 +02:00
Bendik Aagaard LynghaugandClaude Sonnet 5 c07f8c880d Drop the investor concern entirely
Lint and reload / lint (push) Successful in 2s
Lint and reload / reload (push) Successful in 0s
It was a worked example of the empathic pattern, not a feature: every
alternative should be empathic, informative, and helpful - information
that gives an answer, not necessarily a path to choose. The pattern
stays (voice rules, README); the investor pages, aggregate, review
alternative, and front-page gateway go. The investors KV bucket and
its test records linger in NATS unreferenced - purge at leisure.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 23:33:53 +02:00
Bendik Aagaard LynghaugandClaude Sonnet 5 176e2210a0 README: the voice rules
Lint and reload / lint (push) Successful in 3s
Lint and reload / reload (push) Successful in 0s
The title is the only question; alternatives are bold statements
argued against each other; explain why input is needed, then hand
over the field; never assume the visitor's role - give the concern
its own page and let the question nav surface it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 23:29:53 +02:00
Bendik Aagaard LynghaugandClaude Sonnet 5 309e3b43df Voice: bold statements, argued alternatives, no questions below the title
Lint and reload / lint (push) Successful in 2s
Lint and reload / reload (push) Successful in 0s
- The page title is the only question anywhere; alternative and
  feature names are now statements in our voice. Where we need input
  we say why (the reply is personal, software lands somewhere, a
  proposal is a stance), then hand over the field.
- The opening question's alternatives become the statements we'd like
  visitors to get behind: "Good software begins with a clear
  argument", "Attention is earned", "Capital needs reasons", "Work
  happens through dialogue, not tickets".
- followup: true on the five post-submission pages keeps them out of
  the question nav until the visitor actually carries a chain.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 23:26:10 +02:00
Bendik Aagaard LynghaugandClaude Sonnet 5 c6d82199be Ask the investment question instead of assuming the investor
"I want to invest" on the front page presumed the visitor's intent.
/invest now asks it from their side - "How do we invest in uhhm?" -
and speaks to what a capital manager is actually accountable for:
reasons. The front page keeps only a light gateway; the question nav
surfaces /invest (like every other qualifying question) from anywhere,
so no concern depends on front-page space.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 23:11:25 +02:00
Bendik Aagaard LynghaugandClaude Sonnet 5 251fe22c25 README: the runtime's dependencies and the optimal usecase, in order
Lint and reload / lint (push) Successful in 2s
Lint and reload / reload (push) Successful in 1s
Replaces the long-stale schema notes (pre-aggregates, pre-resources,
pre-proposal-loop) with what the repo actually is now: declare a state
graph, give it a way in (submission), a way through (review
transitions), let automations react, and change the system through
itself.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 22:56:54 +02:00
Bendik Aagaard LynghaugandClaude Sonnet 5 c488932695 Remove the proposal-loop demo page
Lint and reload / lint (push) Successful in 2s
Lint and reload / reload (push) Successful in 0s
It existed to prove the round-trip (proposed -> approved -> PR ->
lint -> merge -> live) and it did. Its job is done.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 22:45:19 +02:00
bl 3c48c47f88 Merge pull request 'Proposal: questions/test-proposal-demo.yaml' (#1) from proposal-4fa4bbee2473 into main
Lint and reload / lint (push) Successful in 2s
Lint and reload / reload (push) Successful in 0s
2026-08-12 20:32:22 +00:00
bl 8fb191fe21 Proposal: questions/test-proposal-demo.yaml
Lint and reload / reload (pull_request) Has been skipped
Lint and reload / lint (pull_request) Successful in 2s
End-to-end verification of the proposal loop - this page is a harmless demo and can be deleted after the round-trip is confirmed.

Proposed-by: Verification Bot
Approved-by: bl@id.uhhm.no
2026-08-12 20:30:34 +00:00
Bendik Aagaard LynghaugandClaude Sonnet 5 bf506a83f2 CI: lint on pull requests too, reload only on pushes to main
Lint and reload / lint (push) Successful in 2s
Lint and reload / reload (push) Successful in 0s
A development-proposal PR previously got no check at all (the workflow
only fired on push to main) - so branch protection's required status
check, the thing that makes the automation's merge_when_checks_succeed
an actual lint gate, had nothing to require. Lint now runs on both
events; the content-reload NATS publish stays push-only.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 22:21:27 +02:00
Bendik Aagaard LynghaugandClaude Sonnet 5 8d10e99726 Actor state machines: employee lifecycle, investors, organizations wired, development proposals
Lint and reload / lint-and-reload (push) Successful in 2s
- applicants grows past the one decision: invited -> active (onboarded)
  -> departed, one continuous graph per person.
- investors: open -> in_dialogue -> committed -> divested, with decline
  edges; submission via a new "I want to invest" alternative on /.
- organizations (prospect -> client -> past_client) finally gets pages:
  a review listing with its transitions and a record-a-prospect form.
- development_proposals: the question architecture dogfooding its own
  development - /develop-proposal takes proposals from anyone (human
  today, an LLM later through the same public form), /develop lets an
  owner approve or reject, and approval hands off to the automation
  that opens the PR (see infrastructure's development-commit workflow).
  The repo's own lint CI gates the merge, same as any human commit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 22:04:11 +02:00
Bendik Aagaard LynghaugandClaude Sonnet 5 7a6cfb5acb Revert the temporary color/icon/image demo
Lint and reload / lint-and-reload (push) Successful in 2s
Confirmed it renders correctly (icon + accent border, banner image) -
reverting since which features/alternatives actually get these is an
editorial choice, not mine to make.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 17:37:19 +02:00
Bendik Aagaard LynghaugandClaude Sonnet 5 326f2d884a Temporary: demo Feature.color/icon and Alternative.image for a visual check
Lint and reload / lint-and-reload (push) Successful in 2s
Will be reverted right after confirming the render looks right.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 17:35:53 +02:00
Bendik Aagaard LynghaugandClaude Sonnet 5 f0abf71d47 review.yaml: give Applicants/Projects a shared consequence button too
Lint and reload / lint-and-reload (push) Successful in 2s
Matches the pattern Subscribers already used (action: /review +
consequence). Portal's per-row Confirm button is going away in favor
of one shared, content-labeled button per alternative that confirms
every selected row at once - see the paired portal change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-12 14:31:59 +02:00
Bendik Aagaard LynghaugandClaude Sonnet 5 4efa26585a Add aggregates.yaml: the full state graph for applicants/subscribers/projects/organizations
Lint and reload / lint-and-reload (push) Successful in 2s
Portal's aggregate state machines used to be compiled Rust, duplicated
across four files plus the bucket-name wiring that dispatched to them.
This is now the single source of truth for what states exist, what
event-type string represents each, and what transitions are legal -
portal loads and validates it at boot/reload (see the paired portal
change). Adding a new aggregate, or reshaping an existing one, is now
a content-only change.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-11 21:42:57 +02:00
Bendik Aagaard LynghaugandClaude Sonnet 5 bc18add801 Lint with portal's question_lint binary, not the hand-maintained yq/jq script
Lint and reload / lint-and-reload (push) Successful in 2s
lint.sh only ever checked shape (missing id/name, duplicate ids, bad
requirement types) - it never validated transition targets against the
real state machines. question_lint runs portal's actual validation
logic (content::validate_questions) with no portal build required in
this repo's CI, since portal's deploy job now publishes it to a stable
path on the same runner/host.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-11 21:16:54 +02:00
Bendik Aagaard Lynghaug e8f266e8f7 Drop the last "someone reads it" phrasing, match the "we" voice used elsewhere
Lint and reload / lint-and-reload (push) Successful in 3s
applied.yaml and index.yaml still described review in the passive
third person ("someone here will read this", "taken on by someone
who reads") after project.yaml and the rest of applied.yaml's own
features had already been reworked to a direct first-person "we"
voice centered on the visitor's value, not on the fact that a person
reads it.
2026-08-06 22:06:44 +02:00
Bendik Aagaard LynghaugandClaude Sonnet 5 a9eaca6b78 Speak to the applicant's actual concern, not just the process
Lint and reload / lint-and-reload (push) Successful in 3s
"A person reads it" reassures about process (a human, not a bot) but
never answers what an applicant actually wants to know: does what I
specifically bring matter here? Rewrote to say that directly, and
named how it gets used once they're in - dialogue-centered
development, not a queue of pre-decided tasks.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-06 12:57:21 +02:00
Bendik Aagaard LynghaugandClaude Sonnet 5 1b709d2a67 Ask subscribers for their name too, not just email
Lint and reload / lint-and-reload (push) Successful in 3s
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-06 11:59:11 +02:00
Bendik Aagaard LynghaugandClaude Sonnet 5 2af9a46afb lint.sh: accept type: select as a valid requirement type
Lint and reload / lint-and-reload (push) Successful in 3s
Matches portal's new resource-backed multi/single-select requirement
kind.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-06 11:57:21 +02:00
Bendik Aagaard LynghaugandClaude Sonnet 5 a424c6d3c2 Rename buckets, fold inquiry into project, add showcase + org fields
Lint and reload / lint-and-reload (push) Successful in 2s
Bucket names describe content, not their relation to portal:
portal_applicants -> applicants, portal_subscribers -> subscribers,
portal_project_inquiries -> projects. ResourceSpec's `bucket:` field
becomes `source: {kind: kv, bucket: ...}` to match portal's new
generalized ResourceSource schema.

The project-inquiry alternative now also shows a live "what we've
built" showcase (a gitea_starred resource, jq-shaped) and asks who the
work is for (organization/contact fields) - b2b framing. Review desk's
"Inquiries" -> "Projects", transitions to accepted/declined instead of
a generic "handled".

Front page gets a `responsible` contact for the "report this question"
mechanism.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-06 08:53:01 +02:00
Bendik Aagaard Lynghaug 731f307ce1 Add self-service Unsubscribe alternative to /subscribed
Lint and reload / lint-and-reload (push) Successful in 3s
Only shows when reached via a ?chain=&email= link (see portal's
self_transition), matching the link the newsletter workflow now
appends to every send.
2026-08-05 18:48:25 +02:00
Bendik Aagaard Lynghaug 9910ce0ec4 Fold newsletter compose into Subscribers as a second feature, not its own alternative
Lint and reload / lint-and-reload (push) Successful in 2s
Per feedback: sending a newsletter is naturally part of "Subscribers",
not a separate review task. Also fixes a real bug - the standalone
Compose newsletter alternative had no `action`, so AlternativeCard
never rendered a submit button/form for it at all (has_action gates
the <form>, unconditionally). action: /review reloads the dashboard
after sending.
2026-08-05 14:23:38 +02:00
Bendik Aagaard Lynghaug 6ed0b65bc2 Add portal_project_inquiries/portal_subscribers durable storage, review dashboard alternatives, newsletter compose
Lint and reload / lint-and-reload (push) Successful in 3s
- index.yaml: record_as on "I want something built" and "Keep me
  posted" - those submissions previously only existed as a NATS
  event, nothing durable, despite promising a reply/subscription.
- review.yaml: consolidated into one dashboard question with four
  alternatives (Applicants, Inquiries, Subscribers, Compose
  newsletter) rather than separate pages per resource.
- Compose newsletter has no record_as/resource of its own - an
  automation (n8n) filters portal.answers.submitted directly for
  question_id == "/review" && alternative == "Compose newsletter",
  matching events.rs's own "gain a second subscriber" design.
- lint.sh: allow type: prosekit (the new rich-text requirement kind).
2026-08-05 13:46:17 +02:00
Bendik Aagaard Lynghaug 1f0d930768 Invoke lint.sh via bash - checkout didn't preserve the exec bit
Lint and reload / lint-and-reload (push) Successful in 3s
2026-08-05 07:20:18 +02:00
Bendik Aagaard Lynghaug fc4cd1b86e Add lint + reload-trigger workflow
Lint and reload / lint-and-reload (push) Failing after 2s
Validates YAML/required fields/duplicate ids/requirement types with
yq+jq against the exact shape content.rs parses (not a full schema
check - that would mean building portal just to lint content), then
publishes portal.content.reload over NATS so every running portal
instance picks up the change without a restart (portal's own
content::watch_for_reload, added alongside this).
2026-08-05 07:19:07 +02:00
Bendik Aagaard Lynghaug d8628dbd15 Rework copy voice, add /review, Kanidm-gated applicant review
English URLs throughout; review.yaml adds a qualifies-gated question
listing applicants via the generic NATS-KV resource mechanism, with
Invite/Decline transitions. Content pass: concrete/positive phrasing
(no negation constructions), warmer inclusive tone, light puns in the
encouragement lines.
2026-07-29 19:39:00 +02:00
Bendik Aagaard Lynghaug 5321f70540 Initial content: opener, project, subscribed, applied 2026-07-29 12:51:55 +02:00