Invites and suggestions are cases; the responsible follow the content
Test / test (pull_request) Successful in 56s

Five things the first cut of accounts-from-content left open.

The first person and the responsible people are set up side by side,
and both try again a minute apart: a seed that waited twenty minutes on
Kanidm used to hold everyone else up, and a responsible person who
failed waited for the next restart.

The responsible are gone over again whenever the content changes.
watch_for_reload publishes portal.content.reloaded once new content is
in place, and seed.rs listens.

Being responsible ends when the content stops saying so: someone no
page names any more leaves RESPONSIBLE_GROUP in the same pass, if
portal is who put them there (Invited.already_member). The account
stays.

An invite keeps itself current. portal_invites has an expired state;
signing in closes a person's invites, a sweep moves the ones whose link
ran out, and a desk's move from expired back to open makes a fresh link
and mails it. Invites made at start are on file with the desks'.

A suggested edit is a case in the built-in portal_edit_suggestions
bucket, the person who made it is mailed at each move, and the card
lists what they sent and where it stands.

Found on the way, against Kanidm 1.11.2: a link's end comes as seconds
since the epoch, which portal did not read, so invite mails said
"until" and nothing; and a mail search answered with a 5xx was taken
for a refusal.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GT37Z1Xtfd9pUuQtMTg6Yt
This commit is contained in:
Bendik Aagaard Lynghaug
2026-09-29 21:00:10 +02:00
co-authored by Claude Fable 5.1
parent 7a50ce77b2
commit ef4789f9b2
14 changed files with 1025 additions and 88 deletions
+5 -1
View File
@@ -167,7 +167,7 @@ pub async fn transition_answers(
/// transition retries. Keeps "zap the whole event stream" a safe
/// operation instead of one that strands every pre-wipe record.
#[cfg(feature = "ssr")]
async fn transition_or_reseed(
pub(crate) async fn transition_or_reseed(
js: &async_nats::jetstream::Context,
schema: &crate::aggregates::AggregateSchema,
item_id: &str,
@@ -291,6 +291,10 @@ async fn apply_transition(
.map_err(|e| ServerFnError::new(e.to_string()))?;
crate::mail::on_state(state, bucket, &item.item_id, &item.to, &answer.responses).await;
crate::people::on_state(state, bucket, &item.item_id, &item.to, &answer.responses).await;
// An expired invite sent again: the move back is the asking.
if bucket == crate::people::INVITES_BUCKET && item.to == OPEN_STATE {
crate::people::on_reopened(state, &item.item_id).await;
}
// The decision extends the DAG as a child of the answer's own
// submission hash, published like any other answer.