M12e: contract pass (pair invite kind, admin name in invite links, sync failed flag, rejection actor, device version name, template id, update check result, untouched accounts, D-027 re-stamp, PushEvent in :core:model, snackbar hosts)

Server: E-3 invite_kind; E-12 link ?from=<admin name>, E-4 passes it into kvitt://invite;
X-Kvitt-App-Version-Name stored in devices.app_version_name (migration 0003, schema 3);
OpenAPI and examples follow. Phone: :core:model, :core:fakes, :data, features and :app
updated together; goldens re-recorded for S-20 (version name) and new S-1/S-22 states.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TQd3pEP9UeN2uifbHknQ7B
This commit is contained in:
2026-10-18 15:00:50 +02:00
parent 6159e18198
commit 8c607153c0
96 changed files with 1090 additions and 298 deletions
+12 -3
View File
@@ -6,8 +6,8 @@
//! hold for every operation are applied once here, by [`CommonRules`], so they cannot drift per handler:
//!
//! * SPEC §14.3.1 headers: every operation takes `X-Kvitt-Protocol` (required on authenticated operations, optional
//! on the unauthenticated ones), `X-Kvitt-App-Version` and `Accept-Language`; every response carries
//! `X-Kvitt-Server-Version`.
//! on the unauthenticated ones), `X-Kvitt-App-Version`, `X-Kvitt-App-Version-Name` and `Accept-Language`; every
//! response carries `X-Kvitt-Server-Version`.
//! * SPEC §14.3.2 always-possible errors: `401`, `426`, `429`, `500`, `503` on authenticated operations and `429`,
//! `500`, `503` on unauthenticated ones, unless the operation documents its own.
//! * Security: the document-level requirement is `device` (bearer, SPEC §8.2); unauthenticated operations declare
@@ -258,6 +258,15 @@ impl Modify for CommonRules {
plain_schema(Type::Integer),
),
);
components.parameters.insert(
"AppVersionName".to_owned(),
header_param(
"X-Kvitt-App-Version-Name",
false,
"The app's `versionName`, sent by the app on every call; stored per device like `X-Kvitt-App-Version` (1 to 32 characters, anything else ignored).",
plain_schema(Type::String),
),
);
components.parameters.insert(
"AcceptLanguage".to_owned(),
header_param(
@@ -339,7 +348,7 @@ fn apply_operation_rules(operation: &mut Operation) {
} else {
"Protocol"
};
for name in [protocol, "AppVersion", "AcceptLanguage"] {
for name in [protocol, "AppVersion", "AppVersionName", "AcceptLanguage"] {
parameters.push(RefOr::Ref(Ref::new(format!(
"#/components/parameters/{name}"
))));