ADR 0044's step 6a (D42, D43, D45): accounts for programs, such as AI assistants, that an identified person makes from the Profile. halkyon_base keeps them on the instance's register, which its members ask: an agent is an account (agent_account: its owner, its name, its public keys, set by itself once) with one token (agent_token: 32 random bytes, kept as their SHA-256 alone, shown once). agents.create, mine, newToken, revoke, delete and setKeys serve its owner; agentSignIn.signIn exchanges the token for an ordinary server-side session of a day, with the scopes identified and agent, at most 60 sign-ins an hour from a network. Revoking a token or making a new one ends the agent's sessions; a token unused for 90 days lapses in every service's pruning. Up to 5 agents a person, where the operator didn't switch them off (AGENTS=off; said on the privacy page and in instance.basics). An agent makes no agents, creates or owns no shared space, is invited and raised as a Viewer alone, is never handed a space on, is named no operator and granted no assistants; each refusal has its test. Deleting a person deletes their agents first; an agent's public profile names whose agent it is. In the apps, every Profile has an Agents card (halkyon_app's AgentsSection): made with a name and a handle, its token shown once to copy, a new token, the token taken back, deleted. Learn's Share sheet marks an agent among the members and offers it no role above Viewer. The migrations, the module's and each host's (Learn, Meditate, Todo, Train), are additive alone: CREATE TABLE "agent_account" with a unique index on "agentId" (one row an agent account) and one on "ownerId" (a person's agents, and deleting them with the person), and CREATE TABLE "agent_token" with unique indexes on "agentId" (one token an agent) and "tokenHash" (the sign-in finds the token by its hash). No existing table is touched, so the server before the deploy keeps working on the migrated database. Whats-New: Make agents in your Profile: accounts for programs such as AI assistants, which sign in with a token and see only what you share with them. Whats-New-De: Leg in deinem Profil Agenten an: Konten für Programme wie KI-Assistenten, die sich mit einem Token anmelden und nur sehen, was du mit ihnen teilst. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
329 lines
24 KiB
Markdown
329 lines
24 KiB
Markdown
# ADR 0044: Shared spaces and collaboration, people and agents alike
|
|
|
|
- **Status:** decided 10 Nov 2026 by the developer: D8 is their own request, D9 and D10
|
|
their choices over the agent's recommendations (all ticked at first; agents editing
|
|
directly), D1 to D7 the agent's recommendations, which they chose; D11 to D13 the
|
|
agent's recommendations, which they chose on 11 Nov 2026, D14 too, and D15 to D26;
|
|
D27 their choice; D28 the agent's, to be confirmed; D29 to D32 the agent's
|
|
recommendations, which they chose.
|
|
Steps 1 to 5 and step 6's first part built; the rest below.
|
|
- **Builds on:** ADR 0005 (publishing, reports and suggestions), ADR 0014 (the module),
|
|
ADR 0016 (one account across an instance), ADR 0017 (key pairs), ADR 0019 (private
|
|
studysets), ADR 0021 (operators and assistants).
|
|
|
|
## Context
|
|
|
|
A studyset belongs to one person: their rows are in their personal space, a private
|
|
one sealed with a data key that only their own devices unwrap. The developer asked (10
|
|
Nov 2026) for Vaia's sharing (*Manage collaborators*: invite by email, an invite link,
|
|
the members and their roles; *Collaboration settings*: joining by link, collaborators
|
|
inviting others; *Share with the community*: choosing what material goes public) and
|
|
for collaboration with everyone, people and agents alike: *invite @Paul or @Claude to a
|
|
studyset*, Claude Code on halkyon.li's host working as an agent account, as Copilot or
|
|
Claude work on a GitHub repository.
|
|
|
|
The sync engine already has shared spaces (`OfflineSyncSpaces`: create, grant, revoke,
|
|
members), with two roles, `readOnly` and `readWrite`, which limit writes alone: a
|
|
member reads everything in the space.
|
|
|
|
## Decisions
|
|
|
|
- **D1.** **Shared spaces live in halkyon_base,** for every app: members, roles,
|
|
invitations, links, settings and the wrapped keys. Learn's studysets use them first,
|
|
Todo's projects later on the same tables.
|
|
- **D2.** **Roles: Owner, Editor, Viewer.** The Owner can do everything, delete the
|
|
studyset and hand it to another member; an Editor changes content; a Viewer reads and
|
|
studies, with their own reviews in their own space. Editor and Owner are the engine's
|
|
`readWrite`, a Viewer its `readOnly`; who is Owner is the module's own table, checked
|
|
by its endpoints.
|
|
- **D3.** **A private studyset's key is wrapped to each member's public key** (ADR
|
|
0017's key pairs) by a member's device that holds it, at its next sync after someone
|
|
joined. The instance never sees the key; reading waits for that sync. A member
|
|
removed gets no new content: the studyset rolls to a new key, as ADR 0019's rewrites.
|
|
- **D4.** **Invites by @handle or by email.** An @handle adds a person on the instance at
|
|
once (they see it in their Inbox and may leave). An email address gets a mail with a
|
|
link to join, which works after signing up; the address is never shown to the other
|
|
members.
|
|
- **D5.** **Joining by link is off at first;** someone joining by it is a Viewer, whom
|
|
the Owner can raise. *Collaborators may invite others* is off at first too.
|
|
- **D6.** **Agents are accounts of their own,** marked as agents, each owned by a person
|
|
who made it from their Profile; listed in the owner's Profile, deleted with them. An
|
|
agent has its own key pair, kept where it runs, signs in with a token the owner made
|
|
(never a password), and is invited like a person: *@claude*.
|
|
- **D7.** **An agent reaches the instance through a small CLI or MCP server** run beside
|
|
it (Claude Code on halkyon.li's host runs as its own user, without Docker): it signs
|
|
in with its token, syncs the spaces it is a member of, opens the sealed rows with its
|
|
own keys, and writes through the same endpoints an app uses.
|
|
- **D8.** **Share with the community** (publishing to Explore, ADR 0005) gets a step
|
|
confirming the material: every PDF, other file and note listed by kind.
|
|
- **D9.** That step's choice: **all unticked at first**, with per kind *tick all*, and
|
|
on any item *everything but this* and *nothing but this*. Cards and the learning path
|
|
always go.
|
|
- **D10.** **An agent suggests; a person accepts.** An agent is a Viewer in the engine's
|
|
sense and writes nothing into the studyset; its changes go as suggestions, which a
|
|
member with Editor's rights accepts or declines, each marked as by @claude. Learn's
|
|
reports and suggestions (ADR 0005, stage 4: `feedback_thread`, the author's
|
|
decisions, accepted suggestions applied by their item) are the start: they become
|
|
suggestions within a shared space too, not only on published releases.
|
|
|
|
- **D11.** **A shared studyset passes on.** Leaving, the Owner picks the next Owner
|
|
first. Deleting their account, ownership passes by itself to the Editor who joined
|
|
first, else to the Viewer who joined first; only with no other member does the
|
|
studyset go with them.
|
|
- **D12.** **An agent's token lapses after 90 days unused;** while used it has no fixed
|
|
end, the owner revokes it any time, and their Profile shows when it was last used.
|
|
- **D13.** **An invitation by @handle waits** for someone who has an account on the
|
|
instance's register but has never used the app: they find it in their Inbox the first
|
|
time they sign in to it, and the inviter sees them as invited until then.
|
|
|
|
- **D14.** **Sharing copies the studyset under new IDs** into the shared space, rewrites
|
|
what names its rows (reviews, tags, the learning path, files) and deletes the old ones,
|
|
as ADR 0019's rewrites do; the fork stays as it is. Found 11 Nov 2026: the engine's
|
|
delete is soft (`insteadOfDelete`: the domain row stays, hidden by a tombstone in its
|
|
space's `crdt_data_rows`), and a synced table's primary key is the row's ID alone, so
|
|
the same ID can't be inserted into another space; teaching the engine to move a row
|
|
was the other way, set aside as a deeper change to its merge, kept on every rebase.
|
|
|
|
- **D15.** **The module reaches the sync engine through its host** (decided 11 Nov 2026,
|
|
the agent's recommendation): each host hands halkyon_base a `SpaceGrants` (create a
|
|
space, grant, revoke, delete), made with `OfflineSyncSpaces`, as it hands a
|
|
`FileStore` (ADR 0043), so the module stays free of the engine and its tests run on a
|
|
stand-in.
|
|
- **D16.** **A shared space's tables are its service's own** (decided 11 Nov 2026, the
|
|
agent's recommendation): a Learn studyset's space and members are in Learn's
|
|
database, beside the engine's space they mirror, a Todo project's in Todo's; members
|
|
are the register's account IDs, their handles asked of the register as now.
|
|
- **D17.** **Never used the app means no account on its service yet** (decided 11 Nov
|
|
2026, the agent's recommendation, reading D13): someone without an `AuthUser` row on
|
|
the service gets a waiting invitation, made a membership the first time they sign in
|
|
there; on the register, where everyone has one, the invited are members at once.
|
|
- **D18.** **A member's public keys are pinned, a change refused, a safety code
|
|
compared** (decided 11 Nov 2026, the agent's recommendation): only the instance hands
|
|
a device a member's keys, and one that swapped in its own would read every studyset
|
|
shared with it. So each device keeps the keys it first saw for each person (as Signal
|
|
and SSH do), seals nothing to keys that changed and says so, and shows a safety code
|
|
made of both people's keys, which two members compare in person or by call; a swap
|
|
after the first sight is caught, one before it by comparing.
|
|
- **D19.** **Step 3 builds the module's wrapped keys and halkyon_app's device side**
|
|
(decided 11 Nov 2026, the agent's recommendation), each tested; Learn takes them into
|
|
its `Privacy` with the copy in step 4.
|
|
- **D20.** **A row in a shared space is trusted when any member this device pinned
|
|
signed it** (decided 11 Nov 2026, the agent's recommendation): current or former,
|
|
never by the author column a row names, which the instance could alter; a few
|
|
signature checks a row, for a group of a few people.
|
|
- **D21.** **A shared space is always sealed** with its key (decided 11 Nov 2026, the
|
|
agent's recommendation): sharing needs keys, so someone without a password sets one
|
|
first, and what goes public goes through *Share with the community*. ADR 0019's
|
|
readable records stay out of shared spaces.
|
|
- **D22.** **Rolling the key seals new writes alone** (decided 11 Nov 2026, the agent's
|
|
recommendation, reading D3): new and changed rows take the new key, earlier ones stay
|
|
under the key they had, which the members keep, since whoever went had read them
|
|
already. The instance marks a space whose member went, and the next writer's device
|
|
makes the new key and seals it to the members.
|
|
- **D23.** **A shared studyset is at the top level for every member** (decided 11 Nov
|
|
2026, the agent's recommendation): sharing takes it with its subtree out of its
|
|
parent, marked as shared; placing it in one's own tree may come later. Invited by
|
|
@handle, it appears in the invitee's list, which they may leave; the Inbox is step 5.
|
|
|
|
- **D24.** **Writes reach a shared space through the engine** (decided 11 Nov 2026, the
|
|
agent's recommendation): found 11 Nov 2026, every gateway writes in the personal space
|
|
and an update of a shared row finds none. Our sync fork updates and deletes a row in
|
|
its own space, checked against the person's role there, and asks the host which space
|
|
a new row goes in, which Learn answers in one place (a card by its studyset, a
|
|
revision by its card), so no gateway changes and none is missed; wrapping each write
|
|
in `transactionForUser` was the other way.
|
|
- **D25.** **Tags stay each member's own** (decided 11 Nov 2026, the agent's
|
|
recommendation): in their personal space under their personal key, as reviews (D2).
|
|
|
|
- **D26.** **Personal rows name shared content by a plain ID** (decided 11 Nov 2026, the
|
|
agent's recommendation): found 11 Nov 2026 by experiment, the engine blanks a
|
|
reference from one space to another (its rule, *foreign keys stay within a space*),
|
|
and reviews, tags (cards', notes', paths'), PDF readings and path answers are
|
|
personal (D2, D25) but name cards, notes, paths, steps and PDFs. Those six tables
|
|
keep their columns without the database relation; Learn deletes what names an item
|
|
as it deletes the item, and upkeep prunes rows naming one gone. Putting them in the
|
|
shared space would show members each other's reviews; teaching the engine to link
|
|
across spaces goes against its design.
|
|
|
|
- **D27.** **A published studyset isn't shared, for now** (decided 11 Nov 2026, the
|
|
developer's choice of the agent's corrected recommendation): publishing reads a
|
|
release from the person's readable rows on the instance (`readReleaseFiles`,
|
|
refusing a private studyset), and a shared studyset is sealed (D21) in another
|
|
space, so it could take no release. Sharing a studyset with a publication anywhere in
|
|
its tree, withdrawn or not, is refused, saying why, until publishing takes a
|
|
release's content from the device, a step of its own that re-points the publication
|
|
to the shared studyset then.
|
|
- **D28.** **The copy is the archive's export and import** (11 Nov 2026, the agent's
|
|
choice, for the developer to confirm): the tree written out with its progress, as a
|
|
backup is, and read back under new IDs into the space through the person's
|
|
repositories, so reviews, answers, readings, tags by name, stars and the archive
|
|
follow as an import's do and no rewrite of its own is needed; unlike a backup, it
|
|
keeps each item's link to Explore (`published`) and each card's origin, as the
|
|
studyset is the same one moved. The old tree is deleted for good once the copy is
|
|
whole; a copy failing or aborted deletes what it wrote and the space it made.
|
|
|
|
- **D29.** **Share opens one sheet** (decided 11 Nov 2026, the agent's recommendation):
|
|
People first (the members with their roles, *Invite by @handle*, or *Share with
|
|
people…* for a studyset not shared yet), then the community part as before (privacy,
|
|
publishing), as Vaia's Share does.
|
|
- **D30.** **A Viewer's studyset screen is read-only in step 4** (decided 11 Nov 2026,
|
|
the agent's recommendation): it says *You can view and study this* and hides adding,
|
|
editing, moving and deleting; a write that slips through elsewhere, which the engine
|
|
refuses, says so rather than failing silently. Each editor is checked one by one
|
|
later.
|
|
- **D31.** **A shared studyset is marked with people** (decided 11 Nov 2026, the agent's
|
|
recommendation): Lucide's *users* in place of the lock, as it is always sealed (D21),
|
|
labelled *Shared · private*.
|
|
- **D32.** **Safety codes are compared from a member's row** (decided 11 Nov 2026, the
|
|
agent's recommendation): their screen shows the code in twelve groups of five and
|
|
*Mark as compared*; a member whose keys changed is flagged on their row, and their
|
|
new keys are trusted on that screen, after comparing. Members are matched to their
|
|
keys by handle, as the member list names nobody by account ID.
|
|
- **D33.** **A writer's device rolls the key at its next upkeep** (decided 11 Nov 2026,
|
|
the agent's recommendation, reading D22): the instance marks a space whose member
|
|
went (removed, left or deleted; an invitation taken back never had a key), and the
|
|
Owner's or an Editor's device that next keeps its keys up after a sync makes the new
|
|
key and seals it to the members. Two devices rolling at once make two keys, both
|
|
sealed to everyone, the later one taken: harmless.
|
|
- **D34.** **A new key sealed clears the mark** (decided 11 Nov 2026, the agent's
|
|
recommendation): the first seal bringing a key ID the space never had, in the same
|
|
transaction. Members the rolling device missed (keys changed, not trusted anew) get
|
|
it later as newcomers do; waiting for every member would roll again and again.
|
|
- **D35.** **A Viewer opens cards, notes and steps read-only** (decided 11 Nov 2026, the
|
|
agent's recommendation, reading D30): the same screens as an Editor's, their content
|
|
shown but not editable, without Save, saying the studyset is theirs to view and
|
|
study; the lists' menus offer them nothing that writes.
|
|
- **D36.** **Burying and reporting a card are hidden in shared studysets for now**
|
|
(decided 11 Nov 2026, the agent's recommendation): both are written on the card's
|
|
row, so one member's would be everyone's, and a Viewer's would be refused. They return
|
|
once they are each person's own rows by the card's ID, as reviews are (D26), a change
|
|
of the synced tables shipped with a release.
|
|
- **D37.** **An email invitation admits only who proves the address** (decided 11 Nov
|
|
2026, the agent's recommendation, reading D4): accepting its link asks for a code mailed
|
|
to the invited address (halkyon_base's email codes), so a forwarded or leaked mail
|
|
admits nobody else; it lapses after 14 days, and someone without an account signs up
|
|
first.
|
|
- **D38.** **The instance keeps an invited address as a keyed hash alone** (decided 11
|
|
Nov 2026, the agent's recommendation): the mail goes out as the invitation is made,
|
|
then only HMAC-SHA256 of the address in lower case stays (ADR 0010's pepper), to check
|
|
the proof against. Members see "invited by email", never the address; resending means
|
|
typing it again.
|
|
- **D39.** **A link opens a page on the instance, then the app** (decided 11 Nov 2026,
|
|
the agent's recommendation): an email invitation's link and a space's join link (D5)
|
|
are pages (`/join/…`) saying what they are and to open Halkyon Learn, with a button
|
|
opening the app where it can (Android's app links) and the code to paste in the
|
|
Library's *Join with a link* everywhere else.
|
|
- **D40.** **What is shared with someone shows in their Inbox and on its card**
|
|
(decided 11 Nov 2026, the agent's recommendation): Profile's Inbox lists "@alice shared
|
|
Japanese with you" with Open and Leave, counted in its badge until seen, and the
|
|
studyset's library card says New until it is opened.
|
|
|
|
- **D41.** **The agent shares Learn's code rather than repeating it** (decided 11 Nov 2026,
|
|
the agent's recommendation): what reads and seals Learn's content (its models, the
|
|
payload codecs, the envelope, halkyon_app's `SharedKeys` and `KeyPins`) moves into
|
|
pure-Dart packages the app and the agent both use, and the agent syncs its spaces
|
|
through the engine's client (pure Dart) as an app does. Over an endpoint handing the
|
|
agent a space's rows with a codec of its own, which would keep the privacy rules in
|
|
two places, and over readable studysets alone, which would leave D6's keys for later.
|
|
- **D42.** **Step 6 is built in three parts** (decided 11 Nov 2026, the agent's
|
|
recommendation): agent accounts and their tokens in the module, made and revoked in
|
|
the Profile (D6, D12); suggestions within a shared space in Learn (D10); then the
|
|
agent itself, its CLI and MCP server (D7, D41). Each is usable and tested alone.
|
|
- **D43.** **An agent's token is exchanged for a session** (decided 11 Nov 2026, the
|
|
agent's recommendation): sent once to the module's sign-in for agents, it gets an
|
|
ordinary server-side session, so every endpoint, scope and revocation holds as for
|
|
people; the instance keeps the token's hash alone and stamps its last use at each
|
|
sign-in (D12), and revoking it ends the agent's sessions. Over the token on every
|
|
call, a second way of authenticating beside Serverpod's sessions.
|
|
- **D44.** **Whoever may not edit suggests** (decided 11 Nov 2026, the agent's
|
|
recommendation): an agent, and a person who is a Viewer, suggest changes within a
|
|
shared studyset, which an Editor or its Owner accepts or declines (D10 widened).
|
|
- **D45.** **Agent accounts as the register keeps them** (decided 11 Nov 2026, the
|
|
agent's recommendations): an agent is an account of the instance's register
|
|
(`agent_account`: its owner, its name), up to 5 a person, made from the Profile by an
|
|
identified person who is no agent, where the operator didn't switch agents off
|
|
(`AGENTS=off`, on otherwise, said on the privacy page). Its token (`agent_token`, one
|
|
an agent, 32 random bytes kept as their SHA-256 alone, shown once) signs it in for a
|
|
day at most; the agent signs in at each start. Its sessions carry `identified` and
|
|
`agent`, and an agent makes no agents, creates or owns no shared space, is invited and
|
|
raised as a Viewer alone, uses none of the operator's assistants, operates nothing
|
|
and signs in no other way. It sets its public keys itself, once. Deleting the owner
|
|
deletes their agents first, as accounts are deleted; its public profile says whose
|
|
agent it is.
|
|
|
|
## Steps
|
|
|
|
1. **Share with the community's confirmation** (D8, D9), needing nothing else here.
|
|
Built 11 Nov 2026.
|
|
2. **Shared spaces in halkyon_base** (D1, D2, D11, D13): the module's tables (a shared
|
|
space, its members and roles, its Owner), its endpoints with their denial tests,
|
|
the engine's grants kept in step, the hand-on as an account is deleted. Built 11 Nov
|
|
2026: the module's `sharedSpace` endpoint, Learn's `SyncSpaceGrants`; Todo hands over
|
|
no engine yet, so its server refuses (`unavailable`) until its projects are shared.
|
|
3. **Keys** (D3, D18, D19): a private studyset's key wrapped to each member's key pair.
|
|
Built 11 Nov 2026: the register hands out members' public keys (`public-keys-of`),
|
|
the module keeps the keys devices sealed (`shared_space_key`, its `sharedSpaceKeys`
|
|
endpoint, a Viewer sealing none, a member's keys going with their membership);
|
|
halkyon_app seals and opens them (`SharedKeys`), pinning each member's public keys
|
|
on first sight (`KeyPins`), with the safety code of halkyon_keys (`safetyCode`).
|
|
4. **Learn shares a studyset** (D14, D20 to D26): the copy, *Manage collaborators* (invite by
|
|
@handle, the members, their roles, leaving), each member's own reviews.
|
|
Built 11 Nov 2026 (D27 to D34): the copy into a space of its own, the Share sheet
|
|
with its members, roles, leaving and safety codes, a Viewer's screens read-only,
|
|
the key rolled once a member goes, and a Viewer's material opened read-only (D35),
|
|
burying and reporting hidden in shared studysets (D36).
|
|
5. **Invitations by email and by link** (D4, D5), the Inbox.
|
|
The instance's half built 11 Nov 2026 (D37 to D40): `sharedSpace.inviteByEmail`
|
|
mails through the register (`send-space-invitation`), naming who invites by their
|
|
handle (refused without one) and nothing of what is shared, and keeps
|
|
`shared_space_email_invitation` (the address's keyed hash, the SHA-256 of the
|
|
link's token, the role); `acceptEmailInvitation` takes a code asked for
|
|
`EmailCodePurpose.joinSpace`, proven through the register (`prove-address-code`),
|
|
and refuses one proving another address (`otherAddress`); inviting the address
|
|
again replaces the invitation, and the hourly pruning forgets those lapsed. A
|
|
space's link (`setJoinLink`, the Owner's; `joinLink`, whoever may invite;
|
|
`joinByLink`, as a Viewer) keeps its token as it is in `shared_space.joinToken`,
|
|
unlike a mailed one, so that it can be copied again: the instance decides who is
|
|
a member anyway, and switching the link off and on makes a new token, ending the
|
|
old one. `preview` and the page at `/join/<token>` (`addJoinPageRoute`, English and
|
|
German, `noindex`, `no-store`) say who invites and as whom, alike 404 for a token
|
|
that never was, lapsed or was switched off. A membership someone else made is new
|
|
to its member (`shared_space_member.isNew`, `addedBy`; `mine` names who shared it,
|
|
`markSeen` clears it); joining by a link or a mailed invitation is not.
|
|
The apps' half built 11 Nov 2026: halkyon_app's `SharedSpaceGateway` asks all of it
|
|
(`joinTokenOf` reads a link or its token pasted), its fake keeping the mails and
|
|
codes it would send; in Learn the Owner's Share sheet invites by handle or by
|
|
email and switches the link, copying it; the Library's menu joins with a link
|
|
pasted, and a link opened on Android (`/join/<token>`, verified with the
|
|
instance's assetlinks) opens the same on a screen of its own, asking for the
|
|
address and its code where the invitation was mailed; what is new shows in the
|
|
Inbox ("@alice shared Japanese with you", Open or Leave), on the Profile tab's
|
|
badge and as New on the studyset's card, until the studyset is opened.
|
|
6. **Agents** (D6, D7, D10, D12), in three parts (D42).
|
|
a. **Agent accounts** (D43, D45), built 11 Nov 2026: the module's `agent_account`
|
|
and `agent_token`, kept by the register and asked by its members;
|
|
`agents.create`, `mine`, `newToken`, `revoke`, `delete` and `setKeys` for the
|
|
owner, `agentSignIn.signIn` exchanging the token for a day's session with the
|
|
scopes `identified` and `agent`, at most 60 sign-ins an hour a network; a token
|
|
unused for 90 days lapses in each service's pruning. The refusals of D45 have
|
|
their denial tests (an agent making agents, creating or owning a space, invited
|
|
or raised above Viewer, handed a space on, named operator, granted assistants).
|
|
`instance.basics().agents`, the privacy page's sentence, `Profile.agentOf`. In
|
|
the apps, halkyon_app's `AgentsSection` in every Profile (made, token shown
|
|
once to copy, new token, taken back, deleted) and members marked as agents
|
|
(`SpaceMember.agent`; Learn's Share sheet: a bot, "Agent", no role above
|
|
Viewer offered, none to hand the studyset on to).
|
|
b. Suggestions within a shared space (D10, D44).
|
|
c. The agent itself, its CLI and MCP server, on a pure-Dart core (D7, D41).
|
|
|
|
## Open questions, before building
|
|
|
|
- **Moving rows between spaces.** Decided: D14.
|
|
- **Whose reviews.** A Viewer's or Editor's schedule for a shared card stays in their own
|
|
space, keyed by the card's ID; the card's own review rows today assume one learner.
|
|
- **Handing on and leaving.** Decided: D11. To build: the account's deletion hands the
|
|
shared spaces it owns on before it deletes the person's own.
|
|
- **Members across services.** Decided: D13. A member of a Learn studyset needs no Todo
|
|
account, and the reverse.
|
|
- **The agent's token:** made and revoked in the owner's Profile, scoped to the agent's
|
|
account, never shown again after making; its lapse decided: D12.
|