fed-sx-m2: Step 6a — follower_graph projection + 18 tests
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 42s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 42s
New next/kernel/follower_graph.erl is the Erlang-fun stand-in for
the genesis follower-graph.sx projection body, mirroring the
shape of actor_state.erl and define_registry.erl.
State shape (substrate has no maps, so a proplist):
[{ActorId, [{following, [PeerId, ...]},
{followers, [PeerId, ...]},
{pending_outbound, [PeerId, ...]},
{pending_inbound, [PeerId, ...]}]}, ...]
Fold rules per design §13.2:
Follow{actor: A, object: B}
add B to A.pending_outbound
add A to B.pending_inbound
Accept{actor: B, object: Follow{A->B}}
A moves from B.pending_inbound -> B.followers
B moves from A.pending_outbound -> A.following
Reject{actor: B, object: Follow{A->B}}
clear A from B.pending_inbound, B from A.pending_outbound
Undo{actor: A, object: Follow{A->B}}
drop A<->B from every list on either side
only the Follow's original actor may Undo it
Edge cases handled:
- self-follow (alice -> alice) is a no-op
- duplicate Follow is idempotent (list sets)
- Accept/Reject/Undo whose :object isn't a Follow proplist
passes through
- Undo by the wrong actor (carol Undoing Follow{alice->bob})
is a no-op
Public API:
new/0, lookup/2, actors/1
following/2, followers/2,
pending_outbound/2, pending_inbound/2
is_following/3, has_follower/3,
is_pending_outbound/3, is_pending_inbound/3
fold/2, fold_fn/0
fold_fn/0 returns the standard 2-arity Erlang fun for
projection:start_link/3 (same plug shape as actor_state and
define_registry).
Local find_keyed/set_keyed/contains/remove_member helpers — no
lists:keyfind/keymember/member in this substrate (same gap as
Step 1a/2b/5a/5c).
18/18 in next/tests/follower_graph.sh covering all four verbs,
predicates, edge cases (self-follow, duplicate Follow, untyped
activity, non-Follow :object, wrong-actor Undo).
Step 6b wires this into the inbox handler so a peer Follow lands,
fires auto-Accept publish (open-world policy per §13.2; manual
moderation deferred to v3).
Conformance 761/761. 130/130 across 9 Step-6-adjacent suites
(inbox, inbox_bucket, inbox_pipeline, inbox_peer_resolution,
actor_state_pure, define_registry_pure, projection_pure,
nx_kernel_multi, smoke_app_pure).
This commit is contained in:
@@ -419,23 +419,28 @@ tracks the state. `Undo{Follow}` reverses it.
|
||||
|
||||
**Deliverables:**
|
||||
|
||||
- New activity-types (runtime via DefineActivity, ideally):
|
||||
Follow, Accept, Reject, Undo.
|
||||
- Follower-graph projection (Erlang-fun stand-in): tracks
|
||||
`{ActorId => #{following => [PeerId], followers => [PeerId],
|
||||
pending_outbound => [PeerId], pending_inbound => [PeerId]}}`.
|
||||
- Accept-handling fold logic: when A receives `Accept{Follow A→B}`,
|
||||
move B from `pending_outbound` to `following`.
|
||||
- Reciprocal: when B receives `Follow A→B`, automatically queue an
|
||||
outbound `Accept` (auto-accept policy; manual moderation v3).
|
||||
|
||||
**Tests:**
|
||||
|
||||
- Follow → 202; sender's pending_outbound includes target.
|
||||
- Auto-Accept on receiving Follow; both sides' graphs update.
|
||||
- Reject leaves no following relationship.
|
||||
- Undo{Follow} removes the following.
|
||||
- Self-follow rejected.
|
||||
- [x] **6a** — `follower_graph.erl` Erlang-fun stand-in for the
|
||||
genesis `follower-graph.sx` projection body. State shape is a
|
||||
property-list keyed by ActorId (maps `#{}` not in substrate),
|
||||
each entry carries `{following, followers, pending_outbound,
|
||||
pending_inbound}` lists. Fold rules:
|
||||
- `Follow{actor: A, object: B}` — A → pending_outbound(B);
|
||||
B → pending_inbound(A).
|
||||
- `Accept{actor: B, object: F=Follow{A→B}}` — A → following(B)
|
||||
on A's bucket; B → followers(A) on B's bucket; pendings cleared.
|
||||
- `Reject{actor: B, object: F}` — pendings cleared, no promote.
|
||||
- `Undo{actor: A, object: F}` — drops A↔B from every list; only
|
||||
F's original actor can Undo (carol can't Undo F{A→B}).
|
||||
Self-follows are no-ops; duplicate Follows are idempotent;
|
||||
Accept/Reject/Undo of non-Follow `:object`s pass through.
|
||||
18 cases in `follower_graph.sh`. The `fold_fn/0` 2-arity fun
|
||||
plugs into `projection:start_link/3` exactly like
|
||||
`define_registry:fold_fn/0` and `actor_state:fold_fn/0`.
|
||||
- [ ] **6b** — Wire follower-graph fold to the inbox handler so a
|
||||
peer Follow lands, fires auto-Accept publish (open-world policy
|
||||
per §13.2; manual moderation deferred to v3). Acceptance test
|
||||
in `follow_lifecycle.sh` covering the end-to-end
|
||||
Follow → inbox → auto-Accept → projection-state-converges flow.
|
||||
|
||||
**Acceptance:** `bash next/tests/follow_lifecycle.sh` passes 14+ cases.
|
||||
|
||||
@@ -808,6 +813,21 @@ proceed.
|
||||
|
||||
Newest first.
|
||||
|
||||
- **2026-06-06** — Step 6a: follower-graph projection
|
||||
(`follower_graph.erl`). Pure-functional fold over Follow /
|
||||
Accept / Reject / Undo activities per design §13.2. State is a
|
||||
proplist keyed by ActorId carrying `{following, followers,
|
||||
pending_outbound, pending_inbound}` lists. Follow pushes onto
|
||||
pendings; Accept moves both sides from pendings into the
|
||||
permanent lists; Reject just clears pendings; Undo drops the
|
||||
pair everywhere (and only the Follow's original actor can Undo).
|
||||
Self-follow is a no-op; duplicate Follow is idempotent;
|
||||
Accept/Reject/Undo of a non-Follow `:object` passes through.
|
||||
`fold_fn/0` is the standard 2-arity fun for
|
||||
`projection:start_link/3` (same shape as `actor_state` and
|
||||
`define_registry`). 18/18 in `follower_graph.sh`. Conformance
|
||||
761/761.
|
||||
|
||||
- **2026-06-06** — Step 5d: POST /actors/<id>/inbox real ingestion.
|
||||
`route/2` now special-cases POST `/actors/<id>/inbox` next to POST
|
||||
`/activity` so the body + full Cfg reach the new
|
||||
|
||||
Reference in New Issue
Block a user