cd /news/ai-agents/phd-fleet-manage-a-virtual-research-… · home topics ai-agents article
[ARTICLE · art-34195] src=github.com ↗ pub= topic=ai-agents verified=true sentiment=· neutral

PhD_fleet – Manage a virtual research lab of AI PhD students via Slack

A new open-source Python toolkit called PhD_fleet allows researchers to manage a virtual lab of AI PhD students via Slack, using Claude Code agents for research tasks and a coach agent for mentoring feedback. The tool runs on any Python-capable host and connects to Slack via Socket Mode, enabling private subnet operation without inbound HTTP.

read10 min views1 publishedJun 19, 2026
PhD_fleet – Manage a virtual research lab of AI PhD students via Slack
Image: source

A small Python toolkit that lets one researcher — the advisor — spawn and converse with a fleet of Claude Code agents through Slack. Each agent is its own Claude Code session in its own workspace directory; Slack messages drive turns; between turns, the agent's filesystem is its long-term memory.

A separate coach agent watches how the advisor advises and gives evidence-based feedback on mentoring craft. The two intertwined goals: get real research done and grow as a mentor.

The host can be anything that runs Python and the Claude Code CLI — a laptop, a lab workstation, a cloud VM, an HPC login node. The bot connects to Slack over Socket Mode, so the host needs no inbound HTTP and works behind a NAT or in a private subnet.

What you getRequirementsQuick startSetup in detailUsageHow it worksConfigurationSecurity modelLimitationsProject layoutLicense

One Slack channel per agent.#student-<name>

for each student you spawn;#mentor-coach

for the coach (created automatically at first startup).Three slash commands./new-student <name> <briefing>

scaffolds a workspace, creates the channel, and kicks off the first turn./coach-review <name> [days]

asks the coach to reviewyourmentoring of a specific student./claude-status

prints a quick local readout — turns, last context size, cumulative tokens, model, cost, and GitHub link per agent. No Claude calls; just a registry view.

Per-agent journal.JOURNAL.md

in each agent's workspace is append-only, one section per turn, ending with aDid / Found / Next

block.Shared paper library.library/

at the project root is a single pool every agent reads from and contributes to. First reader of a paper writes the canonical summary; later readers add a separate notes file.library/README.md

is a regenerated index — agents only read it.Per-turn commits to per-agent GitHub branches (optional). If you configure anorigin

, after each turn the bot stages that agent's workspace into a fresh commit onagent/<name>

and force-pushes it with a lease. The Slack final message links to the branch for review. With noorigin

, this step is skipped silently.Quiet by default. During a turn the bot posts at most one short*"started"*message and the agent's final reply — no per-tool stream of decorative messages. Failures and timeouts post a:warning:

line.

**Python 3.11 or newer.**The Claude Code CLI on The bot shells out to it, soPATH

.claude --version

must work in the same shell where you run the bot.A Claude.ai subscription* or*an Anthropic Console API key (seeClaude authentication).A Slack workspace(free or paid) where you can install a custom app.*Optional:*aGitHub repository if you want per-turn review branches. The bot runs fine without one.

git clone <this repo URL>
cd phd_fleet
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt

cp .env.example .env          # then fill in the three required values

.venv/bin/python bot.py

Then in Slack, run /new-student alice "your project briefing"

to spawn your first agent. The rest of this section explains each step.

See the Quick start above. A virtualenv is recommended but not required — any environment with the dependencies in requirements.txt works.

The repo ships slack-manifest.yaml — a manifest that already lists the three slash commands, the bot scopes, and the event subscriptions. To use it:

  • Go to https://api.slack.com/appsCreate New App→** From a manifest**. - Pick your workspace, paste the contents of slack-manifest.yaml

, and confirm. - Under Basic Information → App-Level Tokens → Generate Token and Scopes, create a token with theconnections:write

scope. Copy it (xapp-…

) — that's yourSLACK_APP_TOKEN

. - Under OAuth & Permissions, install the app to your workspace. Copy the** Bot User OAuth Token**(xoxb-…

) — that's yourSLACK_BOT_TOKEN

.

cp .env.example .env

To find your ADVISOR_SLACK_USER_ID

: in Slack, click your profile picture → View full profile → the menu → Copy member ID. It looks like U0123ABC456

