DUNIN7 · LOOMWORKS · RECORD
record.dunin7.com
Status Current
Path change-requests/cr-2026-226-w7-system-actor-kind-v0_1.md

CR-2026-226 — W7: the system actor kind — v0_1

Version. 0.1 Date. 2026-08-20 Status. Open. First CR of the H0 lane (Boundaries handoff Step 4; work order loomworks-cr-brief-h0-native-habitat-v0_1.md §2 W7). Scope is W7 only. Runtime direction: no conflict — this CR implements O3 (land the system actor kind, migrate the masqueraders) and honors O1 by keeping every migrated write at its named chokepoint (per ruling R1: the sin was the costume, not the attendance). Rulings shaping this CR. R1 (loomworks-handoff-step-4-v0_1.md): the human-authority gates gain an explicit, named allowance for enumerated sanctioned system writers wearing kind="system"; writes are NOT restructured away from the gates; the enumerated writer list is written as data, not scattered conditionals, so it can one day be a governed object — the first boundary-shaped control (an allowlist) on machine actors. Findings cited. Investigation findings v0_2: B6/Surprise 10 (masqueraders — FORM_SUBMITTER_ACTOR wears contributor explicitly to pass commit_assertion; _BOOTSTRAP_ACTOR same precedent; root cause: no system kind in ActorKind), Surprise 9 (plural chokepoints — the gates this CR touches are the commit-path family, distinct from the spend gate).

Scope

  1. ActorKind gains "system" (src/loomworks/memory/base.py:22). system is a machine kind: it joins NON_HUMAN_ACTOR_KINDS, so the spend tier and every existing machine-actor rejection treat it as non-human by default.
  2. The sanctioned-writer allowlist, as data. SANCTIONED_SYSTEM_WRITERS: frozenset[UUID] beside NON_HUMAN_ACTOR_KINDS in memory/base.py, initially {…0002 (bootstrap), …0050 (form submitter)}, plus a single predicate has_human_commit_authority(actor) (human kinds → True; system + allowlisted id → True; all else → False). The four data-layer human-authority gates (engagement/assertions.py:284/:515/:601, engagement/creation.py:905/:959) and the HTTP dependency gate (api/deps.py:1250) consult the predicate instead of the bare frozenset test — one definition, no scattered conditionals.
  3. Migrate the masqueraders. FORM_SUBMITTER_ACTOR (credit/form_submitter.py) and _BOOTSTRAP_ACTOR (engagement/bootstrap.py) become kind="system"; their docstrings stop justifying the costume and start citing the allowlist. No new masqueraders (O3).
  4. DB migration 0112 widening ck_memory_events_actor_kind_values to admit 'system' (precedent: migration 0057 widened the same CHECK for 'companion'), plus the ORM CheckConstraint alignment in memory/events.py.
  5. Tests. System+allowlisted passes commit/discard/retract/creation gates; system non-allowlisted is rejected; agent/companion remain rejected; existing bootstrap/form-submission flows green; suite at zero failures.

Out of scope

Sessions, grantability, presence (W1/W2/W4 — their own CRs). No change to DecidedBySchema or boundary-record deciders (system is not a decider today). No new writers on the allowlist beyond the two named. LEGACY_UNRESOLVED_ACTOR_ID untouched (findings' business, not this CR's).

Acceptance