Murmurent: Group-Level Design¶
Repos used by a group¶
These are the same three classes of repository introduced in Overview: members, groups, projects, described here with the internal layout that matters at the group level. Scope is kept separate across them.
- The commons (
hallettmiket/murmurent): the shared agent definitions, rules, hooks, and install tooling. Centre-wide; cloned by everyone. - The lab-management repository (
murmurent_lab_mgmt_<lab>): group-scoped, one per group. Holds the role registry (roles/), inventory (inventory/), audit logs, the project registry, and group-wide protocols. - Project repositories: project-scoped, one per active project. Each
holds
MEMBERS(the project's access list),exp/,src/,findings/, and the rest of the lab's standard project layout, private to its members. A project's primary repository may also carry aCHARTER.md, a short file recording the project's purpose and sensitivity tier; it is a legacy-but-supported marker that predates the current certificate-based project model (see Creating a project).
Agent classification: the freeze flag¶
Agents ship as shared definitions in the commons, but a member may want to customize an agent for their own work. Each agent's frontmatter carries a freeze flag that says whether that is allowed. "Freeze" marks an agent whose definition should not drift from the shared version, for consistency and reproducibility.
frozen: the member cannot make their own version. Used for safety-critical agents whose drift would erode group consistency or compliance, for examplesecurity_guard,conscienceand the millwrights.personal: the member may make their own version (murmurent agent fork, or Modify on the dashboard). Used where individual variation is acceptable, for exampleartistandbookworm.
Three agents are never editable whatever their flag, because they guard the
rest: security_guard, adversary and conscience, plus any agent that
withholds a tool able to send data out.
The flag is checked when a member tries to edit an agent, and frozen agents always run the centre version. Which version runs otherwise is set by five levels (person in one project, project, person, lab, centre): see Choosing an agent's version.
Planned: levels and the cascade¶
Two further rules were designed but are not built:
- Role override: a role could pin an agent to the shared version. For
example,
bookwormispersonal, but abookworm_curatorrole, whose output the whole group relies on, would always run the shared definition. - Automated runs: an agent run by a bot (GitHub Actions or other shared automation) would always use the shared version, because bots write to shared results and those must be reproducible.
Both would sit on top of the levels described in Choosing an agent's version.
Tool preferences (defaults + overrides)¶
Every reference agent ships with sensible defaults so it is useful immediately: the Artist plots with matplotlib and the viridis colormap, the Bookworm formats citations in a default style, and so on. A member who prefers different defaults can override them per member, without editing the shared agent definition. Preferences that several agents care about (the plotting library, the citation style, the prose language) use a shared, controlled vocabulary, so that setting one value applies it across every agent that honours it.
Each agent's frontmatter may declare a defaults block listing the tools, libraries, formats, or styles the agent uses unless told otherwise:
---
name: artist
freeze: personal
defaults:
plotting: matplotlib
figure_size: 8x6
presentation: quarto
colormap: viridis
---
Standardised vocabulary vs free-form¶
Some preferences are cross-cutting (multiple agents care): plotting library, figure size, citation style, prose style. Some are agent-specific (only one agent ever uses them): Zotero collection name, GPU id, patent jurisdictions.
Cross-cutting fields use a controlled vocabulary so a member can set them once and have every relevant agent honour the setting. The vocabulary is documented at murmurent/preferences.md and updated via PR (adding a field requires a brief justification: why does this need to be cross-cutting?).
| Field | Type | Used by |
|---|---|---|
language |
enum (en/fr/...) |
all text-producing agents |
prose_style |
enum (academic/terse/casual) |
all text-producing agents |
audience |
enum (lab/domain-experts/lay) |
artist, conscience, bookworm |
plotting |
str (matplotlib/seaborn/plotnine/altair/...) |
artist, blacksmith |
figure_size |
str (e.g. 8x6) |
artist, blacksmith |
colormap |
str (viridis/cividis/inferno/...) |
artist, blacksmith |
presentation |
enum (quarto/reveal/beamer/keynote) |
artist, occasionally bookworm |
citation_style |
str (nature/vancouver/apa/chicago) |
bookworm, conscience, artist (captions) |
language_runtime |
str (e.g. python-3.12/r-4.4) |
blacksmith |
package_manager |
enum (uv/pip/poetry/conda/renv) |
blacksmith |
audit_verbosity |
enum (terse/standard/verbose) |
adversary |
Agent-specific fields stay free-form; the agent picks its own field names. Examples: bookworm.zotero_collection, lawyer.jurisdictions, blacksmith.gpu_id.
Personal preferences profile¶
A member can set the cross-cutting fields once, applied across all their installed personal agents:
# ~/.claude/murmurent-preferences.yaml
language: en
prose_style: academic
plotting: seaborn
figure_size: 10x8
colormap: viridis
citation_style: nature
audience: domain-experts
The profile is local: never committed to the lab-management repo or any group artefact. It expresses individual preference rather than group policy. The member's preferences travel with them across machines via whatever personal sync they prefer (dotfiles repo, manual copy).
Resolution order¶
When an agent asks "what plotting library should I use?", the answer cascades:
- Conversation override ("use altair this time"): always wins.
- Agent's local
defaults: set on the agent file directly. - Personal preferences profile (
~/.claude/murmurent-preferences.yaml): fills in any standardised field not set in (2). - Registry default (centre):
frozenagents stop here;personalagents only fall through to here for fields not set in (2) or (3).
A member who sets plotting: seaborn once in their profile gets seaborn from every personal agent unprompted. To override for a single agent (e.g. bookworm uses pandoc-style citations regardless), they edit that agent's local copy.
Validation¶
murmurent install checks each installed agent's defaults against the centre vocabulary:
- Standard fields: validate type / enum. Unknown enum values are flagged.
- Possible misspellings: free-form fields with Levenshtein distance < 3 to a standardised field name produce a warning ("did you mean
figure_size?"). - Missing-but-expected: an agent that produces figures without
plottingorfigure_sizeproduces a warning.
All checks warn rather than hard-reject. The controlled vocabulary will lag actual practice; rejection would bottleneck adding new fields. Members fix warnings either by aligning to the vocabulary or by extending the vocabulary via PR to murmurent/preferences.md.
Worked example. A member who prefers seaborn for plots and APA for
citations sets two cross-cutting fields once in their personal preferences
profile: plotting: seaborn and citation_style: apa. From then on, the
Artist produces seaborn figures and the Bookworm formats references in APA
in every session, on every project, with no per-task instruction and no
edit to either agent's definition.
Customizing an agent, and keeping your changes¶
The commons agents are symlinked into ~/.claude/agents/ from your commons
clone, so a git pull on the commons updates them automatically. If you
edit a symlinked agent file directly, you are editing the shared clone, and
a later pull can conflict with or overwrite your change.
To customize an agent and keep the change, fork it:
murmurent agent fork <name>
This replaces the symlink with a personal copy you can edit freely. The copy
is a non-symlink, so scripts/setup.sh preserves it across commons upgrades
rather than overwriting it. The canonical copy lives in your personal vault
under agent_forks/ (so murmurent vault sync carries your forks to your
GitHub and on to your other machines), and is hardlinked into
~/.claude/agents/ so Claude Code loads it with no extra sync step. On
another machine, pull the vault and run murmurent agent relink to load the
forks there too. A pre-existing ~/.murmurent/agent_forks/ (the old,
machine-local home) is migrated into the vault automatically the first time
murmurent agent relink runs; the originals are kept as an on-disk backup.
Because the commons keeps evolving, Murmurent records the commons version you forked from and provides a merge-style drift indicator:
murmurent agent list # each agent: linked vs forked, with drift status
murmurent agent drift # which forks have upstream changes to review
murmurent agent drift reports, per fork, whether the commons version has
changed since you forked (UPSTREAM), whether your copy has local edits
(LOCAL-ONLY), both (DIVERGED, the case to reconcile), or that the commons
no longer ships the agent (ORPHANED). To return to the shared version,
murmurent agent unfork <name> restores the symlink.
Tier separation (recap)¶
- Personal agent: definition copied to the person's
~/.claude/agents/. Per-person memory. Evolution allowed ifpersonal. - Group-registered agent: definition in the group's agent registry. Members install on demand.
- Group-role agent: registered agent + a role assignment held by a specific member. The agent runs in that member's CC environment but its charter and permissions belong to the role.
We are explicitly NOT pursuing shared brains in this iteration.
Privacy and access¶
- Personal: lives in the person's vault and
~/.claude/agent-memory/. Never leaves their machine. - Project-scoped: lives in a private GitHub repo (one per project) with a checked-in
MEMBERSfile as the source of truth. Filesystem ACL on$MURMURENT_DATA_ROOT/append_only/<project>/is synced from MEMBERS. - Group-shared: agent definitions in the Murmurent repo; curated findings in a group oracle; readable to all group members.
- Sensitive artefacts that must travel (cloud, email, off-VM): wrap in
agewith MEMBERS as the recipient list. Re-encrypt on membership change.
Push mechanics: branches, PRs, and bots¶
The push verb covers a wide range of artefacts (notebook entries, code, photos, findings, charter amendments). Forcing PR review on all of them creates intolerable friction for in-progress work; allowing direct push to all of them undermines review where it matters.
Decision: branch protection by path. Different artefacts get different rules. The boundary between "direct push" and "PR required" matches the social contract: nobody objects to a member writing in their own notebook; everyone has a stake in what gets published as a finding.
Default path: personal branch, PR to main¶
Each member works on branches named member/<github-handle>/<topic> in the project repo.
murmurent push <project>→ push the current branch as a personal branch. Direct push, no review.murmurent push <project> --finalize→ open a PR from the personal branch tomain. Bot and human reviews per the path rules.
The CLI inspects the diff and, if any changed path requires PR, refuses direct push and offers to open one instead.
Branch protection rules¶
| Path | Requirement |
|---|---|
CHARTER.md |
PR + PI approval |
MEMBERS |
PR + PI approval |
findings/** |
PR + reviewer (PI or designate) |
src/protocols/** |
PR + reviewer |
src/** (code) |
PR + reviewer |
exp/<n>_<slug>/notebook.md with status: complete |
PR + reviewer |
exp/<n>_<slug>/notebook.md with status: planned\|running |
direct push to personal branch |
exp/<n>_<slug>/{pages,sketches,data}/** |
direct push |
obsolete/** |
direct push |
roles/** (lab-management repo only) |
PR + PI approval |
inventory/** (lab-management repo only) |
PR + lab_manager (or via inventory MCP) |
Bot reviews on PR¶
Each PR triggers GitHub Actions per the paths involved. Bots are meant to always run the shared versions of the agents (the planned automated-run rule above; not built yet).
| Bot | Triggers on | Effect |
|---|---|---|
adversary |
src/**, *.py, *.R, *.ipynb |
Posts a review comment with methodology / leakage / cross-validation concerns |
conscience |
findings/**, CHARTER.md |
Reviews EDID concerns; flags problematic language or framing |
security_guard |
every PR | Scans the diff for restricted file paths or accidental secrets |
bookworm |
findings/**, notebook.md finalisations |
Checks that citations are present and resolvable |
Auto-publish on merge¶
When a PR merges to main, a merge Action runs:
findings/**changes → auto-published to the group oracle (theoracleMCP exposes apublishtool the Action calls).notebook.mdwithstatus: complete→ audit entry written to the project registry; refined-data checksums re-verified.MEMBERSchange → ACL re-sync on the lab VM;agere-encrypt of any encrypted bundles.
Append-only-data updates¶
Bulk data lives under two directories whose names state the guarantee they carry: immutable/ holds read-only source data by convention, and append_only/ holds derived outputs. The legacy names raw/ and refined/, along with the MURMURENT_LAB_VM_ROOT environment variable, remain recognized during a transition. murmurent data migrate renames an existing root to the new scheme.
When an analysis produces new files in $MURMURENT_DATA_ROOT/append_only/<project>/<exp>/, the notebook's append_only_data: and checksums: fields need updating. This is a frequent operation; it should not require PR review.
murmurent push <project> --refined <exp>recomputes checksums for files in$MURMURENT_DATA_ROOT/append_only/<project>/<exp>/, updates the notebook'sappend_only_data:andchecksums:fields, and pushes to the member's personal branch.- The eventual
--finalizePR rolls all those personal-branch updates intomainalong with the status flip tocomplete.
Why path-based, not all-PR or all-direct¶
- All-PR creates friction on every notebook keystroke. Members stop using the tool, or push less, and the artefact-as-message model breaks.
- All-direct removes review where review is the point (charter changes, findings, completed experiments).
- Path-based puts the review boundary at the artefact-type boundary. The rule is legible: "what gets reviewed is what others depend on."
Group-level agents assignable to individuals¶
The PI maintains a role registry. Each role:
- references an agent definition in the group's registry
- has cardinality:
singleton,quota: N, oropen - has zero or more current operators (members assigned to run it)
- has an audit log of operator changes (handoff dates, transferring PI signature)
Initial role examples:
- lab_manager: singleton; typically the admin assistant. Owns inventory, supply orders, scheduling, reagent provisioning.
- sysadmin: singleton or pair; permissions, repos, lab VM, backup integrity.
- bookworm_curator: quota of 2; curates the group's Zotero library on behalf of all members.
- oracle_curator: quota of 2; annual rotation. Facilitates finalisation choreographies (ensures all expected agents have weighed in, statements are well-formed, cross-references are correct) and handles periodic legacy maintenance of the group oracle.
- adversary_chair: rotating; runs adversarial review on group-bound submissions (papers, grants).
- conscience_chair: rotating or singleton; EDID review on outgoing artefacts.
Only the PI may assign or revoke a role. Role transitions are first-class events with an audited handoff.
Role registry file format¶
One markdown file per role, in the group's lab-management repo at roles/<role>.md:
---
role: lab_manager
agent: lab_manager
cardinality: singleton
---
# lab_manager
## Charter
Owns inventory, supply orders, scheduling, reagent provisioning. Authorised
to mutate the inventory database. Authorised to spend up to $X without
explicit PI approval.
## Current operators
- @member_a (since 2026-04-01)
## Audit log
- 2026-04-01: assigned @member_a (PI: @the_pi, signed)
- 2026-01-15: revoked @prev_admin (handoff complete)
- 2026-01-01: assigned @prev_admin (PI: @the_pi, signed)
Role transitions¶
A role transition (assign / revoke / transfer) is a first-class auditable event. Transitions are not lightweight: they touch credentials, permissions, in-flight obligations, and possibly agent memory.
Steps¶
- Initiate: issue filed in the group's lab-management repo, labelled
role-transition. Names: role, current operator (if any), proposed operator, effective date, reason. - Acknowledge: proposed operator comments on the issue accepting the role and confirming they have read the charter.
- Handoff checklist: current operator works through:
- Document the current state of the role's responsibilities (open requests, pending orders, active reviews).
- Rotate credentials where rotation is possible; share securely otherwise (1Password, age envelope).
- Update permissions: filesystem ACLs, GitHub team membership, MCP server credentials, lab VM accounts.
- Transfer or archive the agent's local memory if the role's continuity depends on it.
- Update the role registry file: replace operator(s), append to audit log.
- PI sign-off: PI closes the issue with a signed approval comment (commit-signed where possible).
- Audit entry: appended to
roles/<role>.mdwith previous operator, new operator, effective date, PI signature.
Unilateral transitions¶
If the current operator cannot participate (left without handoff, deceased, terminated), the PI initiates the transition with an explicit unilateral flag. The audit entry records the absence of operator acknowledgement and notes any credentials that had to be rotated rather than transferred.
Quota and cardinality enforcement¶
The CLI's roles assign command refuses to exceed cardinality without an explicit --force flag, which itself appears in the audit log. This makes accidental violations visible.
Onboarding flow¶
Adding a new member to a group is a multi-stage choreography. Goals: low friction for the new user, auditable, revocable, with age keys handled correctly so encrypted artefacts remain accessible.
Stage 1: Invitation¶
The PI files an issue in the lab-management repo titled Onboard @<github-handle>. The issue specifies:
- Which onboarding profile to apply (e.g. student, postdoc, pi-collab, core-staff).
- Which projects to admit the user to (initial MEMBERS).
- Any role assignments at the start.
- Any restrictions (read-only on certain projects, no write to lab-management, etc.).
Reusable profiles live at lab-mgmt-repo/onboarding/<profile>.md. Each profile is a YAML+markdown spec listing: agents-to-install, default projects, default role, default permissions.
Stage 2: Local setup¶
The new user runs:
murmurent onboard <group> --profile student
This:
- Clones the Murmurent repo (default ~/repos/murmurent).
- Installs agents per the profile (planned; see the freeze flag section above).
- Configures MCP servers (inventory, oracle, request board) in ~/.claude/settings.json.
- Generates a personal age key pair; pushes the public key to lab-mgmt-repo/keys/<github-handle>.age.
- Creates lab-mgmt-repo/members/<github-handle>.md with a profile stub (interests, contact, current projects).
- Opens a PR with the key + member profile.
Stage 3: Approval¶
- PI reviews the PR and approves the public key + profile.
- For each project listed in the onboarding issue, PI runs
murmurent project admit <project> @<handle>. Each admit is a normal admit event in the project lifecycle (audit entry, ACL re-sync, re-encryption ofagebundles to include the new recipient). - PI assigns starting roles via
murmurent role assign. Each is a normal role transition.
Stage 4: Confirmation¶
- New user verifies they can pull each project they were admitted to.
- New user runs
murmurent doctorto confirm install integrity. - Onboarding issue is closed.
Required artefacts¶
| Artefact | Location |
|---|---|
| Onboarding issue | lab-management repo issues |
| Onboarding profile | murmurent/onboarding/<profile>.md (centre) or lab-mgmt-repo/onboarding/<profile>.md (lab override) |
| Member profile note | lab-mgmt-repo/members/<github-handle>.md |
| Member age public key | lab-mgmt-repo/keys/<github-handle>.age |
| Audit entries | one per project admit, one per role assignment |
Profile catalog¶
Four profiles ship as centre defaults at murmurent/onboarding/<profile>.md. Each lab can override or extend at lab-mgmt-repo/onboarding/<profile>.md: lab-specific version wins when both exist. New lab-specific profiles (e.g. clinical-fellow for a translational lab) are just additional files.
student¶
Graduate or undergraduate student joining the group under a supervisor.
---
profile: student
description: Graduate or undergraduate student joining the group
---
agents:
- bookworm
- blacksmith
- artist
- adversary
- conscience
- security_guard
default_projects: [] # PI admits explicitly
default_roles: []
permissions: member
lead_eligible: false
expiry: null
lawyer excluded by default: IP concerns rarely matter for students; can be added later via murmurent agent add.
postdoc¶
Independent researcher; eligible to lead projects.
---
profile: postdoc
description: Independent researcher; eligible to lead
---
agents:
- bookworm
- blacksmith
- artist
- adversary
- conscience
- security_guard
- lawyer
default_projects: []
default_roles: [] # rotating roles like adversary_chair assigned later
permissions: member
lead_eligible: true
expiry: null
pi-collab¶
External PI collaborating on a specific project.
---
profile: pi-collab
description: External PI collaborating on a specific project
---
agents:
- bookworm
- adversary
- artist
# blacksmith excluded — collaborators run their own infrastructure
default_projects: [] # the collaboration project admitted explicitly
default_roles: [] # often co-lead on the collaboration project
permissions: scoped # only the projects they are admitted to; no group-wide read
lead_eligible: true
expiry: 12_months_default # renewable
Scoped permissions matter: pi-collab cannot browse other group projects.
visitor¶
Visiting student or sabbatical scientist; short-term, observation-mostly.
---
profile: visitor
description: Short-term visitor (visiting student, sabbatical, etc.)
---
agents:
- bookworm
- artist
default_projects: []
default_roles: []
permissions: read_only # cannot push, cannot complete SEAs, cannot mutate inventory
lead_eligible: false
expiry: 3_months_default # required; renewable
A note on cores and factories¶
Core facilities (sequencing centres, mass-spec, imaging cores) are not modelled as a profile within a regular group. Each core is its own group: its own people, its own specialised agents, its own inventory, its own choreographies. Cross-group collaboration (a sequencing-core member working on another group's project) is handled by admitting them to that project as an external member, exactly as for any other cross-group case. The (deferred) centre-level design will spell out how groups and cores interconnect; nothing in the present group-level design needs a core-staff member type.
Layering¶
Profiles describe the baseline install. Roles, project memberships, and permission elevations are layered separately via the normal flow (PI runs murmurent role assign, murmurent project admit). A student who will act as bookworm_curator gets the student profile and then has the role assigned. This keeps the profile count from exploding combinatorially.
Validation¶
murmurent install and murmurent onboard check the resolved profile:
- Warns if a referenced agent doesn't exist in the registry.
- Warns if a referenced role doesn't exist.
- Warns if expiry is missing for pi-collab or visitor (these are time-bounded by default).
Consistent with the warn-not-reject philosophy used elsewhere.
Offboarding (mirror)¶
Triggered by the PI when a member leaves:
- Each project runs release; each role runs revoke or transfer.
- Member age key is moved from keys/ to keys/archive/: preserved for decrypting historical bundles, not used for new ones.
- Member profile note marked status: alumni with departure date; not deleted (preserves attribution on past work).
- A final summary of contributions is appended to the member profile note.
Squads (work in progress)¶
Work in progress
The squad model (subgroups within a project) is a design that is not yet implemented. This section documents the intended shape; the CLI verbs and registry described here are not available yet.
A squad is the subgroup that actually performs a project, experiment, or SEA. It has a lead and members. Squads can be nested: a project squad has experiment squads inside, which have SEA squads inside.
Group membership ≠ squad membership. Not every group member is in every squad. The PI is default-present in every squad but can opt out (--no-pi) when their day-to-day involvement isn't useful.
Authority cascade¶
| Authority | Can do |
|---|---|
| PI | Project birth/end, charter amendments, role assignments, project-lead transfers |
| Project lead | Form/dissolve sub-squads, transfer experiment/SEA leads, admit/release at project level |
| Sub-squad lead (experiment, SEA) | Invite/release members within their scope |
| Member | Claim, complete, decline SEAs assigned to them |
Project-lead is assigned by the PI. Sub-squad leads are assigned by the project lead.
Squad verbs¶
| Verb | What it does | Authority |
|---|---|---|
form |
Create a squad with a lead, initial members, and a scope (project / experiment / SEA) | PI for project-level; project lead for experiment/SEA-level |
invite |
Propose adding a member; member accepts | Lead |
release |
Remove a member from the squad | Lead |
transfer_lead |
Handoff lead to another member | PI for project-level; project lead for sub-squads |
dissolve |
End the squad (project end, experiment closure, or SEA done) | Lead, with PI sign-off for project-level |
promote |
Upgrade a sub-squad's scope (e.g. experiment → project when work grows) | PI |
SEA verbs¶
| Verb | What it does |
|---|---|
request |
File an SEA request on the group request board, naming target squad or member |
claim |
Declare you'll perform an offered SEA |
complete |
Mark an SEA complete with a delivery artefact |
decline |
Refuse an SEA, with reason |
Squad registry¶
Each squad has a markdown file in the lab-management repo at squads/<scope>/<name>.md:
---
squad: brca_imaging
scope: project
lead: '@member_a'
members: ['@the_pi', '@member_b']
pi_present: true
status: active
parent: null
---
# Squad: brca_imaging
## Charter
[link to project charter]
## Sub-squads
- exp/3_titration (lead: @member_b)
## Audit log
- 2026-04-01: formed (PI: @the_pi, lead: @member_a)
- 2026-04-15: invited @member_b
Choreographies¶
Choreographies (recurring multi-actor workflow recipes, including the finalisation choreography) are documented on their own page: Choreographies. This is an area of active development.
Dashboards¶
Each member has a dashboard. The PI gets an enhanced version of it. Two implementations sharing the same underlying data.
Markdown snapshot¶
lab-mgmt-repo/dashboards/<github-handle>.md: regenerated nightly by a GitHub Action and on relevant events (PR merge, role assignment, squad change). Grep-able, version-controlled, readable in Obsidian, consumable by CC. This is the canonical source of truth for what's in a member's view.
Live local view¶
murmurent dashboard --hifi opens a local FastAPI web application that reads the snapshot plus live MCP queries (inventory, oracle, request board) plus local git state. Member-level by default; PI sees additional sections automatically (based on identity).
Member dashboard contents¶
- Identity: handle, email, group, status (active, on leave, alumni).
- Agents: name, effective freeze flag, install type (symlink vs copy), last sync, drift status for personal copies.
- Squads: project / experiment / SEA scope, role (lead vs member), status.
- SEAs:
- Outgoing: requests you've made of others, with status.
- Incoming: assigned to you, with status and deadlines.
- Outstanding analysis: SEAs / experiments / projects where you are a squad member or lead and
analysis_status != concluded. Sorted by age sincecomplete. Visual escalation: subtle yellow at >2 weeks unexamined; red and escalated to squad lead + PI dashboards at >2 months. The panel header reads "what does each result mean?" (pedagogical purpose stated explicitly). - Security and compliance: per-project sensitivity badge (
standard/restricted/clinical), the controls required for that tier, and your compliance status (TCPS 2 certified ✓, TOTP enrolled ✓, signing key registered ✓, etc.). Missing required controls render in red with a one-click action to resolve. Includes an Elected upgrades subsection where you toggle stricter-than-required controls (always-on 2FA, always age-encrypted off-VM transfers, etc.). Required and elected stay distinct: you can layer stricter controls but cannot opt out of required ones. See Sensitivity tiers. - Quick MCP queries: inventory search bar (low / expiring shortcuts), oracle latest, request board.
- Recent activity: your commits, your PRs, oracle publishes touching projects you're in.
- Storage / compute: your workspace size, your share of
$MURMURENT_DATA_ROOT/append_only/, GitHub Action minutes consumed. - Notifications: PR reviews waiting on you, SEAs assigned, role transitions to ack, oracle digests.
- Quick actions: new experiment, push, request SEA, open a project repo.
PI dashboard adds¶
- Group overview: members (active / alumni), projects (active / paused / archived), squads, role holders.
- Audit: recent admits / releases, role transitions, charter amendments, sensitive PR merges, inventory orders.
- Accounting: budget vs spend, outstanding orders.
- Compute: lab VM CPU/GPU per member or per project, storage usage by project, GitHub Action minutes.
- Onboarding queue: open onboarding issues, pending key/profile PRs.
- Oracle: pending publishes, recent findings, oracle health (size, last curation).
- Risk: stale roles, frozen agents needing update, encrypted bundles needing re-encryption, unmerged personal branches > 30 days old.
- Compliance grid: across all
clinicalprojects in the group, a member-by-control matrix. Members with expiring or expired certifications, missing 2FA enrollments, or pending REB renewals surface here. Drives onboarding-style "fix this" actions on individual member dashboards.
Implementation notes¶
- The Streamlit app reads the markdown snapshot for any data it can find there, falling back to live MCP queries for things that need to be fresh (current inventory state, oracle latest).
- A
dashboardMCP exposes tools for the snapshot generator (list_squads_for_member,recent_activity,compute_usage) so other agents can query the same data without duplicating logic. - The snapshot generation script is a GitHub Action that the lab-management repo runs on a cron (nightly at 04:00 local) plus event triggers.
Sensitivity tiers and project-level controls¶
Each project declares a sensitivity tier in its CHARTER.md frontmatter. The tier is project-level: projects in the same group can be at different tiers. Tiers govern which controls apply to whom.
Tier definitions¶
| Tier | Examples | Controls layered on standard |
|---|---|---|
standard |
Most basic-research projects | (baseline: signed commits, branch protection, MEMBERS-scoped repos, age for off-VM transfer) |
restricted |
Unpublished collaborator data, IP-sensitive work | + per-project secret keys; off-VM artefacts must be age-encrypted; 4-hour MCP token TTL |
clinical |
PHI / human-subject regulated data | + REB number in CHARTER, TCPS 2 (CORE-2022) certification per member with annual renewal, no data in the repo (only methods/code), per-project encrypted FS volume, 2FA SSH, 15-minute MCP token TTL with TOTP refresh, PHI pattern detection in hooks, BAA-tier API or de-identification mandatory for any LLM-mediated step |
Policy file¶
The control-per-tier matrix is single-sourced at murmurent/sensitivity-policy.yaml (centre default), with lab override at lab-mgmt-repo/sensitivity-policy.yaml. Adding a new tier or amending controls is a PR.
# murmurent/sensitivity-policy.yaml (excerpt)
tiers:
standard:
mcp_token_ttl: 8h
data_in_repo: allowed
ssh_2fa: false
restricted:
mcp_token_ttl: 4h
off_vm_must_be_age_encrypted: true
ssh_2fa: false
clinical:
mcp_token_ttl: 15m
mcp_token_2fa: true
data_in_repo: forbidden
fs_encryption: per_project_volume
ssh_2fa: true
member_certifications_required: [tcps2_core_2022]
api_constraint: baa_or_deidentified
CHARTER frontmatter¶
A clinical charter must declare additional fields:
---
project: brca_clinical_imaging
lead: '@member_a'
sensitivity: clinical
reb_number: WREM-2026-0142
reb_expires: 2027-08-01
data_residency: ca
---
murmurent project new validates these; CI re-validates on every charter change.
Sensitive-project-mode controls in detail¶
For sensitivity: clinical:
- No data in repo: notebook frontmatter's
immutable_data:andappend_only_data:paths point only to lab VM locations. Checksums also stay on the VM, not in the repo. A pre-commit hook refuses commits that introduce data files into a clinical repo. - Per-project encrypted volume on lab VM. Mounted only while the project is active; unmounted on session end.
- PHI pattern detection extends the secret-leak hook with patterns: OHIP (10-digit + 2-letter version code), MRN-style identifiers, SIN, DOB-near-name proximity. Outbound calls (WebFetch, mcp__slack, Anthropic API) refuse to send any prompt or argument matching these patterns.
- Member certifications: each clinical-project member holds current TCPS 2 (CORE-2022) certification, recorded in
lab-mgmt-repo/members/<handle>.mdwith expiry date. Auto-revocation on expiry. - REB-bounded access: when the REB approval expires or amends, member access auto-pauses pending re-approval.
murmurent breach <project> --description <text>: opens an incident in the lab-management repo, notifies PI immediately, starts the PHIPA 24-hour clock, drafts the IPC notification.
Honest flag: LLM API and PHI¶
Sending any PHI-containing prompt to the Claude API itself violates PHIPA unless on Anthropic Enterprise with a BAA-equivalent and verified Canadian residency. Three options, usually combined:
- De-identify aggressively before any prompt; hooks enforce by refusing prompts matching PHI patterns.
- Anthropic Enterprise tier with BAA / equivalent, with verified residency.
- Keep data-touching analyses outside CC: CC handles methods, code, deliberations, and writing; the actual PHI-touching analysis runs locally on the lab VM without LLM mediation.
This is a constraint of using any LLM tool on regulated data rather than a Murmurent limitation.
Dashboard reflection: required vs elected¶
Each member's dashboard adds a Security and compliance panel:
- Per-project block: sensitivity badge, the controls required for that tier, the member's compliance status (TCPS 2 certified ✓, TOTP enrolled ✓, signing key registered ✓, etc.). Missing required controls show in red.
- Outstanding required: aggregated list of any required control the member doesn't currently satisfy. Clicking takes them to the action that resolves it.
- Elected upgrades: members can opt into stricter controls than required (e.g. always-on 2FA, always age-encrypt off-VM artefacts even for standard projects). Toggles on the dashboard; persisted in the personal preferences profile.
- PI view: across-all-clinical-projects compliance grid; flags members whose certifications are expiring or expired.
The required-vs-elected distinction is enforced: a member cannot opt out of a control their project requires, but can layer stricter controls on top.
CLI¶
| Command | Effect | Authority |
|---|---|---|
murmurent project sensitivity <project> [--set <tier>] |
Read or change a project's sensitivity tier | PI (raising tier always allowed; lowering requires PR + audit) |
murmurent compliance status [--project <p>] |
Show your compliance state | Member |
murmurent compliance certify <cert-name> --expires <date> |
Record a certification (e.g. TCPS 2) | Member; verified by PI |
murmurent audit verify <repo> |
Walk the audit chain, verify signatures and hash chain | Anyone with repo access |
murmurent secret rotate <scope> <name> |
Rotate and re-encrypt a secret | Scope-appropriate authority |
murmurent breach <project> --description <text> |
Open a breach incident; start PHIPA clock | Any project member |
Glossary¶
- Project: highest unit of work in the group. Has a single lead, a charter, and one or more repos. Multi-person, multi-experiment, accumulates semi-structured data. (Replaces the earlier term "investigation".)
- Repo: a lab-convention repository (
<name>/in your repositories folder, which is~/reposunless you chose another, withexp/<n>_<slug>/inside). One or more per project, optionally grouped in one project folder. - Experiment: a unit of work within a project repo. Lives in
exp/<integer>_<good_name>/(per the lab's project-structure convention); date is in the notebook frontmatter. Has its ownnotebook.mdentry. - SEA: Skill, Experiment, or Analysis. Atomic, callable unit of service exchanged between people, groups, factories. Multiple per experiment; sometimes free-standing.
- SEA verbs:
request,claim,complete,decline. - Squad: subgroup that performs a project, experiment, or SEA. Has a lead and members. Nestable.
- Lead: the member responsible for a squad's progress. Distinct from PI.
- Notebook entry: the
notebook.mdinside an experiment folder; markdown with required frontmatter and embedded media. - Charter: one-paragraph statement of purpose and scope, committed as
CHARTER.mdin a project repo. - MEMBERS: file in a project repo enumerating participants with access.
- Verb: an action a member performs that touches group artefacts.
- Role: a group-level agent assignment held by a specific member, with cardinality and audit.
- Operator: the member currently running a role's agent.
- Codev: code development via git / GitHub PR review.
- Frozen agent: must run from the group-registry version (no per-member drift). Default for safety-critical agents; forced for bots.
- Personal agent: members may copy and modify locally. Default for stylistic agents.
- Freeze cascade: the resolution order for an agent's effective freeze flag: bot pin → role override → registry default.
- Lab-management repo: the group-scoped repo that holds roles, inventory, audit logs, dashboards, squad registry, and the project registry. Distinct from per-project repos.
- Choreography: a recurring multi-actor pattern with a documented recipe. Adopted by a project via
choreography:in its CHARTER frontmatter. - Finalisation choreography: multi-scope deliberation ritual (SEA / experiment / project):
examine→conclude. Always produces a deliberation document; optionally a finding. - Deliberation document: markdown artefact produced by the finalisation choreography. Contains agent contributions, member reflections, group-oracle context, an attempted statement (which may or may not be a clean consensus), caveats, dissent, and approval log.
- examine / conclude: the two analysis-track stages following operational
complete.analysis_status: not_started → examined → concluded. - Operational status vs analysis status: two parallel state tracks on every SEA / experiment / project. Operational tracks the work; analysis tracks the deliberation.
- Dashboard: markdown snapshot + Streamlit app showing each member's agents, squads, SEAs, outstanding analysis, and (for PIs) group-wide audit/accounting/compute.
- Discussion: persistent record of a meeting / Slack thread / async deliberation, filed at
discussions/<date>_<topic>.mdwith outcome (decided / open / blocked / tabled). - Protocol: codified procedure for a wet or dry experiment. Project- / group- / centre-scoped.
- Skill: a Claude Code-discoverable instruction set, invoked by name. Group- or centre-scoped.
- Freeze: immutable snapshot of a project at a point in time: git tag + manifest + age-encrypted bundle. Used for paper / thesis / grant submission moments.
- oracle_curator: group-level role responsible for facilitating finalisation choreographies and handling periodic legacy maintenance (cross-reference health, citation rot, tag drift). Quotaed to 2; annual rotation.
- Standardised vocabulary: controlled list of cross-cutting
defaultsfield names documented atmurmurent/preferences.md; groups extend viamurmurent/<group>/preferences.md. Used for fields multiple agents share (plotting, citation_style, prose_style, etc.). - Personal preferences profile:
~/.claude/murmurent-preferences.yaml, local to each member's machine, sets standardised fields once for allpersonalagents. Never committed to group repos. - Onboarding profile: YAML+markdown spec at
murmurent/onboarding/<profile>.md(centre default) orlab-mgmt-repo/onboarding/<profile>.md(lab override) bundling agents-to-install, default permissions, lead eligibility, and expiry for a class of new member. Four centre defaults:student,postdoc,pi-collab,visitor. Cores are themselves groups, not profiles within a group. - Sensitivity tier: project-level property declared in
CHARTER.mdfrontmatter:standard/restricted/clinical. Controls per tier defined inmurmurent/sensitivity-policy.yaml. Projects in the same group can be at different tiers. - PHIPA / TCPS 2 / REB: Personal Health Information Protection Act (Ontario), Tri-Council Policy Statement on research ethics, Research Ethics Board. Together they govern
clinicalprojects. - Required vs elected controls: required controls come from a project's sensitivity tier; elected controls are stricter-than-required preferences a member opts into. Members cannot opt out of required.