# Changelog One line per change, grouped by the release that shipped it. Newest first. A release is a Cargo.toml bump plus a `v` tag; the content repos pin the tag they run (see the end of this file). ## 0.3.47 (2026-09-24) - iris: needs.yaml takes `stage_words`, each state in the site's own words, carried into the simulation model for the trainer's desk score. No change to portal itself. ## 0.3.46 (2026-09-23) - An invite first looks the email up in Kanidm (`/v1/raw/search` on `mail`, when the service account may read people: `idm_people_pii_read`) and only adds an existing account, under whatever username, to the group; no second account, no reset link. ## 0.3.45 (2026-09-23) - A mail stream that cannot be created at boot (subjects owned by an older stream) is a warning, not a failed start; 0.3.44 took the kasse sites down for two minutes on the rename. ## 0.3.44 (2026-09-23) - The mail stream is named `mail` (was `WORMHOLE`); gdo 0.1.2 matches. Delete the old stream on each NATS after upgrading both. ## 0.3.43 (2026-09-23) - Mail is content: `mail: { to, subject, body }` on a state in aggregates.yaml sends when a record enters that state (`to: submitter` or one address; `{field}`, `{site}`, `{chain}`, `{email}` in the templates), and an invite mails its one-time link built in; `site.yaml` gains `mail: { from, reply_to, invite }`. Portal renders and publishes on `portal.mail.send` in the `WORMHOLE` stream; uhhm/gdo delivers. The lint requires a required `email` field on every form recording into a bucket that mails the submitter. ## 0.3.42 (2026-09-23) - people: the Kanidm person lookup reads the body, since Kanidm 1.11 answers a missing person with 200 and `null` rather than 404; before this every invite skipped account creation and failed on the group step ## 0.3.41 (2026-09-23) - publish: the iris release step retries on a fresh clone when its push loses a race against a push to iris main (the v0.3.40 tag never published) - iris: `check --access` prints the access matrix, and every check validates the compiled policy against the schema ## 0.3.40 (2026-09-23) - One access policy, compiled from content into Cedar on every load (`src/access.rs`): pages, resource reads, desk transitions, self transitions and the automation endpoint all decide through it; decisions carry rule ids, refusals and mutations publish on `portal.access.decided`, and `portal.access..may` answers the same question for other apps over NATS - `invite: {group}` on an alternative onboards a person into Kanidm from a desk (`src/people.rs`, `KANIDM_API_TOKEN`), recording the one-time credential link in `portal_invites` and on `portal.people.invited` ## 0.3.39 (2026-09-23) - publish: releases the matching uhhm/iris by proxy - re-pins, builds, tests and tags it as this version (needs the `IRIS_PUSH_USER` variable and `IRIS_PUSH_TOKEN` secret); iris carries portal's version number from here on ## 0.3.38 (2026-09-22) - publish: the iris build step gets a writable cargo home; the v0.3.37 tag never published ## 0.3.37 (2026-09-22) - The lint moves to its own repo, uhhm/iris: `question_lint` becomes `iris check`, `needs_replay` becomes `iris replay`; the tarball ships `iris` (pinned in publish.yml) instead of `question_lint`; the needs module and local loaders leave portal ## 0.3.36 (2026-09-22) - Question nav: a followup is offered only once earned - the verified chain answered the alternative leading there, or a link that records nothing leads on from an earned page. Standing on a page no longer unlocks the pages behind its forms; the chain index now records which alternative was answered - One bounded HTTP client for every server-side fetch: 20 s budget, size caps per kind of fetch (`src/http.rs`) - Uploads are refused past 20 MB while streaming, and the route body limit that had capped them at axum's 2 MB default is raised to match - `tests/jetstream.rs`: the store, the event log and the projection tested against a real JetStream (CI starts a throwaway one) - `content` split into `gitea`, `validate`, `markdown`, `handlers`; every path still `content::x` - This changelog, and the table of what each site runs - Business needs as a checkable spec: needs.yaml + question_lint pass - needs: `success` states and a resolved simulation model - needs: `stages` - every stage the business named must be a state - needs_replay: make simulated cases real aggregates, report buckets by state - needs: also_moved_by - a need carried by more than one group's desk ## 0.3.35 (2026-09-12) - Item cards: no paragraph margin under the description ## 0.3.34 (2026-09-11) - Question nav: offer followups by reach, not by carrying any chain ## 0.3.33 (2026-09-02) - Head preloads: latin font, wordmark, content-host preconnect ## 0.3.32 (2026-09-01) - Per-site favicon via site.yaml ## 0.3.31 (2026-09-01) - Markdown links allow tel: ## 0.3.30 (2026-09-01) - Single alt-image shows natural height ## 0.3.29 (2026-09-01) - Markdown image layout hints + AVIF assets ## 0.3.28 (2026-09-01) - Hero description renders inline markdown ## 0.3.27 (2026-09-01) - wordmark_invert: explicit, not inferred from .svg ## 0.3.26 (2026-09-01) - Select fields: inline static options ## 0.3.25 (2026-09-01) - Content-shipped stylesheet override (site.yaml stylesheet) ## 0.3.24 (2026-09-01) - Gateway submit-as-link loses the underline ## 0.3.23 (2026-09-01) - Chrome speaks the site's language: site.yaml lang + i18n table ## 0.3.22 (2026-09-01) - site.yaml wordmark_height; raster logos keep their colors ## 0.3.21 (2026-09-01) - Hero paragraph centers its measure box ## 0.3.20 (2026-09-01) - Sign-in becomes optional: KANIDM_URL unset disables auth cleanly ## 0.3.19 (2026-09-01) - Markdown images in descriptions, gated and framed ## 0.3.18 (2026-09-01) - Item cards render inline markdown descriptions - Convergence: ink converges onto the decoded key (WebGL glow) - Gesture results overlay the canvas - the page never jumps ## 0.3.16 (2026-08-30) - Announce sweeper: refresh open records from content ## 0.3.15 (2026-08-30) - Hidden fields: carrier value without a label row ## 0.3.14 (2026-08-30) - Feature cards: icon column, one text edge ## 0.3.13 (2026-08-30) - Voice field: nothing leaves the browser until Keep it here ## 0.3.12 (2026-08-30) - Voice preview stays hidden until there is something to play ## 0.3.11 (2026-08-30) - README: gesture and voice fields - Validate templated actions against the page pattern ## 0.3.10 (2026-08-30) - type: voice, Requirement.value, playable resource cards ## 0.3.9 (2026-08-30) - Templated actions; places are pickable in the gesture input ## 0.3.8 (2026-08-30) - Descriptions are inline markdown; Feature.link withdrawn ## 0.3.7 (2026-08-30) - Feature.link: a card's name may point elsewhere ## 0.3.6 (2026-08-30) - Announced pages: event windows, header announcements, summary tasks ## 0.3.5 (2026-08-30) - Gesture widget: stop drawing the decoded ghost path - Gesture widget: ghost path back, for v2 keys only ## 0.3.4 (2026-08-25) - Alternative.disabled: announced but not yet takeable ## 0.3.3 (2026-08-25) - Empty list says so before it can parse as zero answers ## 0.3.2 (2026-08-25) - README: hashed pkg assets in the release flow - Resource empty text: content-declared, and null counts as empty ## 0.3.1 (2026-08-25) - Content-hashed pkg assets (hash-files) so stale bundles can't pair with new wasm ## 0.3.0 (2026-08-25) - Hero becomes a content-owned module; site asset proxy; gesture growth fix ## 0.2.4 (2026-08-25) - Gates keep the question nav ## 0.2.3 (2026-08-25) - Attended-bucket lint rule; app-lifetime resources fix first-nav corruption ## 0.2.2 (2026-08-24) - One app-level Title fed by a shared site resource ## 0.2.1 (2026-08-24) - Docs: routing bullet + design doc marked implemented - Revalidating cache on app assets; site title survives SPA navigation ## 0.2.0 (2026-08-24) - Initial commit: content-driven onboarding portal - Load content from Gitea directly, drop the local clone; add deploy workflow - Share sccache between manual dev builds and CI - Give CI its own SCCACHE_SERVER_PORT - Erase view types at every list/component boundary, not just leaves - Redirect cargo-leptos's tool cache out of act-runner-bare's StateDirectory - Fix Ship release: site-root is project-relative, not CARGO_TARGET_DIR - Drop sudo from the deploy workflow - NoNewPrivileges blocks it outright - Caddy snippet: use multi-line log block - Fix static asset 404s and build-time SITE_NAME - Hot-reload content on a NATS trigger instead of requiring a restart - Add prosekit rich-text field, Gitea repo embeds, automation KV read endpoint; fix apex/www session-cookie mismatch on /auth/callback - Fix prosekit-editor.js: remove bare CSS imports that aborted the whole module - Redirect back to the originating page after sign-in, not always / - Fix hero-canvas navigation race; style the prosekit editor and add a toolbar - yes.js: guard against the canvas not being in the DOM yet - Fix get_resource/transition_answer: scope feature lookup to its own alternative - Add self-service transitions, authorized by item possession not group membership - Event-sourced applicant/subscriber/project aggregates, generalized resources - deploy.yml: write GITEA_API_TOKEN for the new GiteaStarred/OrgRepos resource sources - Cargo.toml: disambiguate bin-target for cargo-leptos - Add manually-triggered backfill workflow, no terminal/sudo needed - Trigger redeploy to pick up PORTAL_GITEA_API_TOKEN secret - Render generic resource lists as cards, not raw JSON dump - Fix recursion-limit build failure in the new item-card renderer - Resource-backed multi/single-select requirement - Add Organization aggregate (client-as-status), wired but inert - Fix favicon (real icon, not an unrelated orange circle) + stack encouragements with the submit button - Fix bucket-404 on empty resources, redesign review actions as select-then-confirm, CSS polish - Inline encouragements with the submit button, vertically centered - Use the real institutional logo for the header wordmark, not plain text - Use the actual UHHM-letters wordmark, not the institutional mark - Responsive base font size via svmin, not fixed 17px - Add plain-unit fallbacks for svmin/svh in case of unsupported engines - Size the YES canvas from its container, not window.innerHeight - Debounce the YES canvas resize handler and skip no-op resizes - Revert canvas sizing to window.innerWidth/innerHeight, gate resize on width change - Stop blocking native scroll on the YES canvas's touch handlers - Quicksand: font-display optional, not swap, to stop the text-jump on scroll - Use lvh/lvmin, not svh/svmin - svh was the wrong end of the viewport - Update style/main.css - Update style/main.css - YES canvas: size off its own container rect, not window.innerHeight - yes.js: freeze .hero-yes's height via inline style, don't trust svh alone - add responsive container width - deploy.yml: build and publish question_lint alongside the app binary - Make aggregate state graphs content-driven, not compiled Rust - One shared, content-labeled Confirm button per alternative instead of one per row - get_resource/get_requirement_options: #[server(default)] on params - Add optional Alternative.image and Feature.color/icon for richer layouts - Drop the feature list's left-padding - Drop question-report entirely, obfuscate the mailto link instead - Transition.from: state graphs deeper than one decision - Alternative.images: array of urls, rendered as a Swiper card deck - Reseed lost event history from the KV projection on transition - Lighten the comment load - Validate action targets; format timestamps client-side; clippy cleanup - Question nav + gateway alternatives - Responsible note under the title; followup pages out of the nav until a chain exists - README: what the engine provides - Feature icons: masked span painted by currentColor, not - Responsible note: one line, same hue and size as the hero description - Responsible note as : meta information, dimmer than the description - Responsible note back to the footer, above the question nav - Bound inputs: a field that loads its value from a sibling-parameterized resource - add some more width to our alternatives - Accent to a dusty press-cyan, a muted echo of the canvas's cyan ink - Editor type in rem: prosekit's px typography ignored the responsive root - Ban px from stylesheets: every length rem/em off the responsive root - No translucent input chrome: solid ink-dim placeholders and embed meta - Editor type scales for real: drop prosekit typography.css, fix selectors - Stable server fn endpoints: open tabs survive deploys - Drop stray local tool temp file, ignore .claude/ - Noise-driven YES, sticky hero with frosted cards, full light theme - increase translucency of laternative cards - Showcase jq test tracks Gitea repo shape and website-first links - Gesture input type, redoal-relay client, gitea_releases, content-driven branding - Echo thumbnails fade in via timeout, not rAF - Trigger deploy: redoal OAuth2 credentials now configured - Deploy becomes publish: portal ships as a versioned release artifact - Semver releases cut with cargo-release; publish on v* tags only - Docs: release/rollout flow after the instance-ownership refactor - README: frame portal as a generic multi-site question engine - Design doc: filesystem routes, sections, dynamic segments - Filesystem routes, sections, dynamic segments; instant YES hero ## What each site runs Read from the hosts on 2026-09-23; a rollout is a pin bump in the site's content repo (`PORTAL_RELEASE` in its `deploy.yml`), or a manual tarball deploy on kasse. | Site | Host | Instance | Version | |---|---|---|---| | uhhm.no | ergo | `app@uhhm-portal` | v0.3.36 | | redoal.com | ergo | `app@redoal-portal` | v0.3.36 | | westra.klingenbergbygg.no | kasse | `app@westra-portal` | v0.3.41 | | portal.klingenbergbygg.no | kasse | `app@klingenberg-portal` | v0.3.41 | | vel.klingenbergbygg.no | kasse | `app@tomtervel-portal` | v0.3.41 |