. The bot rejects messages from anyone whose user ID does not match this value — the only access-control surface.

Two paths, in order of preference:

Subscription (recommended). Ifclaude

is OAuth-authenticated to your Claude.ai account — i.e.,~/.claude/.credentials.json

exists from runningclaude /login

once — the bot just uses it.LeaveANTHROPIC_API_KEY

unset.API key. To pay per token through the Anthropic Console, setANTHROPIC_API_KEY=sk-ant-…

in.env

. The Agent SDK picks it up and ignores the subscription path.

.venv/bin/python bot.py

You should see:

… INFO ready — listening on Socket Mode (advisor=U0123ABC456)
… INFO A new session (s_…) has been established
… INFO ⚡️ Bolt app is running!

Health check: in any channel where the bot is present, @<bot> ping

returns pong

.

For long-running operation, run the bot under a process supervisor — tmux

is the simplest path, systemd --user

if you want auto-restart on crash. The bot reconnects to Slack on transient disconnects but cannot survive a full process exit without a supervisor.

/new-student alice "Investigate AlphaFold confidence on disordered regions. Read the recent literature, then propose a small experiment."

What this does:

  • Validates the name against ^[a-z0-9][a-z0-9_-]{0,40}$

. - Scaffolds students/alice/

fromstudent_template/

, filling in the name and project briefing. - Creates #student-alice

in Slack and invites you. - Registers Alice in agents.json

(the runtime registry). - Kicks off the first turn — the agent reads its CLAUDE.md

, gets oriented, and reports back.

After that, every message you send in #student-alice

becomes the next prompt. The agent's session resumes across turns and across bot restarts.

The coach has its own channel, #mentor-coach

, created at first bot startup. Two ways to use it:

Free chat. Anything you write in#mentor-coach

becomes a prompt —*"How should I handle a student proposing a method I think is wrong?"*The coach responds in a coaching voice, asks clarifying questions, and names a relevant framework when appropriate.Structured review./coach-review alice 7

pulls the last 7 days of#student-alice

plus a recent excerpt of Alice'sJOURNAL.md

, and asks the coach to reviewyourmentoring of Alice — what was done well, what could be sharper, each tied to a specific moment. The result is posted in#mentor-coach

, and the coach also updates a longitudinalmentor/coach/notes/advisees/alice.md

.

/claude-status

Ephemeral reply listing every agent: kind, turns taken, last context size, cumulative input/output tokens, model, total cost, and the GitHub branch link if available. Pure local read — does not call Claude.

Manual on purpose. To see what you have, look in students/

. To archive a student: mv students/<name> students/_archived/

and remove its entry from agents.json

. Slash commands for operations you'll do twice a year aren't worth their weight.

Each student lives in students/<name>/

:

— persona and project briefing, filled in at create time fromCLAUDE.md

student_template/

.— append-only research log, one section per session.JOURNAL.md

— private scratchpads, design notes, intermediate analyses.notes/

the rest of the directory— actual work artifacts (code, data, results).

Each student is told to check library/

(the shared pool) before reading anything new, to write new paper summaries there, and never to run git — the bot handles publishing.

The lab-wide habits and conventions live in LAB_CONTEXT.md, auto-appended to every agent's system prompt. Edit it once and every agent picks up the new rules on its next turn.

The coach lives in mentor/coach/

, with the same workspace structure as a student plus notes/advisees/<name>.md

for longitudinal observations. Its CLAUDE.md

