Related to the ForumWG topic of resolvable context collections, there are four FEPs that are currently in consideration:

  1. FEP-7888: Demystifying the context property
  2. FEP-400e: Publicly-appendable ActivityPub collections
  3. Draft FEP-171b: Conversation Containers, an evolution of Conversation Containers
  4. FEP-76ea: Conversation Threads

@[email protected] made a suggestion last month to hopefully reduce the number of moving parts:

  • Both FEP-400e and FEP-1b12 implementations: support FEP-7888 (context collection)
  • FEP-400e implementations: upgrade to Conversation Containers
  • FEP-1b12 implementations: add target property to Announce activity that points to context collection.

This takes FEP 400e out of the running (potentially). But the day after that last meeting, @[email protected] put together FEP 76ea, and now we’re back to three.

My concern is that all three FEPs (7888, 171b, and 76ea) all share these distinct qualities:

  • They establish a conversational context for a given object
  • They federate out an Add on collection addition. (76ea also sends Remove)
  • They contain some concept of a context owner (attributedTo)

They differ on the following qualities:

  • 7888/171b use context whereas 76ea uses a new property thr:thread
  • 171b specifies a new object type Context
  • Collection items:
    • 7888 sends objects in chronological order
    • 171b sends activities in chronological order
    • 76ea sends objects in reverse chronological order

In the lead up to the November WG meeting I’d like to address those differences. All three FEPs are in pre-draft or draft stages, and so I am hoping we can find some common ground and compromise.


Pinging interested parties (who were not already mentioned above) for comment:

