morgana: row 193 design r2: trail and replies hide other users' unshared thread messages (sage-1354), 193b split into b1 and b2, coverage from FED r4.1 covers (gizmoduck-0018)
This commit is contained in:
1 parent
5d15979e06
commit
19c015741d
1 file changed
+54
-24
@@ -1,6 +1,6 @@
|
||||
# Row 193 design: many humans per business, human roles, private and shared threads
|
||||
|
||||
Morgana (Opus 5.5), 2026-10-20. Gitea #1691, queue row 193, brief `docs/plans/2026-10-20_human-users.md`
|
||||
Morgana (Opus 5.5), 2026-10-20. Revision 2: sage-1354 (5.7 private from the owner, 193b split) and gizmoduck-0018 (coverage and the R4 predicate in FED r4.1). Gitea #1691, queue row 193, brief `docs/plans/2026-10-20_human-users.md`
|
||||
(blob f7472d1e47b1). Reviewer Duckworth, gate owner Sage. Base: `refactor` at 53bb59462.
|
||||
|
||||
Read for this design: COMMON.md (53bb59462), the brief, Gizmoduck's federation design r3.1 (a2840a6be on
|
||||
@@ -252,7 +252,8 @@ and `thread.shares`. A new `#threadRead(s, id)` admits the owner or `#readable`,
|
||||
| `thread.messages` | `#thread` | `#threadRead` |
|
||||
| `thread.open`, `thread.send` | `#thread` | `#thread`, plus a collaborator grant for users |
|
||||
| `project.list`, `names` | business-wide | per FED 3.5 for users, unchanged for the owner |
|
||||
| `trail`, `replies`, `messages`, `inbox`, `agents`, `tasks`, `decisions`, `credentials` | human or reader | users refused (`access-refused`); owner unchanged |
|
||||
| `trail`, `replies` | human, reader or tester; any message by id | users refused (`access-refused`); for the others, messages in other users' unshared threads hidden (5.7) |
|
||||
| `messages`, `inbox`, `agents`, `tasks`, `decisions`, `credentials` | human or reader | users refused (`access-refused`); owner unchanged. A user-thread run can't send to the human (`user-thread-human`, FED 3.6), so `messages` holds none of a user's thread |
|
||||
| `message.receive` by a run | its own thread (:1933) | unchanged: runs read their own thread, never a share |
|
||||
| `threadsState` (launcher) | all threads | adds `revoked` per 5.6 |
|
||||
|
||||
@@ -267,22 +268,36 @@ Gizmoduck asked that a grant loss end access to threads shared with that user (g
|
||||
- An unshare row ends access on the next request in the same way.
|
||||
- A user who loses coverage of their own thread also loses its shares: rule 3 in 5.4.
|
||||
|
||||
One change to FED R4. FED 3.7 marks a user thread `revoked` when no live grant covers it. With viewer, that
|
||||
is wrong: a viewer grant covers the thread but must not let a run continue. Row 193 changes the predicate
|
||||
for `revoked`, claim end and the launch check to "no live **collaborator** grant covers the thread". A
|
||||
downgrade to viewer then ends the run within one launcher tick, as a revocation does. This has its own test
|
||||
and mutant (section 8).
|
||||
The downgrade case. With viewer, "no live grant covers" would be wrong for runs: a viewer grant covers the
|
||||
thread but must not let a run continue. FED r4.1 (73677b87d, gizmoduck-0018) puts this in R4: `revoked`,
|
||||
claim end and the launch check call `covers` with level `collaborator`, so a downgrade to viewer ends the
|
||||
run within one launcher tick. Row 193 doesn't carry that change. 193b1 still has a downgrade test and a
|
||||
mutant (section 8), because viewer is new in this row.
|
||||
|
||||
### 5.7 What "private" means, stated plainly
|
||||
|
||||
Sage ruled (sage-1354): a user's thread is private from the owner on the bus too. Jason's words are "private
|
||||
agent sessions per user", and in Case 2 the owner of Melanie's hub is not the user whose thread it is.
|
||||
|
||||
- A user's thread is private from every other user and viewer at the bus boundary. They reach the bus only
|
||||
through the Console and the row 196 verifier, and every read is filtered.
|
||||
- It is not private from the owner in a strong sense, and the design doesn't claim it is. The thread's
|
||||
instance runs with the owner's role authority (F10), the owner holds the store as the instance's OS user,
|
||||
and `trail` and `replies` read any message by id. Row 193 adds no owner verb that lists or reads other
|
||||
humans' threads, and the Console doesn't show them. I chose to leave `trail` and `replies` unfiltered so
|
||||
the owner can audit what their instance did on a user's behalf. Tradeoff: an owner who has a message id
|
||||
can read it. Filtering those two verbs is a small change if Duckworth or Sage wants it.
|
||||
- It is private from the owner at the bus boundary as well. Row 193 adds no owner verb that lists or reads
|
||||
another human's threads, the Console doesn't show them, and the two board reads that return messages by
|
||||
id, `trail` and `replies`, filter them (below).
|
||||
- The filter. A message is hidden from a session when it is in a thread (`message_threads`) whose owner is a
|
||||
`user:<id>` other than the caller and `#readable(s, t)` is false. Threads owned by the business human or
|
||||
by `tester` are not affected, so row 177's behaviour is unchanged.
|
||||
- `trail`: drops hidden messages, their `deliveries` rows, and `events` rows whose `subject` is a hidden
|
||||
message id. A `subject` that is itself a hidden message returns `message-not-found`, as an unknown id
|
||||
does, so the reply doesn't confirm the id exists.
|
||||
- `replies {id}`: a hidden `id` fails `message-not-found`, and hidden messages are dropped from the
|
||||
result.
|
||||
- The filter applies to every session that can call these verbs: the owner, reader sessions and the
|
||||
tester.
|
||||
- What it does not do. The owner holds the store as the instance's OS user and can read it with sqlite, and
|
||||
the thread's instance runs with the owner's role authority (F10). That is the same-uid limit in section 6,
|
||||
not a bus path. An owner audit verb, explicit and logged, is a follow-up row only if Jason asks for one
|
||||
(sage-1354).
|
||||
|
||||
## 6. The bus human boundary (brief point 5)
|
||||
|
||||
@@ -342,7 +357,7 @@ the instance's uid.
|
||||
|
||||
## 8. Tests and mutants
|
||||
|
||||
Bus (193b), each in `packages/bus/tests/`:
|
||||
Bus, each in `packages/bus/tests/` (193b1: viewer, mixed grants, downgrade, board reads; 193b2: shares, migration, the 5.7 filters):
|
||||
|
||||
- Viewer: each allowed verb works, every other verb refuses `access-refused`, including `thread.create`,
|
||||
`thread.open`, `thread.send` and `thread.share`.
|
||||
@@ -360,6 +375,11 @@ Bus (193b), each in `packages/bus/tests/`:
|
||||
- Non-owner `thread.share`, `thread.unshare`, `thread.shares` refuse. Tester refuses on all three.
|
||||
- `thread.list` plain shape unchanged. `thread.list {shared: true}` returns only readable threads.
|
||||
- Board reads refuse users and viewers.
|
||||
- `trail`, for the owner, a reader session and the tester: a user's unshared thread message as `subject`
|
||||
fails `message-not-found`; a decision trail that reaches such a message omits it, its deliveries and its
|
||||
events. Once the thread is shared with the owner, the owner's trail shows it.
|
||||
- `replies`, same three sessions: a hidden `id` fails `message-not-found`; hidden replies are dropped. The
|
||||
owner's own and the tester's threads are unchanged.
|
||||
- Migration: a 3i store with threads migrates to 3j, row counts unchanged, `thread_shares` empty and
|
||||
append-only (update, delete, replace each abort). Restore-guard and `COUNTED` include it.
|
||||
|
||||
@@ -382,30 +402,40 @@ Mutants, each must be killed:
|
||||
8. `share-not-covered` check removed.
|
||||
9. `thread.share` owner check removed (sharee re-shares).
|
||||
10. A user allowed `trail`.
|
||||
11. The `trail` filter removed (the owner reads a user's unshared message by id).
|
||||
12. The `replies` filter removed (the owner reads replies in a user's unshared thread).
|
||||
|
||||
## 9. Build rows and order
|
||||
|
||||
| Row | Work | Chains on | Schema |
|
||||
|---|---|---|---|
|
||||
| 193a | R1 level check widened to viewer | FED R1 candidate | none |
|
||||
| 193b | Bus: viewer verb list, per-grant level checks, `revoked` predicate, `thread_shares`, share verbs, `#readable`, `#threadRead`, filters | FED R3, R4 candidates, row 196 verifier | 3j |
|
||||
| 193b1 | Bus: viewer verb list, per-grant level checks, `revoked` predicate, users refused on board reads | FED R3, R4 candidates, row 196 verifier | none |
|
||||
| 193b2 | Bus: `thread_shares`, share verbs, `#readable`, `#threadRead`, the 5.5 filters, the 5.7 `trail` and `replies` filters | 193b1 candidate | 3j |
|
||||
| 193c | `mosaic access` user verbs and `--level viewer`, if R5 lacks them | FED R5 candidate | none |
|
||||
| 193d | Console: shared list, share controls, viewer read-only | FED R6, row 192 candidates | none |
|
||||
| 193d | Console: shared list, share controls, viewer read-only | FED R6, row 192 candidates, 193b2 for share controls | none |
|
||||
|
||||
193b can be split again if Sage wants the no-schema part live first: 193b1 viewer and level checks,
|
||||
193b2 `thread_shares`. Both stay one design.
|
||||
Sage ruled the split (sage-1354): 193b1 has no schema change and can go live first. 193b2 carries 3j, and
|
||||
Sage takes its migrate window to Mos when 193b2 is near. The 5.7 filters go in 193b2 because they use
|
||||
`#readable`. Until 193b2 lands no user thread exists live (FED R8 is later), so nothing is exposed in
|
||||
between.
|
||||
|
||||
Dependency I need settled by R1 or R3 before 193b: how a grant's `projects` matches a thread's project.
|
||||
FED 3.5 says `projects` is `["*"]` or "declared project ids". Threads point at bus project UUIDs
|
||||
(`projects.id`, broker.mjs:2306), and a bus project may have no `repo` in the business file. Row 193 uses
|
||||
whatever single `covers(grant, {project, instance})` R3 defines and adds no second one. I am raising this
|
||||
with Gizmoduck.
|
||||
Coverage, settled in FED r4.1 (73677b87d, gizmoduck-0018, still under Duckworth's review). A grant's
|
||||
`projects` is `["*"]` or bus project UUIDs (`projects.id`, broker.mjs:2306; `threads.project` references it).
|
||||
R1 exports one pure `covers(grant, {business, project, instance}, now, {level})`. Row 193 uses only that
|
||||
function and adds no second one (Sage confirmed, sage-1354):
|
||||
|
||||
- share reads (5.4 rules 2 and 3) and `share-not-covered`: `covers(g, thread, now, {})`, any level;
|
||||
- `thread.create`, `thread.open`, `thread.send`, `thread.share`: `covers(g, thread, now, {level: 'collaborator'})`;
|
||||
- `project.list` and `names` for users: any level, as FED 3.5.
|
||||
|
||||
`covers` needs no further argument for row 193. If r4.1 changes in review, 193b1 follows it.
|
||||
|
||||
Follow-ups, not in this row:
|
||||
|
||||
- A read-only users and grants page for the owner in the Console.
|
||||
- A second owner or admin level, if Jason wants one.
|
||||
- Filtering `trail` and `replies` for user threads, if 5.7's tradeoff is ruled the other way.
|
||||
- An explicit, logged owner audit verb for user threads, only if Jason asks for one (sage-1354).
|
||||
|
||||
## 10. Gate
|
||||
|
||||
|
||||
Reference in new issue
Block a user