carries a coaching persona and a vocabulary of named frameworks (GROW; SBI; Vygotsky's ZPD; feedforward). It uses the same runner, the same per-agent lock, and the same scaffolding as a student. The coach is reactive only: it speaks when summoned — there is no auto-review after every student turn.

library/

is a single directory at the project root that every agent reads from and writes to:

First reader of paperXwriteslibrary/<citekey>.md

(a markdown summary with YAML frontmatter) andlibrary/<citekey>.pdf

if it's freely downloadable.Later readers who want to add their take write aseparatefile —library/<citekey>__notes_<their-name>.md

. They never edit a peer's summary.is the index.library/README.md

Agents only read it. The bot regenerates it after each turn by walkinglibrary/*.md

, parsing frontmatter, and rewriting the table.

Citekey collisions (two papers by the same author in the same year) are resolved with letter suffixes — jumper2021a

, jumper2021b

. The full conventions live in LAB_CONTEXT.md. This per-file-ownership shape avoids the failure mode where two agents append to the same index at once and clobber each other.

If you've configured origin

, the bot publishes each agent's workspace to a per-agent branch (agent/<name>

) after every turn, using a force-push with a lease. It stages into a temporary git index so the bot's commits never disturb your working tree. The Slack final message links to the branch on GitHub. The step-by-step rationale is documented in the comments of src/agents.py (commit_and_push

).

If you don't want this, simply don't add an origin

. The bot skips the publish step silently and the Slack messages won't include review links.

Most behavior is set in .env

; the per-agent .claude/settings.json

files carry the permission deny-list (see Security model). The .env

knobs:

Variable Default Purpose
SLACK_BOT_TOKEN
required Bot User OAuth Token (xoxb-… ).
SLACK_APP_TOKEN
required App-Level Token with connections:write (xapp-… ).
ADVISOR_SLACK_USER_ID
required The single user allowed to talk to the bot.
ANTHROPIC_API_KEY
unset Optional. Set to use a Console API key instead of subscription auth.
AGENT_TURN_TIMEOUT_SECONDS
3600
How long one turn may run before being canceled.

This is defense-in-depth, not a sandbox. Each agent's .claude/settings.json

carries a permission deny-list shipped in both templates:

  • Sensitive paths are never read: **/.env*

,**/.ssh/**

,**/.aws/**

,**/.config/gh/**

,id_rsa*

,id_ed25519*

,/etc/**

,/root/**

. - Privilege-escalation and cluster-job verbs are never run: sudo

,su

,chmod

,chown

,srun

,sbatch

,scancel

,salloc

.

Per-deployment additions (e.g. other cluster schedulers) are easy to add to the same file. The deny-list is not isolation: a determined prompt-injection from a fetched paper could still misuse Bash

or Write

within the agent's workspace. Run the bot only on a host where that risk is acceptable, and only ever as the single configured advisor — the bot drops every Slack message whose user ID doesn't match ADVISOR_SLACK_USER_ID

.

Single advisor. No team / multi-advisor mode — the framework is shaped around one researcher's attention.Reactive only. Agents run when you message them. There is no scheduled wake-up, no inter-turn autonomy, no proactive coach observation.No web UI. Slack is the UI.No automated test suite. Verification is by smoke-running the bot.Not a sandbox. SeeSecurity model.

phd_fleet/
├── bot.py                  # Slack entry point: handlers, bootstrap, main()
├── src/
│   ├── paths.py            # path constants, env config, logger, NAME_RE
│   ├── agents.py           # registry, workspace scaffolding, git publishing
│   ├── library.py          # LAB_CONTEXT  + library/README.md index
│   ├── slack_io.py         # Slack helpers, status, markdown → mrkdwn renderer
│   └── runner.py           # run_agent — the per-turn Agent SDK driver
├── requirements.txt
├── pyproject.toml          # ruff / isort config
├── slack-manifest.yaml     # paste into api.slack.com/apps to provision the app
├── .env.example            # copy to .env and fill in the required values
├── LAB_CONTEXT.md          # lab-wide rules, auto-appended to every agent
├── student_template/       # scaffold copied into students/<name>/
│   ├── CLAUDE.md
│   ├── JOURNAL.md
│   ├── notes/
│   └── .claude/settings.json
├── mentor_template/        # scaffold for the coach (created once at startup)
│   ├── CLAUDE.md
│   ├── JOURNAL.md
│   ├── notes/advisees/
│   └── .claude/settings.json
├── library/                # shared paper pool
│   └── README.md           # regenerated index; never edit by hand
├── students/<name>/        # one workspace per student (created at runtime, gitignored)
├── mentor/coach/           # the coach's workspace (created at startup, gitignored)
└── agents.json             # runtime registry (created at runtime, gitignored)

agents.json

, the virtualenv, and the per-agent workspaces under students/

and mentor/

are all gitignored. The intent: the repo tracks the toolkit — the bot, the templates, the library scaffolding — while each agent's evolving work lives on its own GitHub branch via the per-turn publishing.

MIT.

── more in #ai-agents 4 stories · sorted by recency
── more on @phd_fleet 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/phd-fleet-manage-a-v…] indexed:0 read:10min 2026-06-19 ·