@[email protected] @[email protected] @[email protected] @[email protected]

  • Evan Prodromou@cosocial.ca
    link
    fedilink
    arrow-up
    1
    ·
    19 days ago

    @julian @silverpill @trwnh @erincandescent @mikedev @[email protected] obviously context isn’t good enough to identify the thread relationship. Trying to hijack it is contrary to the AS2 spec and common interoperability. One proposal tries to specify it by duck-typing every object; the other by defining a new Collection type. The clearest way to define the relationship is with a property, thread, which is specific, clear, and requires no other changes.

    • Erin 💽✨@akko.erincandescent.net
      link
      fedilink
      arrow-up
      1
      ·
      19 days ago

      @evan @julian @silverpill @trwnh @mikedev “context” isn’t good enough to identify any relationship; this is the fundamental problem. It is a property that should never have existed. Today it identifies one relationship: the (loosely defined) thread. I would argue that this is in fact “common interoperability”. I would also argue that this fully meets the incredibly vague letter of the ActivityStreams specification.

      I have yet to see an alternative use of context that would not be better served by inventing a more specific term. Yes, this applies to use of context for thread too, but we have 7 years of its user to identify the thread as a defacto standard.

  • Scott M. Stolz@authorship.studio
    link
    fedilink
    arrow-up
    1
    ·
    8 days ago

    Something like this:

    {  "@context": "https://www.w3.org/ns/activitystreams",  "thr": "https://purl.archive.org/socialweb/thread#",  "thread": {    "@id": "thr:thread",    "@type": "@id"  },  "root": {    "@id": "thr:root",    "@type": "@id"  }  "traits": {    "traits": "inbox, outbox, conversation, amendable"  }}
    

    This would let me know that it is a thread and that people can follow the context. I can then render it as a thread and potentially add a follow or subscribe button for the context.

    You could also declare that it does not have an inbox and outbox, which saves me the trouble of checking myself.

  • Jenniferplusplus@hachyderm.io
    link
    fedilink
    arrow-up
    1
    ·
    20 days ago

    @julian @silverpill @evan @trwnh @erincandescent @mikedev
    I continue to feel that this general idea is in keeping with the as-written intent of the context property. Adding a new property is likely to make adoption slower and more complicated. Adding a new object type is also likely to make adoption slower and more complicated, but less so than the property. Either of those option only really makes sense to disambiguate from existing uses, of which there is very little.

    • Jenniferplusplus@hachyderm.io
      link
      fedilink
      arrow-up
      1
      ·
      20 days ago

      @julian @silverpill @evan @trwnh @erincandescent @mikedev
      As a side note, it’s beyond frustrating that the original goal of this topic was to accomplish reply-limiting controls. But, a year and a half later, we’re still bike shedding data types and haven’t even gotten to the point of talking about an actual behavioral protocol for proposing, accepting, rejecting, and verifying the addition of an object to a relevant collection.

  • Mike Macgirvin 🖥️@fediversity.site
    link
    fedilink
    arrow-up
    1
    ·
    20 days ago

    Conversation containers does send a ‘Remove’ if something is removed from a collection. But we typically don’t waste resources on spammers, so we don’t necessarily send a ‘Remove’ if we decided not to add it in the first place (perhaps due to comment controls, permissions, or blocks). You can send these as Remove activities if you want, but please don’t force me to do so.

    If you’ve fetched a conversation collection, it would be most common to do so in order to cache the conversation locally. In this case storing under local entities is going to be much more efficient if parents are stored before their children, so the InReplyTo can be pointed to an existing stored entity. If you do it backward, it’s kind of awkward.

    That said, our implementation of Collection objects has a flag to reverse the returned item order. So as long as the order is defined and isn’t going to change, I don’t actually care what it is; beyond the storage efficiency mentioned above. We need to sort it how we want after fetch anyway, because that’s just basic input sanitisation.

    It would seem most natural to me to send the collection in “thread” order - start from the root and recursively traverse every branch/leaf encountered. I’ll mention this for consideration – but again I really don’t care.

  • Scott M. Stolz@authorship.studio
    link
    fedilink
    arrow-up
    1
    ·
    10 days ago

    Reposting here, since the conversation got sidetracked onto Mastodon.

    @infinite love ⴳ

    Most contexts are multi-faceted in this way. #[1](https://w3id.org/fep/7888#the-different-types-of-context-and-how-they-are-actually-the-same)

    Yes they are. But, as I said, there are practical reasons for wanting to retrieve the conversation container (forum thread, social media thread, chat thread, etc.) as opposed to the reply tree.

    The biggest practical reason is delegation of moderation. In order to limit spam, trolls, and off-topic discussions, the owner(s) of the conversation container (thread) will moderate content by removing it from the conversation container, moving it to another conversation container, or flagging it in some way.

    If @julian spots a spam post on NodeBB, he can delete it. And if I retrieve the conversation container (thread) that NodeBB places the posts in, I will get the moderated version of the thread without the spam. If I parse the reply tree, I get the spam even though Julian deleted it.

    It also helps keep conversations together when the forum moderator moves a post to a different thread. If you follow the reply tree, the post would appear in the wrong thread.

    If you want threaded discussions to behave like threaded discussions over ActivityPub, you need to provide additional information that allows that to happen.

    cc: @Evan Prodromou


    1. https://w3id.org/fep/7888#the-different-types-of-context-and-how-they-are-actually-the-same ↩︎

  • Scott M. Stolz@authorship.studio
    link
    fedilink
    arrow-up
    1
    ·
    13 days ago

    To give you an example:

    Here is a thread (conversation with a topic):

    #[1](https://community.nodebb.org/topic/18364/fep-convergence-400e-7888-171b-conversation-containers-76ea)

    Here is a collection (a list of posts labelled “Forums and Threaded Discussions Task Force”):

    #[2](https://community.nodebb.org/category/31/forums-and-threaded-discussions-task-force)

    Notice how they are rendered completely different.

    A collection is NOT a thread. And some apps need to know which is the thread so we can render the display properly.


    1. https://community.nodebb.org/topic/18364/fep-convergence-400e-7888-171b-conversation-containers-76ea ↩︎

    2. https://community.nodebb.org/category/31/forums-and-threaded-discussions-task-force ↩︎

    • julian@community.nodebb.orgOP
      link
      fedilink
      arrow-up
      0
      ·
      10 days ago

      @[email protected] Yes. Per 1b12, audience is the appropriate property to use to denote a collection of loosely-related objects. context is ideal for closely-related objects (like ones that share a reply tree, but that doesn’t rise to the level of a requirement), like posts in a context (topic/thread).

      In your examples, the “thread” is a Collection when resolved and is referred to in objects via context. The “category” is an Actor when resolved, and is referred to in objects via audience.

      As for the other conversation, it honestly seems like you and @[email protected] are splitting hairs and really want the same things.

      • Scott M. Stolz@authorship.studio
        link
        fedilink
        arrow-up
        1
        ·
        10 days ago

        @julian The sticking point is basically whether I should guess what the thread is, or be told what the thread is.

        He wants me to perform a bunch of tests and guess; I want it to be declared.

        I am not sure what you are going to do when there are multiple collections associated with a post. Because unless the thread is declared, you won’t know which is which.

        • julian@community.nodebb.orgOP
          link
          fedilink
          arrow-up
          0
          ·
          10 days ago

          @[email protected] said in FEP Convergence (400e, 7888, 171b/Conversation Containers, 76ea):

          I am not sure what you are going to do when there are multiple collections associated with a post.

          My plan is to take the first item in context and treat that as the most narrowly scoped collection containing that object. The assumption is that the contexts are ordered from narrow-to-wide. Whether or not additional contexts would be helpful is irrelevant to me, as I am converting this object into a NodeBB topic only.

          You can make arguments that this is not correct. I will not disagree. I will simply say that, like it or not, this is the simplest form of compliance most implementors will opt for.

          • Scott M. Stolz@authorship.studio
            link
            fedilink
            arrow-up
            1
            ·
            10 days ago

            @julian The sad thing about this is that we have an opportunity to set the standard here, and we are not taking that opportunity.

            Yes, we have to assume that not all platforms will add the additional fields, and we have to have a fallback way of processing incoming posts.

            I am just flabbergasted that "thread": "https://remote.example/thread/117", is too hard to include with posts distributed by the forum. Even if you don’t use it, some of us might find that to be useful information.

            But it appears that I am alone in this, so I will just let it be.

            • julian@community.nodebb.orgOP
              link
              fedilink
              arrow-up
              0
              ·
              9 days ago

              Hi @[email protected], thanks for your thoughts. I took the evening to think it over.

              I agree that using thr:thread would be the most explicit way of signalling that NodeBB topics are threads, and if 76ea were to be adopted by a majority of implementors, then I would reconsider. Currently I am on the fence about whether or not I should implement it.

              I do want to point out that @[email protected]’s FEP 7888 doesn’t expressly forbid multiple contexts, and I think upthread they even said that such a use case would not be contrary to the FEP:

              based on this conversation i probably need to add examples to fep-7888 of how one might process multiple contexts. ~ trwnh

              My opinion today is that when NodeBB uses context, it is doing so to signal that “hey, these posts are part of this collection”, and exactly nothing more. It is up to the receiving end to decide how to implement it, and I think that this sort of variety is absolutely wonderful.

              If, for example, someone were to see my context and say “I’d like to map this context’s objects out into a word cloud and do sentiment analysis on it”, I think that’s a perfectly cromulent use of the data. For me to say “this here is a thread, and you better treat it as a thread” is contrary to the spirit of ActivityPub and fedidevs in general.

              It would be like the ForumWG saying to Eugen, @[email protected], et al. that “we’ve decided that YOU, Mastodon, need to support as:Listen and render inline players, and playlist support, etc.”. That’s ridiculous, wouldn’t you agree?

              • Scott M. Stolz@authorship.studio
                link
                fedilink
                arrow-up
                1
                ·
                9 days ago

                @julian

                If, for example, someone were to see my context and say “I’d like to map this context’s objects out into a word cloud and do sentiment analysis on it”, I think that’s a perfectly cromulent use of the data. For me to say “this here is a thread, and you better treat it as a thread” is contrary to the spirit of ActivityPub and fedidevs in general.

                Simply declaring a collection to be a thread does not force people to display it as a thread. But it does provide valuable information to people who do want to display it as a thread.

                So you are not removing people’s choice by simply stating that on your platform, this is considered a thread.

          • Scott M. Stolz@authorship.studio
            link
            fedilink
            arrow-up
            1
            ·
            10 days ago

            @julian If that is how you want to do it, I respect that. But I do find it odd that the FEP actually has a field for thread and nobody plans on using it. But, most platforms either have 1 context or 0 contexts, so it makes sense.

            I still haven’t decided how I will do things with my fediverse-enabled project management system. At some point, there will probably be multiple contexts. If that is the case, I probably will put the thread as the first context and then also declare what the thread is using FEP 76ea or something similar.

            That way, at least with my project, you won’t have to guess which one is the thread. And since your platform only has one context, then I won’t have to guess either.

  • silverpill@mitra.social
    link
    fedilink
    arrow-up
    1
    ·
    20 days ago

    @julian @evan @jenniferplusplus @mikedev @erincandescent @evan @trwnh

    >7888/171b use context whereas 76ea uses a new property thr:thread

    I think context is good enough. Streams and NodeBB already provide this collection and Mitra will too.

    >171b specifies a new object type Context

    It is for easier identification of conversation-Add activities. My server may receive many different kinds of Add activities, and it would be nice to have some indication of what collection is being modified.

    This is just an idea though. Streams uses Collection type

    >Collection items:

    My use case requires items to be activities, but I can support both variants of context collection. Conversation activities can be also put into a different collection.

    • Erin 💽✨@akko.erincandescent.net
      link
      fedilink
      arrow-up
      1
      ·
      20 days ago

      @[email protected] @[email protected] @[email protected] @[email protected] @[email protected] @[email protected] @[email protected]

      My plan is to bring Akkoma into conformance with FEP-7888. Contexts (as they implicitly exist) contain Objects in Akkoma today, and my plan is to make that collection dereferencable.

      At the moment I have no plan to implement sending of Add activities though that could come in the future.

      Regarding FEP-171B’s use of the Context object type, I am ambivalent. We will likely expose an (Ordered?) collection at first.

      FEP-76EA’s definition of thr:thread doesn’t match how Akkoma maintains or interprets the Context today. My opinion is that I would only invest time in implementing it if implemetations actually coalesced around it; it doesn’t suit our needs. In particular the requirement “The tree structure of the thread should be maintained; every object in the thread collection, except the root, should have an inReplyTo property that matches the id of another object in the collection.” does not match how Akkoma handles quote posts (it places them in the same Context).

    • julian@community.nodebb.orgOP
      link
      fedilink
      arrow-up
      0
      ·
      20 days ago

      @[email protected] said in FEP Convergence (400e, 7888, 171b/Conversation Containers, 76ea):

      It is for easier identification of conversation-Add activities. My server may receive many different kinds of Add activities, and it would be nice to have some indication of what collection is being modified.

      In that case, are you trying to sidestep a potential future conflict? I think I can see your rationale that duck typing “resolved context is a Collection” may not be specific enough, but I am not currently aware of anybody outside of 400e/7888 that has a context that even resolves to anything.

      • silverpill@mitra.social
        link
        fedilink
        arrow-up
        1
        ·
        20 days ago

        @julian

        No, that’s for target property. Here’s an example of Add.target from Streams:

        "target": {
          "id": "https://streams.lndo.site/conversation/ed4775f8-18ee-46a5-821e-b2ed2dc546e8",
          "type": "Collection",
          "attributedTo": "https://streams.lndo.site/channel/red"
        },
        
        

        In my code I have a handler for Add activity, which then sends activity to one of the other handlers:

        - Add{target: featured}
        - Add{target: subscribers}
        - Add{target: context}

        Currently I use heuristics to determine which one to use. But with Add.target.type == <meaningful type name> the code would be simpler and less fragile.

        Do you have something like that in NodeBB? I wonder how others solve this “routing” problem

        • julian@community.nodebb.orgOP
          link
          fedilink
          arrow-up
          0
          ·
          20 days ago

          @[email protected] said in FEP Convergence (400e, 7888, 171b/Conversation Containers, 76ea):

          Do you have something like that in NodeBB? I wonder how others solve this “routing” problem

          We do the same. We don’t actually handle Adds right now, but our Update handler has a switch..case based on type.

          Adding a separate type would be easier, yes. Otherwise you’re looking at additional logic to tease apart different variants that share the same base object type. For example, the logic for Update(Note) has further logic paths depending on whether the note is publicly addressed or not.

          So then yes, a Add{target: Collectionish} might be vague, but that’s only the case if other implementations use the same.

          • silverpill@mitra.social
            link
            fedilink
            arrow-up
            1
            ·
            20 days ago

            @julian This pattern can also be used in Accept and Undo activities:

            {
              "type": "Undo",
              "id": "https://social.example/activities/undo/1"
              "object": {
                "type": "Like",    # or "Follow"
                "id": "https://social.example/activities/like/1"
              }
            }