Kgothatso Ngako d7aac49cf1 fix: hold a payload whose parent has not arrived instead of losing the event
A receiver hit `FOREIGN KEY constraint failed` on an artifact submission
and lost the whole group event. The artifact referenced a dialect the
receiver did not have, MantraArtifact.dialectId is a foreign key, and
SQLite answers a violated constraint by aborting -- which rolled back
the entire transaction the inbound pipeline runs in. Gone with it: the
NostrEvent, the MarmotGroupEvent, the submission's MarmotInnerEvent
holding the payload verbatim, and the transcript line. Nothing retries,
so the artifact stayed lost even once the dialect turned up.

Every nip30303 entity is a child of another and the schema enforces all
of it -- artifact→dialect, version→artifact, chapter→version,
chunk→chapter, translations→both of theirs -- so this was every branch,
not one.

And submissions make arriving before your parent ordinary rather than
exotic. That is the point of them: an admin submits a backlog in
whatever order they hold it, and a member who joined last week can be
sent what the group was told last month. Both produce payloads whose
parents are not here yet, and both were losing data.

So check the parents before inserting. A payload that arrives early is
held on the submission row -- awaitingEventId names what it waits for --
and applied when that arrives. Releasing one can release another, a
version freeing its chapters and those freeing their chunks, so it walks
outward until nothing more comes unstuck. A payload with a second parent
still missing is re-pointed at that one rather than retried on every
arrival.

Nothing is written to the transcript while a payload is held. Nobody has
said anything yet; the line appears when it is applied, in the position
its own timestamp gives it.

Two things fall out of the shape:

parentRefsOf is pure and separate from the lookups, because the mapping
is the part that can silently drift from the schema and there is no
database harness in commonTest to catch it. ParentRefsTest pins one case
per kind. Which table an id lives in is carried as the kind of event
that would have created it, so there is no second enum to keep in step.

applyInnerEvent takes ids rather than a GroupEvent, since replay happens
long after that object is gone. A released payload is recorded as not
ours: we hold the parents of anything we wrote, having written those too.

Also reconstructs a held bare nip30303 event from its own columns rather
than parsing its content as an event -- only submissions carry an event
there, and reading both that way would have stranded every bare one
permanently.

Verified: the v5→v6 migration runs clean on the receiver's real
populated database. The hold path itself still needs a fresh submission
from a sender to exercise end to end.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-05 21:05:35 +02:00
2026-07-05 23:21:02 +02:00
2026-07-28 09:10:20 +02:00
2026-03-24 04:47:19 +02:00
2026-03-23 01:41:39 +02:00
2026-03-23 01:41:39 +02:00
2026-03-23 01:41:39 +02:00

This is a Kotlin Multiplatform project targeting Android, iOS, Desktop (JVM).

  • /composeApp is for code that will be shared across your Compose Multiplatform applications. It contains several subfolders:

    • commonMain is for code thats common for all targets.
    • Other folders are for Kotlin code that will be compiled for only the platform indicated in the folder name. For example, if you want to use Apples CoreCrypto for the iOS part of your Kotlin app, the iosMain folder would be the right place for such calls. Similarly, if you want to edit the Desktop (JVM) specific part, the jvmMain folder is the appropriate location.
  • /iosApp contains iOS applications. Even if youre sharing your UI with Compose Multiplatform, you need this entry point for your iOS app. This is also where you should add SwiftUI code for your project.

Build and Run Android Application

To build and run the development version of the Android app, use the run configuration from the run widget in your IDEs toolbar or build it directly from the terminal:

  • on macOS/Linux
    ./gradlew :composeApp:assembleDebug
    
  • on Windows
    .\gradlew.bat :composeApp:assembleDebug
    

Build and Run Desktop (JVM) Application

To build and run the development version of the desktop app, use the run configuration from the run widget in your IDEs toolbar or run it directly from the terminal:

  • on macOS/Linux
    ./gradlew :composeApp:run
    
  • on Windows
    .\gradlew.bat :composeApp:run
    

Build and Run iOS Application

To build and run the development version of the iOS app, use the run configuration from the run widget in your IDEs toolbar or open the /iosApp directory in Xcode and run it from there.


Learn more about Kotlin Multiplatform

Description
Mantra as a kotlin multiplatform project
https://mantra.press
Readme 8.2 MiB
Languages
Kotlin 100%