fed-sx-m2: Step 2b — actor_state projection fold + 19 tests
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 28s

next/kernel/actor_state.erl mirrors define_registry's structure: a
2-arity fold_fn that plugs into projection:start_link/3, an
Erlang-fun stand-in for the genesis actor-state.sx projection body.

State shape:
  [{ActorId, Profile}, ...]

Profile is a property list with :type, :name, :preferredUsername,
:summary, :icon, :public_keys, :moved_to, :created. Maps #{} aren't
registered in this substrate, so this matches the kernel bucket /
registry shape convention.

Folding rules per design §9.1-§9.4:
  - Create{Person|Service|Group}: register profile, capturing object
    fields + :published seq as :created. Duplicate Create no-overwrite.
  - Update{Person|Service|Group, patch}: deep-merge :patch into
    profile last-write-wins per key.
  - Move: record :moved_to.
Other activity types and non-actor object Creates pass through.

Local find_keyed/has_keyed/set_keyed helpers (same gap as Step 1a:
no lists:keyfind/keymember in this substrate).

19/19 in next/tests/actor_state_pure.sh covering:
  - new/0/has/2/lookup/2/actors/1 base cases
  - Create for Person/Service/Group all three actor types
  - Profile field capture (name, preferredUsername, public_keys, created)
  - Duplicate Create no-overwrite
  - Two independent actors
  - Update field merge + per-key last-write-wins
  - Update for unknown actor pass-through
  - Move :moved_to
  - Non-actor Creates pass through
  - Activities without :actor pass through
  - fold_fn/0 returns is_function(F, 2)

Conformance 761/761. Step-2-adjacent no-regression gate 106/106
across 6 suites (define_registry_pure, projection_pure,
projection_server, nx_kernel_multi, bootstrap_start, smoke_app_pure).
This commit is contained in:
2026-06-06 11:53:14 +00:00
parent 0c44a10c8f
commit bcfbd9a528
3 changed files with 372 additions and 10 deletions

View File

@@ -169,16 +169,25 @@ publicKey rotation history, profile fields, follower counts, etc.
`bootstrap_load.sh` 15/15, `bootstrap_populate.sh` 14/14,
`bootstrap_start.sh` 10/10). `bootstrap_build.sh` 12/12 picks
up the new bundle CID dynamically.
- [ ] **2b** — Actor-state projection fold (Erlang-fun stand-in,
mirrors Step 5d-pure's `define_registry`):
- On `Create{Person|Service|Group}`: register the actor's profile
in `{ActorId => #{type, name, preferredUsername, summary, icon,
public_keys, created}}`.
- On `Update{Person|Service|Group, patch}`: deep-merge the patch.
- On `Move`: record `:movedTo` pointer.
- `next/kernel/actor_state.erl` with `fold_fn/0` plugging into
`projection:start_link/3`. Pure-functional + gen_server-bridged
tests as a single `actor_state_pure.sh`.
- [x] **2b** — Actor-state projection fold (Erlang-fun stand-in,
mirrors Step 5d-pure's `define_registry`). `next/kernel/actor_state.erl`
with state shape `[{ActorId, Profile}, ...]` where `Profile` is a
proplist with `:type / :name / :preferredUsername / :summary /
:icon / :public_keys / :moved_to / :created`. Maps `#{}` aren't
registered in the substrate, so the profile is a property list
(same shape choice as the kernel's bucket / registry state).
Folding rules:
- `Create{Person|Service|Group}` (from a known `:actor`):
captures profile fields + `:created` (=`:published` seq).
Duplicate Creates are no-overwrite.
- `Update{Person|Service|Group, patch}`: merges `:patch` into the
profile last-write-wins per key.
- `Move`: records `:moved_to` on the profile.
Other activity types and non-actor object Creates pass through.
`fold_fn/0` plugs into `projection:start_link/3`. Local
`find_keyed/has_keyed/set_keyed` helpers (same gap as 1a — no
`lists:keyfind`/`keymember` in the substrate). 19 cases in
`actor_state_pure.sh`.
- [ ] **2c** — `nx_kernel:bootstrap_actor/4(ActorId, Profile,
KeySpec, State)` — publishes `Create{Person{...}}` as the actor's
first activity, exercising the full pipeline. Integration test
@@ -691,6 +700,18 @@ proceed.
Newest first.
- **2026-06-06** — Step 2b: actor-state projection Erlang module.
New `next/kernel/actor_state.erl` with `fold/2` over Create / Update
/ Move activities. Profile is a property list of `:type / :name /
:preferredUsername / :summary / :icon / :public_keys / :moved_to /
:created`. Create captures fields and `:published` as `:created`;
duplicate Create is no-overwrite; non-actor Creates and `:actor`-
less envelopes pass through. Update last-write-wins per patch key.
Move records `:moved_to`. `fold_fn/0` is a 2-arity Erlang fun for
`projection:start_link/3` (structural twin of `define_registry`).
`next/tests/actor_state_pure.sh` 19/19. Conformance 761/761.
Step-2-adjacent no-regression gate 106/106 across 6 suites.
- **2026-06-06** — Step 2a: genesis Person/Service/Group object-
types. Three new SX files in `next/genesis/object-types/` with
the same shape as `note.sx` / `sx-artifact.sx` (`:name`, `:doc`,