fed-sx-m2: Step 9a — pure-functional backfill slicing + 20 tests
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 49s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 49s
New next/kernel/backfill.erl owns the §13.3 backfill mode
slicing. Given an outbox log + a mode, returns the activity
list to send to a new follower as backfill.
Public API:
slice/2(Mode, LogState) default Wrap=false
slice/3(Mode, LogState, Wrap) Wrap=true wraps entries
wrap_backfill/1 add {backfilled, true}
parse_mode/1 lift Follow :backfill field
Modes:
none new follower: forward-only content
full entire outbox
{last_n, N} last N activities (FIFO)
{last_t, T, NowFn} entries with :published in
(NowFn()-T .. NowFn()]
{since_cid, Cid} entries after the one with :id = Cid
(consumes the matched entry; returns
every entry after it)
wrap_backfill/1 marks each entry {backfilled, true}. Per §13.3
wrapped bodies preserve :id so the receiver's replay defence
still catches duplicates from the live stream.
parse_mode/1 accepts:
nil / none / full / {last_n, _} / {last_t, _, _} /
{since_cid, _} — pass through or normalize
Proplist with :mode + :limit -> {last_n, N}
Proplist with :mode + :duration -> {last_t, T, fun() -> 0 end}
Proplist with :mode = full -> full
Anything else -> none (open-world default)
Substrate gotchas re-confirmed and worked around:
- lists:nthtail/2 not registered — rolled drop_n/2
- Pattern-alias 'Pat = Var' not supported by this port's
parser — parse_mode/1 clauses use explicit deconstruction
20/20 in next/tests/backfill.sh covering all five modes plus
edge cases (N=0, N>length, T=0 -> empty window, since_cid
hit/miss/unknown), wrap_backfill semantics, parse_mode for
atoms / tuple shapes / proplists / unknown / nil.
Step 9b (outbox listing ?since=Cid&limit=N pagination) and
Step 9c (Follow-Accept-backfill wiring) layer on top.
Conformance preserved at 761/761.
This commit is contained in:
@@ -630,11 +630,37 @@ Per §13.3: A wants B's history when A first follows B. Four modes:
|
||||
|
||||
**Deliverables:**
|
||||
|
||||
- Follow activity may carry `:backfill {:mode :last-N :limit 100}`.
|
||||
- On Accept, B's outbox is GET-paged with appropriate filters.
|
||||
- `GET /actors/<id>/outbox?since=Cid&limit=N` returns a paged response.
|
||||
- Backfill bodies wrap the original activities in `:backfilled true`
|
||||
so projections can decide whether to re-fold or skip.
|
||||
- [x] **9a** — Pure-functional backfill slicing in
|
||||
`next/kernel/backfill.erl`:
|
||||
- `slice/2,3(Mode, LogState[, Wrap])` returns the entry list
|
||||
for a given mode. Wrap=true marks each entry
|
||||
`{backfilled, true}` so receiving projections can decide
|
||||
whether to re-fold or skip (per §13.3, wrapped bodies
|
||||
preserve `:id` so replay defence still catches duplicates).
|
||||
- Modes: `none`, `full`, `{last_n, N}`, `{last_t, T, NowFn}`,
|
||||
`{since_cid, Cid}`. NowFn is a 0-arity fun so tests can
|
||||
fake-time it.
|
||||
- `parse_mode/1` lifts the Follow activity's `:backfill`
|
||||
value (atom or proplist) into the internal mode tuple;
|
||||
unknown shapes degrade to `none` (open-world default).
|
||||
Substrate gotchas re-confirmed:
|
||||
`lists:nthtail/2` not in this port (rolled `drop_n/2`);
|
||||
pattern-alias `Pat = Var` not supported (rewrote
|
||||
`parse_mode/1` clauses with explicit deconstruction).
|
||||
20/20 in `backfill.sh` covering all 5 modes (with edge
|
||||
cases: N=0, N>length, T=0, since_cid hit/miss/unknown),
|
||||
wrap_backfill, parse_mode atoms / tuples / proplists /
|
||||
unknown.
|
||||
- [ ] **9b** — `GET /actors/<id>/outbox?since=Cid&limit=N`
|
||||
pagination route. Extends the Step 4d outbox listing with
|
||||
the `?since=` query param (calls `backfill:since_cid_entries/2`).
|
||||
Acceptance test extends `http_multi_actor.sh`.
|
||||
- [ ] **9c** — Follow → Accept → backfill-delivery wiring.
|
||||
The receiving kernel reads the Follow's `:backfill` field
|
||||
via `parse_mode/1`, slices its outbox, and dispatches each
|
||||
entry to the new follower's delivery_worker queue (Step 8d).
|
||||
Gates on Blockers #2 (httpc) for the actual peer fetch path
|
||||
but the in-process drain works today.
|
||||
|
||||
**Tests:**
|
||||
|
||||
@@ -1010,6 +1036,21 @@ proceed.
|
||||
|
||||
Newest first.
|
||||
|
||||
- **2026-06-07** — Step 9a: pure-functional backfill slicing.
|
||||
`next/kernel/backfill.erl` with `slice/2,3(Mode, LogState
|
||||
[, Wrap])` returning the appropriate activity list. Modes
|
||||
`none / full / {last_n, N} / {last_t, T, NowFn} /
|
||||
{since_cid, Cid}` cover the §13.3 grammar; `wrap_backfill/1`
|
||||
marks each entry `{backfilled, true}` (id preserved so the
|
||||
receiver's replay defence still works). `parse_mode/1` lifts
|
||||
the Follow activity's `:backfill` value (atom or proplist)
|
||||
into the internal mode tuple; unknown shapes -> none. 20/20
|
||||
in `backfill.sh`. Substrate gotchas re-confirmed:
|
||||
`lists:nthtail/2` not registered (rolled `drop_n/2`); pattern-
|
||||
alias `Pat = Var` not supported in this port (rewrote
|
||||
`parse_mode/1` clauses with explicit deconstruction).
|
||||
Conformance preserved at 761/761.
|
||||
|
||||
- **2026-06-07** — Step 11b: projection folds for the new verbs.
|
||||
Two new modules in `next/kernel/`:
|
||||
`announce_state.erl` (per-Cid announcer-set fold, set
|
||||
|
||||
Reference in New Issue
Block a user