* feat(agents): vendor trimmed ponytail doctrine (full + ethos) * feat(agents): compose ponytail doctrine layer, bundled with fable * docs: document ponytail doctrine bundled with fable-mode * style: add trailing newline to ponytail doctrine files test * docs: changelog + map/rag for ponytail doctrine (0.19.0) user-facing docs skipped: Fable precedent absent from README/deployment/usage; ponytail is default-off internal. --------- Co-authored-by: Renn F <rennf93@users.noreply.github.com>
5.3 KiB
Agent Doctrine Layers
How compose_prompt (roboco/agents/factories/_base.py) assembles an agent's system prompt at spawn, and where the optional doctrine layers sit. Doctrine is prompt-only — no hooks, no grok-path changes — and gated by a single flag.
Layer order
compose_prompt(role, team, agent_slug, base_path=None, ambient=None) concatenates these layers with --- separators, skipping any that resolve to empty/None:
_tool_load_directive_layer(role)— top-of-prompt "your tools are ready" block._lifecycle_layer(prompts_path, role)— autogenerated verb-surface fragment (_generated/lifecycle-<role>.md).base.md— universal base rules (identity separation, envelope shapes, ground rules).fable_doctrine_layer(prompts_path)— vendored Fable behavioral doctrine (0.18.0).ponytail_doctrine_layer(prompts_path, role)— vendored Ponytail build-laziness doctrine (0.19.0), role-scoped, bundled with Fable._role_layer(prompts_path, role)— role prompt (roles/<role>.md)._autogen_verbs_layer(prompts_path, role)— per-role verb-signature table._team_layer(prompts_path, team)— team prompt (teams/<team>.md).- Identity file (
identities/<slug>.md). ambient— optional architectural-conventions ambient block (conventions_ambient_layer).
The order is load-bearing: lifecycle precedes base so the agent reads its allowed verb surface before any other instruction. Reordering changes model attention priority.
Fable doctrine (0.18.0)
fable_doctrine_layer(prompts_path) returns the vendored agents/prompts/doctrine/fable.md text, or None when settings.fable_mode_enabled is off / the file is missing. Source: github.com/rennf93/opus-fable-playbook (MIT), YAML frontmatter stripped. Covers communication, turn-discipline, autonomy-calibration, honesty, code-discipline, delegation, precedence. Slotted right after base.md. When the flag is on, the Fable hooks (docker/scripts/fable-*.sh) are also installed at spawn on the Claude runtime (and a non-denying honesty-nudge hook on grok) — those hooks are out of scope for this chunk; this file is about the composed prompt only.
Ponytail doctrine (0.19.0)
ponytail_doctrine_layer(prompts_path, role) returns the vendored Ponytail build-laziness doctrine, gated on the same fable_mode_enabled flag (no separate flag — ponytail is Fable's complementary build-doctrine). Slotted into compose_prompt immediately after fable_doctrine_layer. Role-scoped:
- Developers (
AgentRole.DEVELOPER) →agents/prompts/doctrine/ponytail.md: the full ladder (YAGNI → reuse-in-this-codebase → stdlib → native-platform → installed-dep → one-line → minimal), the rules, theponytail:comment convention, an Intensity table, and a 5-point RoboCo preamble that makes the ladder yield to the Architectural Conventions Standard (placement), the 80% coverage gate + QA review + self-verification, the per-team design bar, task hygiene, and reviewer feedback. The layer appends a one-line**Operative intensity: {settings.ponytail_intensity}.**directive that the Intensity table resolves into concrete behavior. - Every other role →
agents/prompts/doctrine/ponytail-ethos.md: the ethos-only cut — the code-mechanics rungs (the ladder) and the Intensity table are removed so they can't leak into prose artifacts (task plans, review notes, docs). The 6th preamble point guards free-text field obligations (ac_verdicts,findings,dev_notes,qa_notes). No intensity directive (ethos runs a fixed restrained stance regardless ofponytail_intensity).
Source: ponytail plugin (MIT, Copyright (c) 2026 DietrichGebert), vendored trimmed, YAML frontmatter stripped. Both doctrine files are static markdown with no DB/project dependency — the layer resolves synchronously inside compose_prompt, like fable_doctrine_layer.
None when the flag is off or the file is missing, so a flag-off spawn is byte-for-byte unchanged.
Intensity knob
ROBOCO_PONYTAIL_INTENSITY (env) / settings.ponytail_intensity (roboco/config.py, Literal["lite","full","ultra"], default full). A string value, not a feature flag — no FEATURE_FLAGS entry, no panel toggle. pydantic validates the Literal at Settings instantiation, so an invalid env value raises at startup.
| Level | Developer behavior |
|---|---|
lite |
Build what's asked; name the lazier alternative in a ponytail: note, don't impose it. |
full |
The ladder enforced. Stdlib and native first, shortest working diff, shortest explanation. Default. |
ultra |
YAGNI extremist: deletion before addition, challenge the requirement before the rung. Still bounded by the preamble (reviewer feedback and explicit requests win). |
Non-developers get no dial — ultra is wrong for prose artifacts, so the ethos runs a fixed restrained stance regardless.
Files
| File | Purpose |
|---|---|
agents/prompts/doctrine/fable.md |
Fable behavioral doctrine (0.18.0). |
agents/prompts/doctrine/ponytail.md |
Ponytail full doctrine — developers (0.19.0). |
agents/prompts/doctrine/ponytail-ethos.md |
Ponytail ethos-only — non-developers (0.19.0). |
roboco/agents/factories/_base.py |
compose_prompt, fable_doctrine_layer, ponytail_doctrine_layer. |
roboco/config.py |
fable_mode_enabled (gates both), ponytail_intensity (string value). |