The Companion writes memory the system then cannot read back. The write side accepts an actor kind of companion; one read serializer does not, so any assertion the Companion authored crashes the Memory room with a 422. That is why E0060's Memory room shows "Couldn't load this room" — all five of its assertions are Companion-authored.
This CR makes read match write, introduced once as a shared ActorKind alias so the two sides cannot drift apart again. The same inspection found a security-relevant hole the display fix would otherwise make more reachable: the commit and discard gates block non-humans by checking kind == "agent", but a companion kind is not "agent", so it slips through. The seed commits Loomworks to Operator-authority — the machine surfaces and signals; the Operator approves. This CR closes the gate so a companion actor cannot commit or discard, making that principle structural.
Two changes, one contract: companion is a first-class actor kind we display honestly, and precisely because it is a distinct kind, we enforce that it is a non-committing one.
companion enforces this in code. Aligned — strengthens the commitment.companion is a legitimate contributor kind the write path already records; the read path must represent it faithfully. Aligned — repairs a read path that contradicted the write path.ActorKind alias — a single Literal["contributor","agent","person","companion"] referenced everywhere the driver-kind is constrained, so the set cannot drift across duplicated inline declarations again.companion to the read-side serializers (the live crash plus siblings of the same bug class):
ActorRefResponse.kind (schemas.py:2525) — the live 422/500.DecidedBySchema.kind (schemas.py:3756)DecidedBySchema.actor_kind (schemas.py:3788)BoundaryRecordResponse.raised_by_actor_kind (schemas.py:3864)ActorRefSchema.kind (schemas.py:4292) — also nested in responsescreate_all cannot silently re-narrow the DB.== "agent"" to "block any non-human kind," explicitly excluding companion, mirroring credit/authorization.py:27 (_AGENT_KINDS = {"agent","companion"}):
compositions.py:73 — silently relabels a companion-triggered composition as contributor. Display-correctness on a path neither probe exercises. Note as residue.Before any change, CC reads each listed site at current HEAD and confirms the line still matches this CR's quoted state (d32c99b may have advanced). Two-pass discipline: inspect-and-report first, edit after confirmation. If any site differs, halt and report the divergence rather than editing blind. Specifically confirm:
credit/authorization.py:27 still defines _AGENT_KINDS = {"agent","companion"} — the canonical pattern the gate fix mirrors. If changed, re-derive the gate-fix shape.== "agent" check.Stage by explicit path. Per-step commits. Push is a separate, explicitly-authorized instruction — do not push until the Operator says so.
Introduce ActorKind = Literal["contributor","agent","person","companion"] in a shared location (co-locate with domain ActorRef in memory/base.py, or a types module importable without cycle — CC's judgment, report the chosen home). Reference it at ActorRef.kind so the alias is anchored to write authority. Commit. Suite green.
Repoint the five read-side Literals to include companion (via the alias where the field is the full driver-kind set; DecidedBySchema.kind also carries rule — preserve that, add companion to the actor portion). Sites: schemas.py 2525, 3756, 3788, 3864, 4292. Commit. Suite green.
Update memory/events.py:79 CheckConstraint to the 4-value set matching live migration 0057. No new migration (DB already correct); this aligns the ORM so a regen cannot re-narrow. Commit. Suite green.
At assertions.py:204, :491; creation.py:748, :802; deps.py:1044 — change the non-human rejection from kind == "agent" to reject any kind in the non-human set ({"agent","companion"}, ideally importing or mirroring _AGENT_KINDS so commit-gate and spend-tier share one definition of "non-human actor"). A companion actor must be rejected with the same error a human-only gate gives an agent. Commit. Suite green.
ActorRefResponse (and the four siblings) without raising — regression lock on the primary bug.companion actor — enforcement lock proving the gate hole is closed.Commit. Suite green.
HALT. Report terminal SHA and suite count. Await explicit push authorization.
After merge, restart the dev server (serializer + gate logic load in-process), then verify against both probes.
kind: companion shown honestly./assertion-groups and /contributors/me for E0060 no longer 500.companion-authored held assertion — the shape that crashed E0060 from clean).companion-attributed actor cannot commit or discard through the gate — attempt the commit path with a companion ref and confirm rejection.Pass condition: E0060 loads and renders; E0089 displays a freshly-added Companion note; companion ref is rejected at the commit/discard gate. Only then does Room 1 advance to Finding 2.
actor_from_person before the gate), so closing it should not change any working path — but it removes reliance on that indirection. Step-5's enforcement test confirms no legitimate human-commit path is caught by the wider rejection.derived_by) are repaired by Step 2 even though the probes don't exercise them — cheap forward-protection, same edit.