barony 0.5.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. barony-0.5.1/.gitignore +6 -0
  2. barony-0.5.1/PKG-INFO +382 -0
  3. barony-0.5.1/README.md +365 -0
  4. barony-0.5.1/pyproject.toml +64 -0
  5. barony-0.5.1/scripts/sync_templates.py +62 -0
  6. barony-0.5.1/src/baron/__init__.py +10 -0
  7. barony-0.5.1/src/baron/cli.py +751 -0
  8. barony-0.5.1/src/baron/clock.py +40 -0
  9. barony-0.5.1/src/baron/data/capability-rules.v1.yaml +150 -0
  10. barony-0.5.1/src/baron/data/templates/collab-repo/.github/workflows/lock-guard.yml +80 -0
  11. barony-0.5.1/src/baron/data/templates/collab-repo/BOOTSTRAP-ADMIN.md +121 -0
  12. barony-0.5.1/src/baron/data/templates/collab-repo/BOOTSTRAP.md +156 -0
  13. barony-0.5.1/src/baron/data/templates/collab-repo/CLAUDE.md +36 -0
  14. barony-0.5.1/src/baron/data/templates/collab-repo/CONVENTIONS.md +146 -0
  15. barony-0.5.1/src/baron/data/templates/collab-repo/COORDINATION.md +201 -0
  16. barony-0.5.1/src/baron/data/templates/collab-repo/ORCHESTRATE.md +87 -0
  17. barony-0.5.1/src/baron/data/templates/collab-repo/PARTICIPATE.md +46 -0
  18. barony-0.5.1/src/baron/data/templates/collab-repo/QUICKSTART.md +48 -0
  19. barony-0.5.1/src/baron/data/templates/collab-repo/README.md +27 -0
  20. barony-0.5.1/src/baron/data/templates/collab-repo/START.md +44 -0
  21. barony-0.5.1/src/baron/data/templates/collab-repo/_failover-cron-sections/cloud-routine.md +23 -0
  22. barony-0.5.1/src/baron/data/templates/collab-repo/_failover-cron-sections/gh-actions-cron.md +34 -0
  23. barony-0.5.1/src/baron/data/templates/collab-repo/_failover-cron-sections/launchd-cron.md +28 -0
  24. barony-0.5.1/src/baron/data/templates/collab-repo/_failover-cron-sections/systemd-timer.md +33 -0
  25. barony-0.5.1/src/baron/data/templates/collab-repo/_handoff/README.md +38 -0
  26. barony-0.5.1/src/baron/data/templates/collab-repo/_handoff/{{DATE}}-bootstrap-to-librarian-genesis.md +38 -0
  27. barony-0.5.1/src/baron/data/templates/collab-repo/adapters/claude/HYDRATE.md +286 -0
  28. barony-0.5.1/src/baron/data/templates/collab-repo/adapters/code-puppy/HYDRATE.md +251 -0
  29. barony-0.5.1/src/baron/data/templates/collab-repo/adapters/generic/HYDRATE.md +181 -0
  30. barony-0.5.1/src/baron/data/templates/collab-repo/adapters/pydantic-ai/HYDRATE.md +176 -0
  31. barony-0.5.1/src/baron/data/templates/collab-repo/agents/__AUTONOMOUS_CRON__/AGENT.md +83 -0
  32. barony-0.5.1/src/baron/data/templates/collab-repo/agents/__AUTONOMOUS_CRON__/persona.yaml +45 -0
  33. barony-0.5.1/src/baron/data/templates/collab-repo/agents/__AUTONOMOUS_EVENT__/AGENT.md +88 -0
  34. barony-0.5.1/src/baron/data/templates/collab-repo/agents/__AUTONOMOUS_EVENT__/persona.yaml +43 -0
  35. barony-0.5.1/src/baron/data/templates/collab-repo/agents/__DEV__/AGENT.md +117 -0
  36. barony-0.5.1/src/baron/data/templates/collab-repo/agents/__DEV__/persona.yaml +42 -0
  37. barony-0.5.1/src/baron/data/templates/collab-repo/agents/__MERGER__/AGENT.md +49 -0
  38. barony-0.5.1/src/baron/data/templates/collab-repo/agents/__MERGER__/persona.yaml +49 -0
  39. barony-0.5.1/src/baron/data/templates/collab-repo/agents/__REVIEWER__/AGENT.md +49 -0
  40. barony-0.5.1/src/baron/data/templates/collab-repo/agents/__REVIEWER__/persona.yaml +50 -0
  41. barony-0.5.1/src/baron/data/templates/collab-repo/agents/librarian/AGENT.md +138 -0
  42. barony-0.5.1/src/baron/data/templates/collab-repo/agents/librarian/FAILOVER.md +104 -0
  43. barony-0.5.1/src/baron/data/templates/collab-repo/agents/librarian/persona.yaml +47 -0
  44. barony-0.5.1/src/baron/data/templates/collab-repo/decisions/README.md +56 -0
  45. barony-0.5.1/src/baron/data/templates/collab-repo/findings/README.md +32 -0
  46. barony-0.5.1/src/baron/data/templates/collab-repo/manifest.example.yaml +41 -0
  47. barony-0.5.1/src/baron/data/templates/collab-repo/wiki/README.md +30 -0
  48. barony-0.5.1/src/baron/data/templates/collab-repo/wiki/index.md +26 -0
  49. barony-0.5.1/src/baron/data/templates/collab-repo/wiki/log.md +7 -0
  50. barony-0.5.1/src/baron/data/templates/collab-repo/workspace-template/AGENTS.md +32 -0
  51. barony-0.5.1/src/baron/data/templates/collab-repo/workspace-template/CLAUDE.md +30 -0
  52. barony-0.5.1/src/baron/data/templates/collab-repo/workspace-template/setup.sh +280 -0
  53. barony-0.5.1/src/baron/data/templates/references/capability-rules.md +52 -0
  54. barony-0.5.1/src/baron/data/templates/references/capability-vocab.v1.md +101 -0
  55. barony-0.5.1/src/baron/data/templates/references/manifest.schema.md +128 -0
  56. barony-0.5.1/src/baron/data/templates/references/persona.schema.md +132 -0
  57. barony-0.5.1/src/baron/forge/__init__.py +38 -0
  58. barony-0.5.1/src/baron/forge/base.py +64 -0
  59. barony-0.5.1/src/baron/forge/github.py +107 -0
  60. barony-0.5.1/src/baron/frontmatter.py +53 -0
  61. barony-0.5.1/src/baron/gitutil.py +117 -0
  62. barony-0.5.1/src/baron/guard.py +573 -0
  63. barony-0.5.1/src/baron/handoff.py +161 -0
  64. barony-0.5.1/src/baron/indexer.py +111 -0
  65. barony-0.5.1/src/baron/ledger.py +120 -0
  66. barony-0.5.1/src/baron/lock.py +184 -0
  67. barony-0.5.1/src/baron/rules.py +160 -0
  68. barony-0.5.1/src/baron/runtimes/__init__.py +54 -0
  69. barony-0.5.1/src/baron/runtimes/pydantic_ai.py +365 -0
  70. barony-0.5.1/src/baron/scaffold.py +742 -0
  71. barony-0.5.1/src/baron/schemas.py +179 -0
  72. barony-0.5.1/src/baron/status.py +319 -0
  73. barony-0.5.1/src/baron/templates.py +36 -0
  74. barony-0.5.1/src/baron/validate.py +268 -0
  75. barony-0.5.1/src/baron/waivers.py +148 -0
  76. barony-0.5.1/src/baron/worktree.py +194 -0
  77. barony-0.5.1/tests/conftest.py +96 -0
  78. barony-0.5.1/tests/test_cli.py +68 -0
  79. barony-0.5.1/tests/test_guard.py +409 -0
  80. barony-0.5.1/tests/test_handoff.py +71 -0
  81. barony-0.5.1/tests/test_indexer.py +63 -0
  82. barony-0.5.1/tests/test_ledger.py +117 -0
  83. barony-0.5.1/tests/test_lock.py +149 -0
  84. barony-0.5.1/tests/test_pydantic_ai.py +318 -0
  85. barony-0.5.1/tests/test_rules.py +100 -0
  86. barony-0.5.1/tests/test_scaffold.py +194 -0
  87. barony-0.5.1/tests/test_schemas.py +45 -0
  88. barony-0.5.1/tests/test_status.py +169 -0
  89. barony-0.5.1/tests/test_template_sync.py +68 -0
  90. barony-0.5.1/tests/test_validate.py +114 -0
  91. barony-0.5.1/tests/test_waivers.py +176 -0
  92. barony-0.5.1/tests/test_worktree.py +150 -0
  93. barony-0.5.1/uv.lock +659 -0
