Skip to main content

AgentBrief — sub-agent briefing primitive

When a LEAD-mode agent hires a sub-agent (via the sidecar /spawn endpoint or via the operator’s crewship hire), the sub-agent needs context — at minimum what mission it’s been assigned, often a curated slice of the parent’s memory, plus any constraints the parent wants to layer on top of the crew policy. Before PR-F, the only knob was SkipConvHistory — a boolean. true meant “start the sub-agent with nothing”, false meant “dump the lead’s full conversation history into the sub-agent’s context.” Either choice was wrong for most cases: nothing meant the sub-agent had to figure out the mission from scratch; everything meant noisy + leaked the parent’s internal monologue. AgentBrief (internal/orchestrator/agent_brief.go) is the middle option. The lead chooses what to share. The sub-agent reads the brief as just another memory tier alongside AGENT.md / CREW.md / PERSONA.md.

The struct

Validation caps

These are defensive safety ceilings, not product constraints. The goal is to make “brief” actually mean brief — defence in depth against a malformed brief (or a parent agent that decides to dump everything) overflowing the sub-agent’s context budget on its first turn.
  • missionMaxBytes = 500 — one paragraph. Anything larger should live in SharedMemory references, not the brief envelope. If the mission needs more than 500 chars to state, the parent’s understanding of what it’s delegating isn’t compact enough yet.
  • sharedMemoryMax = 10 — ten parent-tier pointers is already a lot of cross-context for one sub-agent to digest. More than that is usually a sign the lead should be sharing a whole CREW.md tier rather than cherry-picking files.
  • constraintsMax = 20 — twenty short do/don’t lines (each must be non-empty). Constraints are not the place to write a whole policy document; they’re the place to write “do not modify migration v107”, “ask before deploying”, “the user dislikes long bullet lists.” If the parent has more, push them into PERSONA.md so they survive across sub-agent hires.
Validate() rejects on every overflow with an explicit message naming which cap fired. ApplyBrief calls Validate() before writing — a malformed brief never lands on disk.

How it gets into the sub-agent’s prompt

The base64 encoding sidesteps a class of shell-quoting bugs the previous direct-interpolation approach was vulnerable to (a single quote in a parent-supplied path could break out of the sh -c envelope — caught by CodeRabbit round 8). The script is a constant string; dir, encoded, target are passed as positional args ($1 / $2 / $3) so the shell can’t reinterpret them. The sub-agent then sees a system prompt section like:
Unbriefed agents see byte-identical output minus the BRIEF.md section — assembleSections skips empty sections, so the [AGENT MEMORY] envelope shape doesn’t drift between briefed and unbriefed agents (no system-prompt-cache-busting differences for the LLM).

Idempotency

ApplyBrief is safe to call repeatedly. Re-applying overwrites in place; the sub-agent always sees the latest brief on the next system-prompt assembly. Use cases for re-apply:
  • Mid-mission update — parent realises the sub-agent needs a new constraint (“oh, also don’t push directly to main”). Lead writes a new brief with the constraint added; next turn the sub-agent sees it.
  • Mission refinement — parent narrowed the scope after discovering the regression was in a different layer. New Mission text lands on disk; next turn the sub-agent gets the focused version.
  • Constraints append — parent reads the agent’s chat and notices a footgun pattern. Adds a do not X line and re-applies.
There’s no “diff vs previous” affordance — every ApplyBrief is a full replace. If the parent wants additive constraints, the parent reads the current brief, appends, and re-writes. This is intentional: full-replace is the simplest contract that can’t drift, and briefs are short enough that a roundtrip-read-modify-write is cheap.

LEAD-driven hire — the canonical caller

LEAD-mode agents in active orchestration are the main caller. The pattern:
The container must exist before ApplyBrief is called (the function does exec into it). For a fresh hire that hasn’t been provisioned yet, defer the brief until the container starts. The orchestrator’s hire flow handles this — ApplyBrief is called as part of post-provision setup.

What’s NOT in the brief

Intentional omissions:
  • No tool grants — what tools the sub-agent can call is determined by the sub-agent’s own agent definition + crew policy. A brief can’t grant a tool the sub-agent doesn’t already have.
  • No credential grants — same reason. Credentials live in sidecar credstore, scoped per agent. A brief can’t lend the parent’s credentials to the child.
  • No “let me see your output” callback — the parent doesn’t subscribe to the sub-agent’s responses through the brief. If the parent wants visibility, it uses the existing peer-message / orchestration-status surface.
  • No autonomy override — a brief in a strict crew doesn’t bypass the inbox approval requirement for the sub-agent’s actions. Policy is still upstream.
  • No nested briefs — a sub-agent that itself hires a grandchild can write a brief for the grandchild, but the parent’s brief doesn’t propagate transitively. Each hop is explicit.
These omissions are why the brief is safe to flow through between trust boundaries — it can’t escalate privileges, it can’t leak credentials, it can’t bypass policy. Worst-case malicious brief is one that wastes the sub-agent’s context budget on irrelevant content. That’s a denial-of-service the validation caps already mitigate.

Testing patterns

Unit tests against the validation contract:
End-to-end against the apply flow uses an in-memory filesystem provider:
System-prompt assembly:

What’s NOT shipped yet (PR-F follow-ups)

  • Aux-LLM-summarised briefs. Today the parent agent writes the brief by hand (or via prompt template). A future surface could let the parent say “summarise the last 20 turns of my conversation into a brief” and have the F3 aux model produce it. Tracked as PR-F follow-up.
  • Operator UI to view + edit briefs. Today the brief is invisible to the operator unless they shell into the container and read BRIEF.md. A dedicated panel in the Agent Canvas showing the current brief + edit history is a nice-to-have.
  • Brief revocation. The current model is overwrite-only. An explicit RevokeBrief(agentID) that drops the brief entirely (so the sub-agent reverts to a fresh-hire context) is missing. Today an empty AgentBrief{} does roughly this but doesn’t delete the file.
  • Multi-parent briefs. A sub-agent assigned to two parents simultaneously can only have one brief. Co-ordinating two parents writing the same brief is “last write wins” today; a richer model (briefs keyed by ParentAgentID, merged at read time) is design-space, not engineering yet.

Cross-references

  • internal/orchestrator/agent_brief.go — implementation
  • internal/orchestrator/agent_brief_test.go — validation + apply tests
  • internal/orchestrator/memory.go::buildAgentMemoryBlock — the read site that picks up BRIEF.md
  • Ephemeral agents — operator-facing hire flow that AgentBrief layers onto
  • Memory Provider interface — the other PR-F architectural primitive
  • Autonomy + self-learning — crew-level policy that briefs sit underneath