What we’re trying to accomplish:

Make it possible for the client to show organization icons (logo) and, at some point in the near future, organization names in the drop-down list of groups available to a user at a particular URI.

This discussion centers around how to make that possible from the API-endpoint perspective, based on what is in place now and what we know now.

What we have now



Please note: This is entirely a technical discussion. The actual behavior of groups, per requirements, will not be affected by this

Good news (I think!)! Excluding migrations and tests, the only references to joinableBy in the codebase are:

  • The model itself
  • The group service in its create* methods
  • h.db’s __init__ where the world group is created (once) with a joinableBy of None
  • h.views.activity in one or two spots that I think Sheetal is removing as I type this

Other group-related services and views don’t give a hoot about joinableBy and I’d argue, neither should we. Removing it would simplify the “group type” matrix and relieve confusion about what “joining” actually means. There’s nothing in our data model that actually prevents a membership relationship from being created, and the fact that the creator is immediately added as a member to a new restricted group “violates” the notion of joinableBy = None anyway, in some ways.

# -*- coding: utf-8 -*-
from __future__ import unicode_literals
class GroupJSONPresenter(object):
"""Present a group in the JSON format returned by API requests."""
def __init__(self, group, route_url=None): = group
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
class ListGroupsService(object):
A service for providing filtered lists of groups.

What we have now

GET /profile

Returns a profile object that contains, among other things, a groups property, an Array of objects; that part of the schema looks like:

    "groups": {
      "type": "array",
      "items": {

This is my overall assessment of what groups “mean” as an intersection of prose requirements and what the h backend app currently has in its code. Some (most?) of this may be rehash and may elicit the “yeah, right, we already know this” response, but if that happens, yay, we’re in alignment and if you think “gosh, this is wrong in some/all ways” that’s a good thing to identify, too.

Extreme tl;dr:

  1. For open groups, Group resources need an additional constraint that defines what the set of valid URLs are for target documents that may be annotated within that group, and this is distinct from access or permissions. I am calling that constraint scope for shorthand, which may be a totally terrible name.
  2. The list of “this user’s groups” is variable and depends on scope/context.
  3. I’m not proposing a specific way to implement a scope constraint (yet, at least) in the data model

Vocabulary Interlude

I am going to use the following terms as defined here. Some of this may seem overtly pedantic, but


What does “groups” mean to the application right now?


Acitivity’s query module has some interaction with groups in constructing and executing queries for activity pages.



The GET profile endpoint returns an object with a groups attribute—an Array of objects with:

View APDS9960.js
// Datasheet:
const Emitter = require('events').EventEmitter;
const util = require('util');
const I2C_ADDR = 0x39; // Sensor's hardwired I2C address (p.8)
const DEVICE_ID = 0xAB; // The device will report this ID (p.25)
const REGISTERS = { // Register addresses
ENABLE : 0x80, // Enable different sensors/features (p.20)
WTIME : 0x83, // Wait time config value (p.21)


These notes and questions are the summary of my analysis of Section 7.1: Browsing Contexts in the WHATWG HTML spec and its related WPT test coverage in html/browsers/windows/.

While I'm a long-time spec consumer and standards-based web dev, this is my first project involving contribution to the project.

General Test Running and Authorship Questions

These are notes or questions that arose from examining the tests for 7.1 but apply more broadly to the WPT infrastructure and conventions.

View deleteEmptyTrees.js
'use strict';
var walk = require('walkdir');
var fs = require('fs');
var path = require('path');
var extfs = require('extfs');
var deleteEmptyPath = function deleteEmptyPath(delPath, cb) {
extfs.isEmpty(delPath, function (isEmpty) {
if (isEmpty) {