@@ -0,0 +1,6 @@
1
+ __pycache__/
2
+ *.pyc
3
+ .venv/
4
+ dist/
5
+ build/
6
+ *.egg-info/
barony-0.5.1/PKG-INFO ADDED
@@ -0,0 +1,382 @@
1
+ Metadata-Version: 2.4
2
+ Name: barony
3
+ Version: 0.5.1
4
+ Summary: Git-native governance for teams of AI coding agents — the baron CLI over Barony's markdown/git collab substrate.
5
+ Project-URL: Homepage, https://github.com/vggg/barony
6
+ Project-URL: Repository, https://github.com/vggg/barony
7
+ Project-URL: Documentation, https://github.com/vggg/barony/blob/main/docs/concepts.md
8
+ Project-URL: Changelog, https://github.com/vggg/barony/blob/main/CHANGELOG.md
9
+ Project-URL: Issues, https://github.com/vggg/barony/issues
10
+ Requires-Python: >=3.10
11
+ Requires-Dist: pyyaml>=6.0
12
+ Requires-Dist: typer>=0.12
13
+ Provides-Extra: pydantic-ai
14
+ Requires-Dist: pydantic-ai-harness<0.11,>=0.10; extra == 'pydantic-ai'
15
+ Requires-Dist: pydantic-ai-slim<3,>=2.14.1; extra == 'pydantic-ai'
16
+ Description-Content-Type: text/markdown
17
+
18
+ # barony — git-native governance for teams of AI coding agents
19
+
20
+ > Install `barony`, run `baron`, import `baron`. Part of **Barony** —
21
+ > [github.com/vggg/barony](https://github.com/vggg/barony) — where the full
22
+ > framework, concepts, and adapters live. This page is the CLI reference.
23
+
24
+ `baron` is **Barony**'s CLI. It
25
+ turns the multi-agent coordination *conventions* that
26
+ Barony emits into *mechanisms*: a small CLI that scaffolds new projects
27
+ (`baron init`), validates the canonical specs, reports clone/branch/ledger
28
+ divergence, and performs the race-prone record-keeping operations
29
+ (finding/decision numbering, handoff lifecycle) safely.
30
+
31
+ **Design principle (ADR-003, `../docs/adr/ADR-003-baron-cli.md`): the
32
+ markdown/git substrate IS the database.** baron is a disciplined reader/writer
33
+ over the same human-legible collab-repo files the personas use
34
+ (`manifest.yaml`, `persona.yaml`, `findings/index.md`, `decisions/index.md`,
35
+ `_handoff/*.md`, `wiki/status.md`). It never introduces another store, and every
36
+ file it writes remains fully human/agent-legible.
37
+
38
+ Dependencies: **typer + pyyaml only**. `git` is driven via subprocess. `gh` is an
39
+ accepted prerequisite for forge features only (`baron lock`) — everything else
40
+ below works without `gh` installed. The pydantic-ai runtime adapter is an
41
+ **optional extra** (`barony[pydantic-ai]`, pinned) — see `baron hydrate` below.
42
+
43
+ ## Install
44
+
45
+ Once published to PyPI (distribution name `barony`):
46
+
47
+ ```bash
48
+ uv tool install barony # installs the `baron` console script
49
+ # or:
50
+ pip install barony
51
+ ```
52
+
53
+ Until then, install from a local clone:
54
+
55
+ ```bash
56
+ # from the repo root, with uv:
57
+ uv tool install ./cli # installs the `baron` console script
58
+ uv tool install './cli[pydantic-ai]' # + the pydantic-ai runtime adapter
59
+ # or for development:
60
+ uv run --project cli baron --help
61
+ # or with plain pip (>= 3.10):
62
+ pip install ./cli
63
+ ```
64
+
65
+ ## Quickstart
66
+
67
+ Verified end-to-end on a bare venv (`pip install` the wheel, no repo checkout):
68
+
69
+ ```bash
70
+ baron init gardenkit --dir gardenkit-collab --code-repo ./gardenkit \
71
+ --personas dev:fern,dev:moss,librarian:iris
72
+ cd gardenkit-collab
73
+ baron validate . # 0 errors
74
+ baron status # green when fresh
75
+ baron finding new --title "First finding" --author fern --no-push
76
+ HANDOFF=$(baron handoff create --for moss --from fern --title "Review the seam")
77
+ baron handoff close "$HANDOFF" --note "Done, see F1."
78
+ baron index # regenerates _handoff/README.md — commit it
79
+ baron worktree add fern # ../gardenkit-worktrees/fern, branch persona/fern
80
+ ```
81
+
82
+ ## Commands
83
+
84
+ ### `baron init <project-name>` (ADR-006)
85
+
86
+ The deterministic, non-conversational scaffold — the mechanical subset of the
87
+ skill's `ORCHESTRATE.md` recipe.
88
+
89
+ ```bash
90
+ baron init gardenkit --dir gardenkit-collab --code-repo ./gardenkit \
91
+ --personas dev:fern,dev:moss,librarian:iris --runtime claude
92
+ ```
93
+
94
+ Flags: `--dir` (default `./<project-name>`), `--code-repo` (local path or git
95
+ URL, recorded in `manifest.yaml` with a relative path + `workspace.worktrees_root`),
96
+ `--personas archetype:slug,...` (archetypes: `dev`, `librarian`,
97
+ `autonomous-event`, `autonomous-cron`, `reviewer`, `merger`; a librarian is
98
+ appended when missing), `--runtime claude|generic|pydantic-ai|code-puppy`,
99
+ `--no-git`. Refuses a non-empty target directory.
100
+
101
+ Emits the canonical layout — `CONVENTIONS.md`/`COORDINATION.md` (filled),
102
+ `manifest.yaml`, `canon/` + `adapters/` copied verbatim from the **packaged
103
+ templates** (vendored from `skills/barony/assets/collab-repo/` per ADR-006;
104
+ drift-guarded byte-for-byte by `tests/test_template_sync.py`), hydrated
105
+ `agents/<slug>/persona.yaml` (identity `<slug>@<project>.local`, commit prefix,
106
+ routing label; generic edit-me scope), a genesis handoff, `findings/index.md` +
107
+ `decisions/index.md` ledger headers, the wiki stub, the lock-guard CI template,
108
+ and a per-persona **runtime kit** under `agents/<slug>/runtime/`:
109
+
110
+ | `--runtime` | Kit contents |
111
+ |---|---|
112
+ | `claude` (default) | Tier-2 persona `CLAUDE.md` + `.claude/settings.json` wiring the `baron guard` PreToolUse hook |
113
+ | `generic`, `code-puppy` | Tier-1 `AGENTS.md` (instruction-only; code-puppy's documented fallback) |
114
+ | `pydantic-ai` | `agent_setup.py` bootstrap (running it needs `barony[pydantic-ai]`) |
115
+
116
+ The scaffold must pass `baron validate` with zero errors before init reports
117
+ success; then `git init -b main` + a first commit of exactly the files written
118
+ (never `git add -A`), unless `--no-git`. Dates come from the injectable clock.
119
+ Persona scope prose, `AGENT.md` manuals, and Tier-3 hydration (Claude
120
+ subagents, code-puppy JSON agents) stay on the conversational path — init
121
+ prints the pointers instead of pretending.
122
+
123
+ ### `baron validate [PATH]` (M1)
124
+
125
+ Validate `persona.yaml` / `manifest.yaml` — a single file, or every one
126
+ discovered under `PATH`. Schemas are declarative Python data
127
+ (`src/baron/schemas.py`) formalized from the prose specs in
128
+ `../skills/barony/references/`; the FROZEN 10-verb capability
129
+ vocabulary is embedded and drift-guarded by a test that re-parses
130
+ `capability-vocab.v1.md`.
131
+
132
+ Checks: YAML parse, missing/unknown fields, types, verbs outside the vocabulary,
133
+ allow/deny overlap (including `write_path` scope overlap), unfilled
134
+ `{{PLACEHOLDER}}` tokens.
135
+
136
+ **Template rule:** directory discovery skips files whose path contains a
137
+ template marker directory (`assets/collab-repo/` or `legacy/`) — emit-time
138
+ templates legitimately carry placeholders and often aren't valid YAML at all.
139
+ Fixture paths (`tests/examples/`) are validated but exempt from the placeholder
140
+ check only. An explicitly named file is always validated.
141
+
142
+ Exit 0 = no errors (warnings allowed) / 1 = errors. `--json` for machines.
143
+
144
+ ```bash
145
+ baron validate tests/examples/tess/persona.yaml
146
+ baron validate . --json
147
+ ```
148
+
149
+ ### `baron status [--fetch] [--sla N] [--json]` (M2)
150
+
151
+ Run from a collab repo (or `--collab PATH`). Reads `manifest.yaml` — including
152
+ the optional `workspace.clones` / `workspace.worktrees_root` fields (see
153
+ `../skills/barony/references/manifest.schema.md`) — and
154
+ reports, with severity:
155
+
156
+ | Check | Severity | Meaning |
157
+ |---|---|---|
158
+ | `ahead` | red | commits never pushed to origin (stranded work) |
159
+ | `behind` | red | origin commits never pulled (stale canonical clone) |
160
+ | `unmerged-branch` | red | local branch not merged to origin default, with last-commit age |
161
+ | `handoff-overdue` | red | `status: open` handoff older than the SLA (default 14 days) |
162
+ | `dirty` | warn | uncommitted paths |
163
+ | `ledger-stale` | warn | newest F/D entry date older than the newest `docs/`/`src/` commit in the code repo (**heuristic**, labeled as such) |
164
+ | `wiki-stale` | warn | `wiki/status.md` `updated:` older than the newest finding entry |
165
+
166
+ Use `--fetch` to refresh each working copy's origin refs first — without it,
167
+ remote-side divergence (the `behind` class) is invisible. Exit 0 = green
168
+ (warnings allowed) / 1 = any red (CI-usable).
169
+
170
+ ### `baron finding new` / `baron decision new` (M3)
171
+
172
+ ```bash
173
+ baron finding new --title "Tracker-gated recall" --author carson
174
+ baron decision new --title "Adopt fps-aware segmentation" --author terrence --body-file body.md
175
+ ```
176
+
177
+ Parses the index for the max ID (both `### F<N>` headings and `| F<N> |` table
178
+ rows), allocates the next, appends a house-style entry
179
+ (`### F<N> — <title> (<date>, <author>)`), commits, and pushes. **On push
180
+ rejection** (someone else claimed the number first): roll back, `pull --rebase`,
181
+ re-parse, renumber, retry — bounded (`--retries`, default 3). git's push
182
+ atomicity is the lock; there is no other store. `--no-push` for offline work.
183
+ Dates come from a single injectable clock (`src/baron/clock.py`).
184
+
185
+ ### `baron handoff create|close|list` (M3)
186
+
187
+ ```bash
188
+ baron handoff create --for tess --from rex --title "Review the seam" --priority high
189
+ baron handoff close _handoff/2026-07-22-review-the-seam.md --note "Done, see F9."
190
+ baron handoff list --open
191
+ ```
192
+
193
+ `create` writes `_handoff/YYYY-MM-DD-<slug>.md` with the standard frontmatter
194
+ (`created` / `status: open` / `for` / `from` / `priority`). `close` flips
195
+ `status` to `done`, adds a `closed:` date and an optional blockquote note, then
196
+ `git mv`s the file to `_handoff/archive/YYYY/` — **archive, never delete**, with
197
+ history preserved. Status edits are textual so prose is never reflowed.
198
+
199
+ ### `baron index` (M3)
200
+
201
+ Regenerates a marker-delimited summary block (`BEGIN/END BARON INDEX` HTML
202
+ comments) in `_handoff/README.md` (creating it if absent): open/done/archived
203
+ counts plus a table of open handoffs (file, for, from, age). Also verifies
204
+ finding/decision numbering: duplicates are errors (exit 1); gaps and
205
+ out-of-order headings are **report-only** warnings — baron never renumbers
206
+ history.
207
+
208
+ ### `baron guard --persona-file <persona.yaml>` (M4)
209
+
210
+ Deterministic capability enforcement as a **Claude Code PreToolUse hook**
211
+ (ADR-004). Implements the documented hooks contract
212
+ (https://code.claude.com/docs/en/hooks — the canonical target of the old
213
+ docs.anthropic.com hooks URL): reads the hook JSON from stdin (`tool_name`,
214
+ `tool_input`, `cwd`), maps the call to the frozen v1 capability verbs, and
215
+ either stays silent (exit 0 — the normal permission flow applies) or blocks
216
+ (exit 2 with the reason on stderr, which the contract feeds to the model).
217
+ `--persona-file` may also come from env `BARON_PERSONA_FILE`.
218
+
219
+ What it decides:
220
+
221
+ - **Bash** — `git push` targeting the default branch → `push_main`;
222
+ `--force`/`-f`/`--force-with-lease`/`+refspec` → `force_push`;
223
+ `gh pr merge` → `merge_pr`; `git merge` while ON the default branch →
224
+ `push_main`. Parsing is **conservative**: an undeterminable push target
225
+ (e.g. bare `git push` outside a repo) is treated as the enforcement-relevant
226
+ verb and denied for personas lacking it, with stderr naming the inference;
227
+ personas holding the verb always pass. Non-git/gh commands pass — guard
228
+ governs capability verbs, not general shell.
229
+ - **Edit / Write / NotebookEdit** — `_handoff/` is universally writable;
230
+ `agents/<other-slug>/` needs `edit_other_personas` (a persona's own
231
+ `agents/<slug>/` dir is its own surface); denied `write_path` scopes always
232
+ block; otherwise `write_code` grants the write, and without it only the
233
+ persona's declared `write_path` scopes remain.
234
+ - **Unknown tools** — pass (a capability gate, not an allowlist).
235
+
236
+ **Policy source (since v0.3.0):** guard's rule table — the command patterns and
237
+ file-op scoping semantics above, plus the conservative-deny ambiguity policy —
238
+ is NOT hardcoded: it lives in the versioned artifact
239
+ `src/baron/data/capability-rules.v1.yaml` (package data, loaded by
240
+ `src/baron/rules.py`; `rules_version: 1`). It is THE single source for
241
+ enforcement rules; the pydantic-ai adapter below consumes the same table, so
242
+ decisions are identical across runtimes. A missing/unsupported artifact fails
243
+ CLOSED. Prose contract:
244
+ `../skills/barony/references/capability-rules.md`.
245
+
246
+ **Fail-closed but not brick:** unreadable persona file / malformed stdin →
247
+ DENY with actionable stderr. Escape hatch: `BARON_GUARD_OVERRIDE=<reason>`
248
+ allows the call BUT appends timestamp/tool/target/reason to
249
+ `.baron/guard-override.log` — a **tracked** file (deliberately not gitignored:
250
+ overrides are visible in diffs); each override is expected to become a
251
+ `_handoff/`. Wire-up (`.claude/settings.json`, matcher
252
+ `Bash|Edit|Write|NotebookEdit`): the Claude adapter's HYDRATE.md step 3c emits
253
+ it. Without baron installed the hook fails non-blocking and denials degrade to
254
+ instructed — honest degradation, never a bricked session.
255
+
256
+ ### `baron lock claim|release|list` (M5)
257
+
258
+ PR-as-lock (ADR-002 §3), replacing the race-prone markdown LOCK-commit
259
+ protocol — **the open PR is the lock**, the forge's PR list is the only state.
260
+
261
+ ```bash
262
+ baron lock claim contracts/models.py --reason "tightening the stage protocol"
263
+ baron lock list
264
+ baron lock release contracts/models.py
265
+ ```
266
+
267
+ `claim` creates branch `lock/<slug>` with one empty commit (via
268
+ `git commit-tree` — the local checkout is never touched; the empty commit is
269
+ load-bearing, GitHub refuses a PR whose head equals its base), opens a draft
270
+ PR titled `lock: <path>` labeled `lock:<path>` with the reason in the body,
271
+ and **refuses if an open lock PR for the path exists** (showing the holder).
272
+ `release` closes the lock PR and deletes the branch. `list` prints
273
+ path/holder/age/PR#. Requires `gh` (raises a clean `ForgeUnavailable`
274
+ otherwise); all forge calls go through the Forge Protocol, so tests run
275
+ against a fake. The CI side is the emitted
276
+ `.github/workflows/lock-guard.yml` template (bash + `gh`), which fails any
277
+ *other* PR touching a locked path.
278
+
279
+ ### `baron worktree add|list|remove` (M6 tooling)
280
+
281
+ The branch-per-persona worktree topology (ADR-003 §2.7): one shared object
282
+ store, worktrees under the manifest's `workspace.worktrees_root`.
283
+
284
+ ```bash
285
+ baron worktree add fern --collab . # <worktrees_root>/fern on branch persona/fern
286
+ baron worktree add fern --repo ../code --root ../worktrees # explicit paths
287
+ baron worktree list
288
+ baron worktree remove fern [--force]
289
+ ```
290
+
291
+ `add` creates `<root>/<persona>` on branch `persona/<persona>` (created from
292
+ the default branch if missing; an existing branch is reused). Defaults resolve
293
+ from the manifest (`workspace.worktrees_root`, `repos[role=code]`); `--root` /
294
+ `--repo` override. `list` shows each worktree's branch with ahead/behind vs
295
+ the default branch. `remove` refuses while the worktree is dirty or its branch
296
+ holds unmerged commits unless `--force` — and NEVER deletes the
297
+ `persona/<persona>` branch (removing a working copy must not destroy history).
298
+ `baron status` sweeps worktrees like clones (each reports its checked-out
299
+ HEAD's divergence; the repo-wide branch sweep runs once, on the shared repo).
300
+ Converting an existing clone-per-persona workspace:
301
+ `../docs/worktree-migration.md`.
302
+
303
+ ### `baron waiver add|list` + `.baron-waivers.yaml`
304
+
305
+ Deliberately-parked `baron status` reds, with mandatory expiry.
306
+
307
+ ```bash
308
+ baron waiver add "clone:rex *" --reason "kept for the vNext experiment" \
309
+ --handoff _handoff/2026-07-23-parked-branch.md --expires 2026-08-15
310
+ baron waiver list
311
+ ```
312
+
313
+ `.baron-waivers.yaml` (collab root, human-legible, baron-managed via `waiver
314
+ add`) holds `{subject, reason, handoff, expires}` entries; `subject` is an
315
+ fnmatch pattern on the status SUBJECT column. A matching, unexpired waiver
316
+ downgrades a red to warn with `(waived: <reason>)` appended — parked work
317
+ stays visible, just not alarm-red. **Expiry keeps waivers honest:** an expired
318
+ waiver stops matching (the red resurfaces) and is itself reported as an
319
+ `expired-waiver` warn; malformed entries are reported, never silently dropped.
320
+
321
+ ### `baron hydrate pydantic-ai --persona-file F [--out agent_setup.py]`
322
+
323
+ Emit a ready-to-edit bootstrap script hydrating one persona onto
324
+ **pydantic-ai** (the fourth runtime adapter,
325
+ `../skills/barony/assets/collab-repo/adapters/pydantic-ai/HYDRATE.md`).
326
+
327
+ ```bash
328
+ baron hydrate pydantic-ai --persona-file agents/fern/persona.yaml --out agent_setup.py
329
+ ```
330
+
331
+ The emitted script imports `baron.runtimes.pydantic_ai.build_agent` and
332
+ carries a model placeholder (`"test"` — pydantic-ai's offline TestModel —
333
+ until you pick a real model). Emission needs only baron; **running** it needs
334
+ the optional extra:
335
+
336
+ ```bash
337
+ pip install 'barony[pydantic-ai]' # pins pydantic-ai-harness>=0.10,<0.11
338
+ # + pydantic-ai-slim>=2.14.1,<3
339
+ ```
340
+
341
+ `build_agent(persona_file, collab_root=None, model=...)` returns a live
342
+ `pydantic_ai.Agent`: instructions composed from the persona spec; harness
343
+ `FileSystem` scoped per write verbs (natively read-only when the persona holds
344
+ no write verb); harness `Shell` only when a shell-granting verb is allowed
345
+ (with test runners denied when `run_tests` is denied); and an in-process guard
346
+ capability (`before_tool_execute` + `ModelRetry` veto — the documented
347
+ interception seam) consuming the same `capability-rules.v1.yaml` as
348
+ `baron guard`, which makes the five guard-covered sub-tool denials natively
349
+ **enforced** on this runtime. Without the extra, importing
350
+ `baron.runtimes.pydantic_ai` raises a clean ImportError with these install
351
+ instructions. Verified against pydantic-ai-harness 0.10.0 +
352
+ pydantic-ai-slim 2.16.0 (2026-07-23).
353
+
354
+ ## Forges
355
+
356
+ `src/baron/forge/` holds a small `Protocol` (`base.py`) with one built-in
357
+ implementation, GitHub via `gh` subprocess (`github.py`) — first consumed by
358
+ `baron lock` (M5: `create_branch`, label-aware `open_pr`/`list_open_prs`,
359
+ `close_pr`). Other forges are plugins discovered through the `baron.forges`
360
+ entry-point group; GitLab is backlog — design sketch in `../docs/BACKLOG.md`.
361
+
362
+ ## Development
363
+
364
+ ```bash
365
+ uv run --project cli pytest cli/tests # from the repo root
366
+ ```
367
+
368
+ The suite includes the capability-vocabulary drift guard, a synthetic divergent
369
+ git topology reproducing the 2026-07-22 triple-stranding incident classes, the
370
+ ledger push-rejection race test, subprocess-driven guard hook tests (synthetic
371
+ PreToolUse JSON on stdin), a recorded fake forge for the lock lifecycle, a real
372
+ two-persona worktree fixture, the waiver downgrade/expiry cases, the
373
+ capability-rules artifact tests (packaged + versioned, verb set ≡ the frozen
374
+ vocabulary, guard-consumes-the-data mutation test), and the pydantic-ai
375
+ adapter tests (offline TestModel/FunctionModel: capability omission, write
376
+ scoping, a scripted-and-vetoed `git push origin main`, the clean import-error
377
+ path), the `baron init` acceptance tests (layout + self-validation via the real
378
+ schemas + runtime kits + the non-empty-dir refusal), and the template drift
379
+ guard (`test_template_sync.py`: the vendored `src/baron/data/templates/` must
380
+ stay byte-identical to `skills/barony/assets/collab-repo/` — re-vendor with
381
+ `python cli/scripts/sync_templates.py`). The dev dependency group repeats the
382
+ pydantic-ai extra's pins so those tests run for real.