compile: register every persona where the CLI actually looks

Merged
KH
2mo ago
+1514-48

agentworkforce agent hn-monitor fails with Unknown persona "hn-monitor", then lists personas that don't include it — which reads like a broken install rather than a layout mismatch.

What was wrong

The CLI resolves personas from <cwd>/.agentworkforce/workforce/personas/ and does not recurse. agentworkforce persona compile writes persona.json beside its input, and deploy is handed persona.ts directly, so nothing else in the pipeline minded that every persona here lives in <agent>/. Only the interactive CLI did — and all 21 personas in this repo were invisible to it.

The fix

npm run compile becomes scripts/compile-personas.mjs instead of a shell loop. It compiles as before, then registers each result into the personas directory.

Two details that aren't obvious:

Keyed on the persona id, not the directory. granola/ publishes granola-prospect, linear/ publishes linear-chat-lead, review/ publishes pr-reviewer. Naming the registered file after the directory would publish all three under a name the CLI then can't resolve.

Not a cp. Relative paths inside a persona JSON resolve against the directory it was read from, so ./agent.ts would point at a handler sitting in the personas directory. Every relative path is rebased as the copy is written.

Three things the shell loop didn't do

BeforeNow
A persona that stops compilingloop continues; npm run evals runs against a stale persona.jsonfails loudly
A renamed or deleted agentghost persona in the picker forever, no source left to rebuild itmanifest-tracked, so the registration goes with it
A persona with no harness—skipped and named, not registered

On the middle one: this is exactly how the three dead *-telegram/persona.json artifacts outlived the agents PR #91 deleted — */persona.json is gitignored, so the build output survived the delete on every machine that had compiled before. Only files the script itself wrote are ever removed; anything agentworkforce install puts there is left alone.

On the last one: spotify-releases and vendor-monitor both say "no model needed" in their source and are pure fetch-and-deliver handlers. There's no interactive session to drop into, and registering them made persona-registry warn on every CLI invocation.

Verified

$ npm run compile
Not registered: askable-gtm (askable-gtm) declares no harness, so there is no interactive session to run
Not registered: linear-chat-lead (linear) declares no harness, so there is no interactive session to run
Not registered: spotify-releases (spotify-releases) declares no harness, so there is no interactive session to run
Not registered: vendor-monitor (vendor-monitor) declares no harness, so there is no interactive session to run
Compiled and registered 14 persona(s)

$ agentworkforce list          # 14 from cwd, no warnings
$ agentworkforce agent hn-monitor
→ hn-monitor via claude (claude-haiku-4-5-20251001)
✔ spawning claude (mount=on)

Tests

Eight new tests in tests/persona-registration.test.mjs, following the .test-build import pattern the existing persona tests use:

  • every directory with a persona.ts is discovered, and nothing else is
  • a directory with a stale persona.json and no persona.ts is not discovered
  • persona ids are unique (the personas directory is a flat namespace)
  • registration is keyed on id, which isn't always the directory name
  • a persona that declares a harness declares everything needed to launch it
  • relative paths still resolve from the personas directory after the rebase
  • package specs and URLs are left alone by the rebase
  • the manifest lives outside the scanned directory — inside, the CLI would report it as a persona with no id, which is the exact warning class this removes

npm test — 288 tests, 286 pass. The 2 failures (required Workforce package closure covers the installed invoke path, published package proof derives exact versions from the producer manifests) are pre-existing on main: same two fail there at 280/278.

Worth a look, not changed here

askable-gtm and linear-chat-lead declare no harness, so they're skipped alongside the two deliberate ones. Unlike those two, both look like oversights rather than decisions — askable-gtm declares harnessSettings: { reasoning: 'low', timeoutSeconds: 300 } and linear-chat-lead declares model: 'gpt-5.5', and neither reads like an agent that runs no model. Picking a harness for a live agent is a cost decision, so I left them alone. Adding one is all it takes for them to register.

🤖 Generated with Claude Code


Summary by cubic

Registers compiled personas where the CLI resolves them and makes askable-gtm launchable via a cloud-backed integration gateway. Previously the CLI didn’t recurse and interactive runs returned “Unknown persona”; now personas register by id with rebased paths and manifest tracking, and askable-gtm routes live queries through Cloud with precise error and access handling.

  • Compile/registration: npm run compile runs scripts/compile-personas.mjs to compile all persona.ts, register harness-backed personas by persona.id into .agentworkforce/workforce/personas, and rebase relative paths. Adds a manifest outside the scanned directory with ownership fingerprints, refuses to overwrite unmanaged files, and removes only stale, previously owned registrations. Fails fast on compile errors; personas without a harness are skipped and named. Tests in tests/persona-registration.test.mjs cover discovery, id uniqueness, rebasing, manifest placement, and collision/escape edge cases.

  • askable-gtm: declares harness, model, and systemPrompt; capability status moves to prototype-gated and provider operation is POST /api/v1/workspaces/:workspaceId/integrations/revternal/actions/social-listen. Live queries use a Cloud integration action client with normalized responses and strict error mapping (connection-required, provider-rate-limited, provider-auth-failed, provider-unavailable, invalid-response, request-failed). Access disclosure uses “Documented endpoint” and preserves credential-source metadata: managed, user (workspace-connected), or unknown. Tests validate gateway behavior and prompt evidence contract.

  • Action: run npm run compile before using agentworkforce list or agentworkforce agent; live queries for askable-gtm require Cloud runtime credentials and a connected Revternal workspace integration.