feat(fleet): opus-fable adoption — doctrine + discipline hooks (v0.18.0 A)

Fleet behaves more like Fable 5 on existing model tiers, behind
ROBOCO_FABLE_MODE_ENABLED (config default off; armed :-true on the NAS compose,
absent from the registry compose).

- Doctrine: vendored agents/prompts/doctrine/fable.md composed into every
  agent's system prompt via fable_doctrine_layer() after base.md.
- Hooks (Claude Code): 4 non-overlapping hooks (stop-gate/bash-discipline/
  honesty-nudge/precompact) appended per-agent via _fable_hook_groups(). The
  make-quality + lint-suppression duplicates are deliberately NOT added (already
  gate-enforced); session-start skipped.
- Hooks (grok): conservative V1 — only the non-denying honesty-nudge, since a
  grok hook deny cancels the whole run.
- Flag on the feature-flags card; hook scripts shipped into the agent image.

Flag-off spawn path proven byte-identical (worktree diff, sha256 match); full
suite green (2074 unit + e2e-smoke + hook harness), mypy/xenon/ruff clean.
Fixed a real stdin bug in the vendored stop-gate hook (heredoc + pipe both
claimed stdin). Distilled from rennf93/opus-fable-playbook (MIT).
This commit is contained in:
Renn F
2026-07-04 06:44:40 +02:00
parent 30289333da
commit 7716830322
24 changed files with 881 additions and 5 deletions
@@ -64,6 +64,8 @@ const FLAG_DESCRIPTIONS: Record<string, string> = {
"Also poll X mentions and draft replies (still held for your approval — nothing auto-replies). Off by default: reading mentions needs a paid X API tier, so leave this off if you only want release posts.",
roadmap_engine_enabled:
"Weekly: the Product Owner explores the company's projects and proposes a themed cycle of 3-7 roadmap items — you approve or reject each one individually; approved items land in the backlog and nothing auto-starts.",
fable_mode_enabled:
"Compose the Fable behavioral doctrine into every agent's system prompt and install the matching turn-discipline/honesty/verification hooks at spawn (both Claude Code and grok runtimes). Off by default; spawn path is byte-for-byte unchanged.",
};
export function FeatureFlagsCard() {