Skip to content

Give 2-BM two human principals, with their names never in the repo - #746

Merged
xmap merged 1 commit into
mainfrom
seed-2bm-staff-actors
Aug 29, 2026
Merged

xmap merged 1 commit into
mainfrom
seed-2bm-staff-actors

Conversation

@xmap

@xmap xmap commented Aug 29, 2026

Copy link
Copy Markdown
Owner

Why

Every run at 2-BM is recorded against the seeded RunWitness agent, because CORA observes the EPICS capture rather than issuing it. No human principal exists at the beamline at all, so there is nothing to act as while testing and nothing but an agent in proj_run_actor_involvement.

This seeds the two beamline staff as human Actors under pinned, deployment-stable ids, so they become usable principals now. What they may do is deliberately left for later.

Names never enter the repository

The display names are personal data and this repository is public. The repo carries only opaque seat ids and kind=human. Each name is supplied at deploy time from the host environment and lands solely in the actor_profile PII vault, matching the pattern register_actor already follows: the ActorRegisteredV2 payload has no name in it.

A slot with no configured name fails before any database connection is opened rather than seeding a blank, and the error names the slot and its environment variable, never a value, so it is safe to print, log, or paste.

Two integration tests pin this directly against Postgres rows: test_seeded_event_payload_carries_no_name and test_seeded_name_lands_only_in_the_profile_vault.

A CLI ceremony, not a boot hook

pilot_seed.py's shape, not _agent_seed.py's. The automatic seeds can run on every boot because an absent config value makes them a safe no-op. A fail-loud name requirement cannot: wiring it into the boot path would stop every other deployment (dev, CI, test, any other facility) from booting unless it also configured two 2-BM names it has no reason to know.

Run it with python -m cora.api.beamline_staff_seed. Idempotent; --dry-run supported.

Identity range

02900000-0000-7000-9000-..., distinct from the seeded-agent range 01900000-0000-7000-8000-... in both the top segment and the fourth-group nibble, so a UUID alone tells you whether a principal is a human seat or an agent. Verified collision-free against every literal UUID in the repo, and pinned by a unit test.

No Policy, deliberately

trust_policy_id stays unset, so AllowAllAuthorize remains in force and behavior is unchanged.

A permissive real Policy looked attractive as a way to exercise the authorize path before it can deny anything. It does not survive contact: there is no canonical command registry in src to enumerate from, so permitted_commands would be a hand-copied list that fails closed the first time a new slice ships. That needs an architecture fitness test tying the seeded command set to the discovered wire commands, and belongs in its own change.

docs/deployments/2-bm/governance.md now records what is actually true, replacing three placeholder operators that no code ever created. It also notes that TrustAuthorize loads exactly one Policy per deployment (Settings.trust_policy_id is a single optional UUID), so the two-Policy split the page used to describe is not a shape Settings can express.

Verification

  • architecture + unit: 34,358 passed
  • Postgres integration: 8 passed
  • pyright, ruff, tach, full pre-commit and pre-push: clean

One note on the architecture tier. It iterates tracked_python_files(), so it silently skipped all three new files until they were staged. Staged first, it caught a real violation (a public *Error class outside the aggregate kernel), now fixed by making the class module-private per the test's documented exemption for tool-local validation helpers.

Known behavior worth flagging

Re-running with a corrected name changes nothing: the ceremony returns early when the actor already exists, so fixing a typo in a display name currently needs a manual vault edit. Consistent with "idempotent, re-runs change nothing", but not obvious.

🤖 Generated with Claude Code

Every run at 2-BM is recorded against the seeded RunWitness agent, because
CORA observes the EPICS capture rather than issuing it. No human principal
exists at the beamline at all, so there is nothing to act as while testing
and nothing but an agent in proj_run_actor_involvement. Seed the two staff
as human Actors under pinned, deployment-stable ids so they become usable
principals now, and leave the shape of what they may do for later.

The display names are personal data and this repository is public, so the
repo carries only opaque seat ids and kind=human. Each name is supplied at
deploy time from the host environment and lands solely in the actor_profile
PII vault, matching the pattern register_actor already follows: the
ActorRegisteredV2 payload has no name in it. A slot with no configured name
fails before any database connection rather than seeding a blank, and the
error names the slot and its environment variable, never a value, so it is
safe to print or paste.

This is a CLI ceremony in pilot_seed.py's shape, not a boot-lifespan hook
like the agent and enclosure seeds. Those can run on every boot because an
absent config value makes them a safe no-op; a fail-loud name requirement
cannot, since it would stop every other deployment from booting unless it
also configured two 2-BM names it has no reason to know.

No Trust Policy is seeded and trust_policy_id stays unset, so
AllowAllAuthorize remains in force. A permissive policy looked attractive
as a way to exercise the authorize path early, but there is no canonical
command registry to enumerate from, so its permitted_commands would be a
hand-copied list that fails closed the first time a new slice ships. That
needs an architecture fitness test tying the seeded set to the discovered
wire commands, and belongs in its own change. governance.md now records
what is actually true, including that TrustAuthorize loads exactly one
Policy per deployment, so the two-Policy split it used to describe is not a
shape Settings can express.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

Coverage report

Click to see where and how coverage changed

FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  apps/api/src/cora/api
  beamline_staff_seed.py 233-235
Project Total  

This report was generated by python-coverage-comment-action

@xmap
xmap merged commit 64fecde into main Aug 29, 2026
19 checks passed
@xmap
xmap deleted the seed-2bm-staff-actors branch August 29, 2026 21:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant