trusty-mpm 1.7.7

trusty-mpm: unified multi-agent orchestration platform (core, daemon, CLI, TUI, Telegram)
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
---
name: tm-workflow
description: The single trusty-mpm delivery workflow — phases and gates, the ticketing/workflow/version-control ownership boundary and handoff, worktree and branch discipline, changelog, PR body, review gate, squash-merge, cleanup, and how a project customizes the workflow via CLAUDE.md
user-invocable: true
version: "2.1.0"
category: pm-workflow
tags: [workflow, delivery-chain, pr, branch-protection, worktree, changelog, customization, verification-gates, pm-required]
effort: medium
---

# tm-workflow — The Delivery Workflow

<!-- #5202: this skill absorbed the retired `tm-pr-workflow`. There is one
     workflow skill. Issue policy lives in `tm-ticketing`; git and PR API
     mechanics live in the `version-control` agent. -->

This is the **sole workflow authority**: the complete sequence from an accepted
outcome to a merged PR and a closed issue, plus the gates applied along the way
and the mechanism a project uses to customize any of it.

It states policy and performs no operations. It never creates or mutates an
issue, a branch, a commit, or a PR — those are delegated, per the boundary
below.

## Ownership Boundary — Route by Artifact, Not by Verb

| Surface | Owns | Does not own |
|---|---|---|
| Core PM instructions | Detect the work shape, load this skill and `tm-ticketing`, preserve user authority, route the resulting actions | Tracker, git, or PR mechanics |
| **`tm-workflow`** (here) | The delivery sequence and its policy: phases/gates, worktree/branch conventions, changelog, PR body requirements, review, merge, cleanup, handoffs | Creating or mutating issues, branches, commits, or PRs |
| `tm-ticketing` + the `ticketing` agent | Whether an issue should exist; search/dedup; issue title and body; labels, assignee, milestone, comments, state, parent/child links | Any git operation, and any PR mutation — including PR title and body |
| The `version-control` agent | Branch/worktree/commit/push mechanics and the whole PR lifecycle: create, title/body, issue links, reviewers, checks, update-branch, arming auto-merge, the merge into main, post-merge verification against the exact head SHA, and reclaiming merged worktrees and their local branches | Authoring workflow policy, deciding whether an issue is warranted, changing issue metadata or state — including the `status:` advance its own merge just made due |

**The rule is the artifact, not "bookkeeping versus mechanics."** Every Issue API
operation (`gh issue …`, `mcp__mcp-ticketer__*`, `aitrackdown`) routes to
`ticketing` under P6. Every Pull Request API operation (`gh pr …`, including
`create`, `edit`, `view`, `list`, `diff`, `review`, `checks`, `update-branch`,
`merge`) and every git operation routes to `version-control` under P7. A PR body
is part of the PR, so it is version control's — splitting one `gh pr edit` across
two agents was the defect this replaced.

### The Handoff (stated once, here)

1. **PM → `ticketing`**: the finding or accepted outcome. Ticketing runs its own
   promotion and dedup gate and returns the canonical issue ID/URL, the outcome
   statement, and the closure conditions.
2. **PM → `version-control`**: the work summary, files changed, test evidence,
   the review verdict, and the issue context ticketing returned. Version control
   writes the PR body and inserts the correct issue link — `Refs owner/repo#N`
   by default, and a closing keyword only on a project whose `CLAUDE.md`
   permits a merge to auto-close the issue.
3. **After merge, PM → `ticketing`**: the merged PR number and squash SHA, for
   the completion comment and the `status:` advance. `version-control` confirms
   the merge and FLAGS the advance it owes — "#4409 owes `status:coded` ->
   `status:merged`" — because it may not make that edit itself. The PM routing
   that flag to `ticketing` is the only thing that keeps the label from going
   stale, since auto-merge lands PRs with nobody watching.

**Neither specialist delegates to the other.** The PM carries context between
them. A `ticketing` dispatch that ends "…then have version-control open the PR"
is a routing error.

## The Full Delivery Chain

```
accepted outcome -> optional issue -> worktree branch -> one cohesive PR
                 -> applicable gates -> trusty-review gate
                 -> squash-merge -> worktree cleanup -> issue closed
```

The chain starts at an **accepted outcome**, not at a ticket. The issue step is
**optional** for docs/CI/chore work and for a small fix the user explicitly asked
for that completes in one PR. It stays **required** for features, reproduced
defects, security work, cross-release dependencies, and any work that must
survive the current session. Whether a given finding earns an issue at all is
decided by the promotion gate in `tm-ticketing`, not here.

Every other step matters. Skipping the worktree step or the review gate is a
protocol violation the PM must not take on the user's behalf unasked.

## One Outcome, One PR

A PR contains one primary outcome and everything required to make that outcome
safely shippable: implementation, regression tests, necessary refactoring,
documentation/API updates, the changelog fragment, and in-scope review fixes.

- Do not split those artifacts into separate PRs because different agents
  produced them. The engineer's code, the QA agent's test, and the doc update for
  one outcome are one PR.
- **One PR may satisfy several tickets** when one coherent change resolves
  them (`Refs #A`, `Refs #B` — a closing keyword only where the project's
  `CLAUDE.md` permits merge-based auto-close, see "Minimal PR Body" below).
  Prefer that over several coupled PRs with an artificial merge order.
- Split only when the outcomes can be reviewed, deployed, or reverted
  independently, or when risk or size makes a stack materially safer to review.

**Bugfix batching is the default, not an exception.** Several open bugs
constrained to the same file, module, or tightly-coupled area go in ONE PR —
one engineer, one worktree, one review, one CI pass — not one PR per issue.
Each issue keeps its own regression test and its own `Refs #N` in the PR body.
One changelog fragment per crate may cover the whole batch, naming each issue.
Unrelated bugs in different modules still get separate PRs. This is what "no
two in-flight PRs share a module" means for a bug cluster: same module now
means same PR, not a serialized queue of small ones.

## The 5-Phase Model and Its Dispatch Briefs

This table is canonical for **whether** a phase runs and carries each skip
condition — moved here from the instruction package by #7423, which keeps only
the phase names and the conditional rule. Where a phase runs, its gate is
blocking — "conditional" governs entry, never rigour (#4594). A project may
replace the whole workflow section via a `WORKFLOW` marker in its `CLAUDE.md`
(see "Customizing the Workflow" below) if its delivery process differs.

| Phase | `subagent_type` | Gate | Skip When |
|-------|-------|------|-----------|
| 1. Research | `research` | Findings documented | User provides explicit instructions, simple task, language/approach known |
| 2. Code Analysis | `code-analyzer` | APPROVED / NEEDS_IMPROVEMENT / BLOCKED | Change is < 100 lines, no architectural impact, and not High risk |
| 3. Implementation | `engineer` (per lang detect) | Tests pass, files tracked, changelog entry added | Docs-only/CI-only change |
| 4. QA | `web-qa` / `api-qa` / `qa` | All criteria verified with evidence | Engineer self-verified (ran full test suite, raw output shown), user says "no QA" |
| 5. Documentation | `documentation` | Docs updated | No public API changes, internal refactor only |

**Phase 1 — Research** (`research`). Required for ambiguous requirements,
multiple possible approaches, or an unfamiliar codebase. Skipped when the user
gave an explicit command or the task is simple operational work.

```
Task: Analyze requirements for [feature]
Return: Technical requirements, gaps, measurable criteria, approach
```

**Phase 2 — Code Analysis** (`code-analyzer`, sonnet — NOT `code-critic`, which
is a separate agent).

```
Task: Review proposed solution
Use: think/deepthink for analysis
Return: Approval status with specific recommendations
```

Both `code-analyzer` and `code-critic` run the Fail-Open Check
(`code-review-standards`) against every failure branch touched, and name
which of its three outcomes each branch lands in: fail-open (defect),
fail-closed (correct), or reported degradation — the failure carried to the
caller in a tested response field (also correct, distinct from fail-open).

Decision: APPROVED → Implementation. NEEDS_IMPROVEMENT → back to Research.
BLOCKED → escalate to the user.

**Phase 3 — Implementation** (the language-specific engineer where one exists).
Requirements: complete code, error handling, test proof, and the changelog entry
described below. Skip only for docs-only/CI-only changes.

**Phase 4 — QA.** Routing: `api-qa` for APIs, `web-qa` for UI, `qa` otherwise.
The gate itself is `tm-verification-protocols`.

**Phase 5 — Documentation** (`documentation`). Skipped for an internal refactor
with no public API change. A spec edit driven by an owner ruling carries a
dated cross-reference at the change site — `// <date>: <owner ruling
summary>, see #<DOC-or-issue>` — mirroring the `// #NNNN:` ticket-attribution
convention for code. A spec change from an owner ruling with no dated
cross-reference fails review.

### Running the Phases

Moved out of the instruction package by #7423, which keeps the phase names and
the conditional rule.

- Don't force 5 phases when 2 will do. After each phase: `git status` →
  `git add` → `git commit`.
- On failure: 1 re-delegate with more context → 2 escalate to Research → 3 block
  and require user input.

### Override Commands

| User says | Effect |
|---|---|
| "Skip workflow" | Bypass the phase sequence |
| "Go directly to [phase]" | Jump to that phase |
| "No QA needed" | Skip phase 4 (not recommended) |
| "Emergency fix" | Bypass Research |

Honour the override and name the bypassed gate in the completion report, so the
missing evidence is visible rather than implied.

### Verification Gates Are Not an Override Target

The verification-gate contract in `tm-verification-protocols` is a
project-independent invariant enforced by CB#8. A custom `WORKFLOW` override can
change *when* QA runs, never *whether* a completion claim requires evidence.

## Sprint, then Harden — the Rest of the Doctrine

The instruction package states the two phases, where to spend the verification
budget, and the hard line (never turn red green by deleting coverage). Two
derived rules live here because they apply only at a specific moment:

- **A branch that has drawn 3+ review rounds is evidence to close and fold**, not
  to attempt round 4. Worked example: #4202 → #4207.
- **Branch = workstream, and it is durable. Worktree = writer, and it is
  ephemeral.** Keep worktrees short-lived; keep branches workstream-scoped.

Slow feature release *causes* too many things in flight. Shortening time-to-land
is the fix; capping WIP treats the symptom.

## Deterministic Work

Use existing search tools and CLIs for extraction, counts, comparisons and
explicit rules before model synthesis. If none fits, delegate a bounded,
read-only disposable helper; spot-check its inputs and representative output.
Use the model for ambiguity and judgment. Recurring tools follow the project's
implementation policy. Batch related routine work within existing budgets,
ownership and guards; this does not relax P10 or require new approval for
work already authorized. See #8021.

## Task-Owned Cleanup

Inventory only the paths this task owns; preserve other sessions and dirty or
unpushed work. Confirm the merged PR and live ownership before removal. Route
the operation through the authorized `version-control` agent. Use literal
paths with the supported guard, not shell-variable loops, for example:
`git -C /absolute/repo worktree remove /absolute/repo/.claude/worktrees/task-name`.
The paths are placeholders to replace with verified targets, never a glob.
A refusal is a finding to resolve, not permission to use `rm -rf` or global
`tm sessions prune-worktrees --merged-prs --force`. See #8021.

## Test Scope Widens by Stage

The project risk/stage test ladder takes precedence. Where it defines no
ladder, use the following stage defaults; widen for affected consumers and
high-risk contracts, not simply for an engineer-to-QA handoff.

| Stage | Scope |
|---|---|
| Developing | tests covering the new or changed code only |
| Merging | the full test files that changed |
| Publishing | the full corpus |

- **Developing** is the inner loop: the targeted test that proves the change,
  re-run as you edit. Nothing wider is owed while the code is still moving.
- **Merging** defaults to whole affected test files. Every test
  file the diff touched runs in full — including the cases you did not edit, and
  any normally-skipped test that lives in one of those files. A change to a
  public interface or to shared test infrastructure alters what a dependent
  package's test files mean, so those files count as changed even though the
  diff never opened them.
  - **Fixture-of-record files widen the same way.** A seed fixture, Terraform
    demo-data file, or content YAML that another package's tests read
    directly is "changed" for every package that reads it, not just the one
    that owns it. Before the critic verdict, grep for the file's consumers
    and run their suites, or state in the review that none exist — gating on
    the changed file's own language tooling (`terraform validate`, `bash -n`)
    alone missed a consumer whose test asserted a fixed row count (#7750).
- **Publishing** is the only stage that owes the whole corpus, plus whatever
  release gates the project defines.

This is the two-phase doctrine at finer grain: developing is SPRINT; merging and
publishing are two different widths inside HARDEN, and merging is the narrower.
A green merge gate is therefore not a release gate.

Stage and rigour are separate axes and both bind. The stage decides how *wide* a
gate runs; the project's risk labels and test ladder decide how *hard* it is
applied and which gates run at all. Pick the rung from the project's `CLAUDE.md`,
then read the stage off where you are in the delivery chain.

A consequence for branch protection: a full-corpus CI job is a publish gate, so a
project may leave it off the required pre-merge contexts and merge while it is
still pending. A **failing** check blocks the merge at every stage — only
*pending* is tolerated.

🔴 **Widening by stage is never licence to skip.** Choosing the narrower scope is
a claim about blast radius that you must be able to prove. It is never licence to
make a red gate green by deleting a test, marking it skipped or ignored, gating
it out of the build, or excluding it from the run. That hard line ("Sprint, then
Harden") is unchanged.

## Worktree Discipline (Mandatory Before Any Source Edit)

The main checkout's write boundary is mechanically enforced, not a convention
to remember (`tm hook --pm-guard`; ADR-0044, ADR-0048). Documents and
configuration stay writable there directly — `.md`, `.toml`, `.json`, `.yaml`,
extension-less files, `.claude/` framework deployment, and `TASK.md` — for the
PM and for every agent it dispatches. Forbidden against it: any SOURCE edit;
`git commit`; any build or test run (whatever the project's gate is: `cargo
build`/`test`, `npm run build`/`test`, `pytest`, …); any destructive git
operation (`git reset --hard`, `git checkout .`, `git stash`,
`git restore .`); any file-mutating command (`sed`/`awk`/`patch`) against a
source file — or anything else that mutates the working tree, index, or build
output outside that documents/configuration carve-out. All source write-side
work happens in a dedicated worktree branched off `origin/main`:

```bash
git fetch origin main
git worktree add -b <feature-or-fix-branch> \
                  .claude/worktrees/<dirname> origin/main
cd .claude/worktrees/<dirname>
```

🟡 **This is for the PM or a human working directly. It is NOT the dispatch
mechanism** — a subagent dispatch declares `isolation: "worktree"` instead, per
"Worktree Discipline" below and `tm-delegation-patterns`.

Fetch-before-branch, and its fetch-after-merge companion, are BASE-AGENT's Git
Workflow rule — the command block above is that rule's provisioning form. Local
`main` can be stale enough to lose commits, or to leave a fresh branch `BEHIND`
the moment its PR opens.

**Keep the main checkout fresh.** Worktrees branch off `origin/main` and stay
current; nothing refreshes the main checkout, so it drifts — and then every
inspection read (`git log`, opening a file to answer a question, checking
whether a fix already landed) silently answers from old code. At session start,
and after a PR this session merged lands on `origin/main`:

```bash
git -C /path/to/main-checkout fetch origin
git -C /path/to/main-checkout pull --ff-only
```

Fast-forward only — it cannot create a merge commit, cannot rewrite history, and
fails loudly instead of resolving anything silently. A dirty tree does not block
a fast-forward unless the incoming commits touch the same files, so the common
case just works.

**A session pause is one of those merges (#7282).** Where a project tracks
`.trusty-mpm/sessions/`, `/tm-session-pause` no longer commits the snapshot onto
the checkout's current branch — a commit on a PR-only `main` strands there, and
the refresh above then fails on every session. The pause commits onto its own
`chore/sessions-<session>-<ts>` branch off the project's default branch (the one
`config.yaml` declares, else `origin/HEAD`, else `main`), pushes it, and opens an
auto-merging docs-only PR. So run the fetch and `pull --ff-only` above after a
pause PR lands, the same as after any other merge, and the snapshot is visible
locally again. Where a project gitignores that store instead, a pause opens no
PR and owes no refresh — the snapshot is machine-local and stays on disk.

🟡 **One cause is an untracked path the incoming commits also add.** `--ff-only`
then aborts with "The following untracked working tree files would be
overwritten by merge", naming the path and nothing else. Diff that untracked
copy against the incoming one BEFORE pulling rather than improvising a recovery
after the abort: identical content resolves itself, differing content is
reported with the diff. The check and both outcomes are in
`Skill(skill="git-workflow")`, under the heading
"Pulling After a Merge — Check Untracked Collisions First" (#7558).

🔴 **If `--ff-only` fails, never clear the way by discarding.** The other usual
cause is another session's uncommitted work. Do not `git stash` — repo-level and
shared across every worktree, so it yanks state out from under sessions running
right now. Do not `git checkout --`, `restore`, `reset --hard`, or `clean`: the
main-checkout guard blocks these, correctly, and hunting for an unblocked
equivalent is routing around a safety control. A file showing ` M` in
`git status --porcelain` was never staged, so nothing recovers it once
discarded.

Identify the owner first — `git status --porcelain` for staged-vs-unstaged, file
mtimes, and the session log for who was alive in that window — and read what a
live peer is doing, per "Concurrent Sessions on One Repo" below, before assuming
the work is abandoned. If it is unowned, **preserve rather than discard**: commit
it out of the way, which is permitted and destroys nothing.

```bash
git checkout -b orphan/main-checkout-$(date +%Y%m%d)
git add -u && git commit -m "wip: orphaned main-checkout changes"
git checkout main && git pull --ff-only
```

Committing reaches the same clean tree as discarding, costs the same number of
steps, and cannot lose anything — the guard exists to prevent loss, not to
prevent refreshing, so the compliant path and the safe path are the same path.
If the checkout has genuinely diverged instead, report it and stop.

This is inspection hygiene only, so reads are not answered from stale code. The
main checkout stays read-only for source; every source edit still happens in a
worktree, and the boundary is enforced mechanically rather than left to
discipline alone. `git pull` is already on the PM's allowlist — no new
authority, not a budgeted direct action.

**A worktree is a writer; the branch is the workstream.** The durable unit is
the branch — one branch per workstream, one session per workstream. A worktree
is only the checkout that lets you write to that branch: ephemeral, disposable,
recreatable at any time with `git worktree add`. Losing a worktree loses nothing
the branch does not still hold.

**One branch and worktree per independently reviewable PR outcome** — not per
ticket, per refactor step, or per experiment. Several related tickets may share
one worktree when a single coherent change satisfies them (`Refs #A`,
`Refs #B`); see "One Outcome, One PR" above for everything that outcome owes
and keeps bundled in that same worktree and PR.

**Experiments stay session-local.** Promote an experiment to a branch and
worktree only once its result is accepted for implementation.

🔴 **A file-mutating subagent dispatch declares `isolation: "worktree"`. That is
the only sanctioned mechanism, and the PM never tells the agent to hand-roll one
— not a literal `git worktree add`, and not prose like "work in a worktree of
your own" (#5649).** The harness provisions the tree and puts the agent in it. A
hand-rolled worktree is invisible to `tm hook --pm-guard`, which reads the
declared `isolation` parameter and never the prompt — so an agent that made its
own tree still counts as occupying the shared HEAD, and the next file-mutating
dispatch is denied for a collision that does not exist. The prose form recurred
in a different project on 2026-09-09, after the literal-command form was fixed
here — the guard cannot see either phrasing, so both are banned, regardless of
wording.

**When `isolation` is unavailable, the PM serializes.** Dispatch one
file-mutating agent, wait for it, dispatch the next. Serializing is always
available and always correct. Hand-rolling a worktree in order to parallelize
anyway is what this rule forbids.

**A brief that needs a named branch names the BRANCH, never a second worktree
(#8337).** An isolated agent that runs `git worktree add` for a ticket-named
path gets the tree, then every Edit, `git -C`, `--git-dir` and removal against
it is refused: Claude Code pins the agent to its assigned worktree, and no
`tm` hook can move that pin. The sanctioned pattern: the agent stays in its
assigned worktree and creates the target-convention local branch there,
tracking the remote branch (`git checkout -b DE-2854 --track origin/DE-2854`),
then pushes it. When the repository needs the directory itself to carry the
ticket name, the PM creates that worktree before dispatch and serializes the
work into it; the agent never creates it.

The dispatch still forbids leaving the assigned tree into the main checkout, and
forbids `git reset --hard`, `git checkout .`, and `git stash` against main.

**Who counts as file-mutating** (#5650): every engineer-tier agent, plus
`documentation` and the three QA agents that author tests — `qa`, `web-qa`,
`api-qa`. Each gets its own worktree, same as an engineer. A dispatch that only
reads does not: `research`, `code-analyzer`, `ticketing`, and `code-critic`,
which shares `role: qa` with the QA writers but recommends rather than edits.
The guard reads this from the bundled agent's own frontmatter
(`dispatch_isolation.rs`), so a custom or project-local agent it does not ship is
never denied — declare `isolation: "worktree"` for one that writes.

🔴 **`version-control` is the one writer that keeps the checkout it is given
(ADR-0056, owner ruling 2026-08-31).** It merges into main and reclaims merged
worktrees, and neither can be done from inside a worktree — isolating it removes
the tree the work needs. The guard therefore neither diverts it in a main
checkout nor denies it beside another writer. It still counts as an OCCUPANT: an
engineer dispatched into the same directory beside it is denied exactly as
before, and engineer source-write confinement is untouched.

🔴 **Cleanup after merge belongs to `version-control` or the PM, never to the
agent that did the work (#5791, ADR-0056).** An engineer reports the merged PR,
the worktree path, and the branch, then stops. `version-control` — or the PM —
confirms `state: MERGED` per PR and reclaims the trees:

```bash
tm session prune-worktrees --merged-prs            # preview, the default
tm session prune-worktrees --merged-prs --force    # reclaim
```

That pass spares any worktree still holding unsaved work, still claimed by a
managed session, or still owned by a live agent, and it reports each one it
spared with the reason. It removes the checkout only — the local branch stays,
and the remote branch is usually already gone via `gh pr merge --delete-branch`.
`tm hook --pm-guard` denies an agent-side `git worktree remove` and names this
command as the remedy, so an agent that reaches for it gets redirected rather
than silently blocked. BASE-AGENT's Git Workflow section states the agent's half.

Two guards made this undelegable to any agent, and ADR-0056 lifted both for
`version-control` alone: its unisolated dispatch is no longer denied as a second
writer on the shared HEAD (#4480), and it is no longer diverted into a worktree
whose fences would keep it off the shared registry. Every other agent is still
held by both, and `git worktree remove` is still denied to all of them — the
prune pass above is the cleanup path precisely because it spares a tree that
still holds work.

**Escape hatch — a throwaway worktree.** If you genuinely need a clean tree to
run one command — a baseline check, a bisect, a build against `origin/main` —
provision a disposable one rather than clearing a checkout another session may
be writing:

```bash
git worktree add .claude/worktrees/baseline-$$ origin/main
# … do the op …
git worktree remove .claude/worktrees/baseline-$$
```

This escape hatch used to stash the main checkout, operate, then restore. The
throwaway worktree needs no main checkout at all, and a run that dies partway
through leaves every other tree exactly as it was instead of stranding
uncommitted work in a stash entry a human has to go find (#4730).

It is the PM's hatch, not an agent's. An agent does not create its own worktree
(#5649), and the guard denies it the `remove` line outright (#5791). An agent
that needs a clean tree asks the PM for one.

Project-specific worktree hazards (binary-install caveats, code-signing caches,
and the like) belong in the project's own reference docs, not here.

## Concurrent Sessions on One Repo

Another tm session may be working this repo right now. Read what it is doing
yourself — never ask the user to relay it.

1. **List, then read, before starting work that could collide.**
   `mcp__trusty-mpm__session_list` returns every managed session with `id`,
   `name`, `cwd`, `state`, and `source_id`. Match on `source_id` /
   `workspace_path`, and exclude your own id.
   `mcp__trusty-mpm__session_activity` then returns that session's raw tmux pane
   content plus `runtime_active`, `pending_decision`, and `state` — its merged
   commits, its running agent and elapsed time, its queued work, and its open
   questions, without asking it anything.
2. **Claim the work on the issue before you start.** Assign yourself, or comment
   the claim. A claim on the tracker survives a relaunch and both sessions
   already read it; a pane message survives neither.
3. **Verify every `session_send`.** It types characters into a pane; it does not
   deliver a message. A long single-line message landed as a bracketed paste
   (`[Pasted text #1][Pasted text #2]`) and the submit never fired — the text sat
   unsent in the target's prompt buffer and the receiving session never saw it.
   After each send, call `session_activity` and confirm the message was submitted
   rather than left in the buffer.
4. **Re-read rather than wait for a reply.** Call `session_activity` again. The
   other session may be mid-agent-run for many minutes and owes you no answer.

`session_proxy_focus` / `session_proxy_message` / `session_proxy_summary` address
a session by a stored focus key instead of an id. `session_proxy_message` is the
same pane injection as `session_send` and carries the same defect.
`session_proxy_summary` gives a lifecycle digest without the raw pane.

## Git Security Review (Mandatory Before Push)

Before any `git push`, delegate a credential scan to the `security` agent.

🔴 **`git status --porcelain` must read empty before you run any gate below
(#7739).** A gate run against a dirty tree proves the working copy, not the
ref a push actually ships; commit every post-rebase edit first, so the ref
gated is the ref pushed.

0. **Verify the base ref before anything diffs it (#7748).** Every range below
   reads the LOCAL `refs/remotes/origin/main`, which a checkout that has not
   fetched can carry hundreds of commits behind — twice on 2026-09-13, and one
   of those would have scanned ~1,270 unrelated paths. Compare `git rev-parse
   refs/remotes/origin/main` with `git ls-remote origin refs/heads/main`, and
   `git fetch origin main` when they disagree. A comparison that cannot be made
   (no remote, no network) refuses the scan naming both shas; it never reports
   the scan as passed. `tm pr open` runs this same gate over its own diffs.

1. **`gitleaks` is the primary check**: `gitleaks git --log-opts="origin/main...<branch>"`
   (verified working, gitleaks 8.30.1). Three-dot, same reason as below — it
   scopes the scan to commits your branch added. Caveat: a three-dot range
   passed to `git log`/`--log-opts` is symmetric-difference, not merge-base-to-HEAD
   — it can also surface commits that only exist on `main`'s side. Treat any
   hit outside your own commits as a false positive to confirm, not a block.
2. `git diff origin/main...HEAD` is a **secondary** signal — the diff about to
   be pushed. Three-dot, because it diffs from the merge base and shows only
   what YOUR branch changed. Two-dot compares the two commits, so files DELETED
   from `main` since your branch point come back as your additions: a measured
   run reported 19 hits that were another PR's deletions where three-dot
   reported zero across 36 files. A scan people learn to wave through is where
   a real secret hides.
3. **`detect-secrets` run against paths, not diff text, is a silent no-op on a
   branch that isn't checked out** — it scans the working tree on disk, so a
   scan invoked against a branch other than the one currently checked out finds
   nothing and reports clean regardless of content.
4. **The sandbox refuses any path containing the word "secrets"** — a
   `detect-secrets` invocation naming its own tool or config path can trip this
   and abort before it scans anything. Route the scan through `gitleaks`
   instead when that happens.
5. `security` scans the gitleaks output and the diff for API keys, passwords,
   private keys, and tokens, and returns either clean or the list of blocked
   items.
6. **Block the push if secrets are detected.** A leaked credential in git history
   survives the commit being reverted.

## Branch Protection

All pushes to `main`/`master` require a feature branch + PR — no exceptions,
including "trivial" docs/chore/typo work (which may skip the linked-issue step
but still requires branch + PR + squash-merge). The only bypass is release
tooling (version-bump / lockfile-update commits per the release workflow), and
only for those commit types.

When a user asks to "commit to main" / "push to main" / "merge to main",
proactively translate: "Creating feature branch workflow instead" — don't wait
for a git error to correct course.

## Changelog Requirement (Before Merge)

Changelogs go stale when updating them is left to "sometime at release", so it is
part of every PR. Every PR that changes a package's source records one bullet per
user-visible change. Docs-only / CI-only PRs may skip this.

**Prefer a per-PR fragment file.** When the package has a `changelog.d/`
directory, that is where the entry goes:

```
<package>/changelog.d/<issue-or-pr-number>-<short-slug>.md

Fixed   <- line 1: Breaking|Added|Fixed|Performance|Changed|Removed|Security|Documentation

- the bullet text, in the package CHANGELOG's existing style
```

One new file per PR, named after the issue/PR number, means two concurrent PRs
never touch the same lines. A shared `## [Unreleased]` section guarantees a
conflict instead. Release time assembles the fragments into `CHANGELOG.md` and
deletes them; never hand-edit `CHANGELOG.md` in a package that uses fragments.

The file goes DIRECTLY in `changelog.d/`, never a subdirectory; a `README.md`
already sitting there is the directory's placeholder, not a fragment to copy.
One category per fragment — the first line IS the category and everything after
it belongs to it, so a second category word inside the body is a gate failure
and two categories mean two files (#7287).

If the package has no `changelog.d/`, add the bullet to `CHANGELOG.md` under the
topmost `## [Unreleased]` heading (create it if absent), matching the file's
existing style.

- A PR that changes source and lands without a matching changelog entry is a
  **review-gate failure** — the same tier as a failing build/test/lint gate.
  Treat it exactly like CB#8: block the merge, delegate back to the engineer,
  don't wave it through as "trivial."
- Self-check with `bash scripts/check_changelog_fragment.sh` before pushing —
  the same script CI and the pre-commit hook run, so a nonzero exit here is the
  same finding CI would report, just earlier.
- If the project also runs automated changelog generation at release time, check
  the project's root `CLAUDE.md` for which mechanism owns `CHANGELOG.md` before
  assuming they coexist. Two writers to one file is a defect; do not invent a
  precedence rule on the fly.

## Minimal PR Body (nine fields)

The `version-control` agent writes this; the PM supplies the material.
`tm pr open` checks each field against its own heading below, verbatim
(`#7727` — every heading must be an ATX `## ` line, one to six `#`s, matched
case-insensitively against the canonical word or a stated alias):

🔴 A rung 1 (docs-only) branch needs `tm pr open --docs-only` explicitly — it
runs the changelog-fragment gate and refuses a docs-only PR without the flag.

🟡 A failure naming a missing heading prints the whole skeleton verbatim, ready
to paste and fill (#7574). On a project whose own `CLAUDE.md` names a different
PR-body standard, `--minimal` skips these nine headings and nothing else —
the attribution footer, the `Refs`/`Closes` rule and the changelog-fragment gate
all still run (#7615). It is never the answer on THIS repo, whose standard these
nine fields are.

1. `## Outcome` — primary outcome and linked issue(s), with the
   `Refs owner/repo#N` link.
2. `## Changes` — what changed, and what is intentionally out of scope.
3. `## Risk` — risk / blast radius.
4. `## Tests` — test evidence at the applicable levels.
5. `## Baseline` — baseline/pre-existing failures and their canonical issue
   (see below).
6. `## Gates not run` — every gate the rung asked for that you did NOT run, and
   why (#7336). A provider call automatic approval refused, a suite you skipped
   for time, an `--include-ignored` run you left out.
7. `## Partial-red accounting` — each target still failing at hand-back, with
   its per-target rerun result (#7336). "This was not an unbroken green
   aggregate run", itemized.
8. `## Docs` — documentation/changelog status.
9. `## Review` — review-finding disposition: fixed here, kept on the parent,
   or separately ticketed.

🔴 **Fields 6 and 7 are answered, never omitted.** A clean run writes the single
word `none` under each — that is a valid whole section, and it is the disclosure
(#7336). Silence is not: `tm pr open` exits 2 naming the missing field, and the
line it prints says `none` is valid.

🔴 **Field 1 models `Refs`, not a closing keyword (#6895).** `Closes`, `Fixes`
and `Resolves` go in only when the project's `CLAUDE.md` permits a merge to
auto-close the issue. On a Refs-only project — one whose `CLAUDE.md` says fix
PRs use `Refs #N` and never `Closes #N` — every issue reference in the body is
`Refs`, because an issue there closes from `status:tested` after live
verification, not from the merge.

GitHub scans the WHOLE body for a closing keyword, not just field 1, and
honours one inside a negation ("does not close #N" still closed it, #5389). So
a single `Fixes #N` in any field auto-closes the issue on squash-merge before
anyone verified the fix — that is exactly what PR #6894 did to #6888. Grep the
finished body for a keyword before opening the PR and after every body edit;
`version-control` carries the command.

**PR body freshness**: when scope or claims change mid-flight, delegate the
update to `version-control` immediately rather than leaving stale assertions.

## Baseline-Failure Protocol (Red That Isn't Yours)

When a required gate goes red, establish whose red it is before doing anything
else.

1. Never turn red green by removing coverage. That hard line is stated in the
   instruction package ("Sprint, then Harden") and is the entry condition for the
   rest of this protocol.
2. Determine whether the branch caused the failure: run the same gate on the base
   branch, check the failing test's recent history, or reproduce it in isolation.
3. **Branch-caused** → fix it in this PR.
4. **Pre-existing** → the disposition is `tm-ticketing`'s to decide (`COMMENT`,
   `REOPEN`, `NEW REGRESSION`, or `NO TICKET`). Hand the run URL, SHA, command,
   and failure signature to `ticketing` and let its gate choose; a single
   unrelated red run is an observation, not automatically a ticket.
5. Report the gate result in exactly this shape:

   ```
   change-specific gates pass; <gate name> blocked by canonical issue #N
   ```

   Never report "all tests pass" while a gate is red, whoever caused it. Merge
   disposition then follows branch protection and the risk tier; a red required
   check is never merged around.

## The trusty-review Gate

Before merge, delegate to the review gate:

```
mcp__trusty-review__review_diff   # in-progress diff review
mcp__trusty-review__review_pr     # open-PR review (correctness/design/security)
```

This is CB#8's evidence for a PR-shaped completion claim — "the PR is ready"
requires a review verdict (or human approval), not just green CI.

## Squash-Merge Is Required

PRs merge via **squash-merge only** — one clean commit on `main` per PR. Delete
the feature branch immediately after. No merge commits, no rebase-merge, for PRs
landing on `main`.

```bash
tm pr merge <PR>                                  # validated body becomes the squash commit (#6808)
gh pr merge <PR> --squash --delete-branch         # fallback on a host without `tm`
```

After a squash-merge the local feature branch shows as "unmerged" to git (the
squashed commit has a different hash). That is expected, not a failed merge.

## Merge-Queue Ownership — the Procedure

The rule is in the instruction package's workflow section: one session owns a
repository's merge queue, an owner's authorization is scoped to the PRs it named,
and green required contexts alone never license a merge. This is how to run it.

**Establish who owns the queue before merging anything.** Ownership is a claim in
git/GitHub state, not an understanding between sessions. Read it from the open
PRs: `gh pr list --json number,author,assignees,isDraft,labels,headRefName`. If
another session's PRs are open on the same base branch and you were not told you
own the queue, you do not — hand your merge-ready PR to that session and report
that you routed it rather than merging.

**Per-PR pre-merge check, in this order.** Required contexts are the last gate,
not the first:

```bash
gh pr view <PR> --json isDraft,labels,assignees,reviewDecision,statusCheckRollup
gh pr view <PR> --json reviews --jq '.reviews[-3:][] | {author:.author.login,state}'
```

Stop on any of: `isDraft: true`; a hold label (`do-not-merge`, or whatever the
project uses); `reviewDecision: CHANGES_REQUESTED`; an unresolved `code-critic`
BLOCK in the PR comments. A critic verdict is a PR comment, so
`statusCheckRollup` cannot see it — read the comments
(`gh pr view <PR> --comments`) before a batch merge, once per PR, not once per
batch.

**Holding someone else's PR.** Convert it to draft (`gh pr ready --undo <PR>`) or
apply the hold label. Send the message too, for awareness — but the message is
never the hold. It loses the race; tonight's incident had a block arrive four
minutes after the merge.

**Handing the queue off.** Say which repository, which base branch, and which PRs
are in flight. The receiving session re-reads state rather than trusting the
list — the handoff is a notification, and the PRs are the record.

On a repository with `tm pr open`, `tm pr queue-check [--base main] [<pr>]` runs
this whole table itself — draft, hold label, `CHANGES_REQUESTED`, an unresolved
`code-critic` BLOCK, then the live required contexts, in that order — and exits
nonzero on the first stop condition it finds.

## Shipped Defaults on the PR

The rule is `tm-ticketing`'s, in the section
**"The Labels, Project, Milestone Standard"** — one standard over issues and
pull requests. Read it there; it is not restated here or in a
`version-control` brief.

What `tm pr open` reads to satisfy the PR half, and where each value comes from:

| Field | Source |
|---|---|
| assignee | `agents.ticketing.default_assignee` |
| `trusty-mpm` label | the convention label, on every PR |
| `ws/<session-name>` label | `$TM_SESSION_NAME`, else `tmux display-message -p '#{session_name}'` |
| component label(s) | `git diff --name-only origin/<base>...HEAD`, mapped to the workspace's crates |
| milestone, project(s) | the issue the body's first `Refs #N` names |
| attribution footer | the `attribution` key tm writes into the provisioned Claude Code settings |

The first three are attached to `gh pr create` and the command refuses to call
`gh` at all until they are in place. The next two are applied by one `gh pr
edit` after the PR exists, best-effort: a failure warns, and a PR with no
`Refs #N` or a diff no crate owns prints the line naming what was skipped. A
workstream is a **label**, never a milestone.

Each of these steps retries once before it gives up. If a field is still
missing after the retry, `tm pr open` exits 3 (`EXIT_PARTIAL`, #7869) rather
than 0 — the PR itself was created, and the printed line names its number,
URL, and the field(s) still missing, so finish them with `gh pr edit` instead
of treating the run as failed.

```bash
gh label create trusty-mpm \
  --description "Created/managed by a trusty-mpm session" --color 8250df \
  2>/dev/null || true
```

## Delegating the PR

PR creation and every later PR edit go to `version-control` — branch push,
`gh pr create`, the body, the issue-closing link, reviewers, `gh pr checks`, and
the merge. Give it: work summary, files changed, test status, the trusty-review
verdict, the canonical issue context from `ticketing`, and the shipped defaults
above. The PM constructs the brief; it never runs `gh pr` itself (P7 / CB#6).

## Customizing the Workflow

trusty-mpm has no separate "workflow engine" a project configures at runtime. The
PM's phase/gate behavior comes from the bundled instruction package, and a
project customizes it via named-section marker blocks in its root `CLAUDE.md`.
This is implemented in `core/instruction_overrides.rs`,
`core/instruction_pipeline.rs`, and `core/claude_md_sections.rs`.

### How the PM Prompt Is Assembled

The bundled PM prompt has one source of truth: the JSON manifest
`assets/instructions/pm-instruction-package.json` (schema v2), embedded at
compile time via `bundled_pm_package.rs`. It declares section order and
composition; the prose for each section is stored separately in
`assets/instructions/sections/*.md`, pulled in as `include_str!` constants
registered in the `SECTION_SOURCES` table (`core/instruction_pipeline.rs`) — a
missing section file is a compile error, not a launch-time surprise. The
nineteen marker tokens (`core/claude_md_sections.rs::section_token`), in prompt
order, are `IDENTITY`, `CORE`, `PM-ALLOWLIST`, `DELEGATION-MECHANICS`,
`AGENT-ROUTING`, `SUBAGENT-RE-ENGAGEMENT`, `PHASES`, `QA-GATE`,
`GIT-FILE-TRACKING`, `TICKETS-PRS-RELEASES`, `MESSAGES-REPORTS-SESSIONS`,
`AUTONOMOUS-EXECUTION`, `MEMORY`, `SEARCH`, `WORKFLOW`, `AGENT-DELEGATION`,
`ENFORCEMENT`, `NON-OVERRIDABLE-RULES`, and `FRAMEWORK-GUARANTEED-CONVENTIONS`.
`IDENTITY` opens the prompt, so its override replaces the opening role
statement in place (#8533).

### The Safety Core (#8533)

Every section is replaceable except a small safety core. The core is
enumerated once in code, `core/instruction_safety_core.rs::SAFETY_CORE`; a test
fails if this table and that list diverge.

<!-- safety-core:start -->
| Member | Section token | Kind |
|---|---|---|
| Memory & Instruction Sources | `CORE` | fixed section |
| Customization Surface | `CORE` | fixed section |
| Detected project stack | `CORE` | generated block |
| Memory protocol | `MEMORY` | pinned block |
| Code search protocol | `SEARCH` | pinned block |
| Agent selection | `AGENT-DELEGATION` | pinned block |
| Agent roster | `AGENT-DELEGATION` | generated block |
<!-- safety-core:end -->

- A **fixed section** declines a marker naming it. The decline is logged and
  reported, and the bundled text stays.
- A **pinned block** survives an override of its section; the project's text
  replaces the rest of that section.
- A **generated block** is computed at launch; no override can author or
  remove it.

There is no other floor: the `is_floor()`/`instruction_floor.sha256` machinery
was retired by #4286, being the appearance of a control rather than one a
project-owned `CLAUDE.md` could enforce.

At session start, `core/instruction_overrides.rs::resolve_pm_prompt` (reached via
`build_system_prompt_for*`) composes the final prompt. It is not a file a user
edits — it is composed fresh per launch.

### The One Customization Surface

A project customizes any section outside the safety core exactly one way: a named-section
marker, `<!-- TRUSTY-MPM: <TOKEN> START v=1 -->` … `<!-- TRUSTY-MPM: <TOKEN> END
-->`, in the project's root `CLAUDE.md` — the sole marker host
(`core/claude_md_sections.rs::HOST_FILES`). This replaces exactly the matching
section, nothing else.

The five legacy per-file overrides
(`.trusty-mpm/{INSTRUCTIONS,AGENT_DELEGATION,WORKFLOW,MEMORY,
PM_INSTRUCTIONS_DEPLOYED}.md`) are RETIRED (#4286) and never read. Never create
one; if a project still has one, move its contents into `CLAUDE.md` and delete
the file — `tm doctor`'s `legacy_overrides` check fails until it is gone.

`CLAUDE.md` is resident in EVERY prompt, so every line there is a standing
per-turn cost. Content needed on every prompt belongs there; content needed only
sometimes belongs in a skill, a doc, or memory.

Robustness: a missing `CLAUDE.md`, a missing marker block, or an empty marker
body all fall back silently to the bundled default — a customization attempt
never blanks a section or crashes launch.

The agent-delegation roster is DYNAMIC, not authored prose: it comes from
`deployed_roster_section` → `roster_section_from_tiers`, a union of the project
tier, `$CLAUDE_CONFIG_DIR/agents`, and `~/.claude/agents`. It renders only the
agents Claude Code does NOT load itself, because the harness injects its own
`Available agent types for the Agent tool` listing covering the rest (#4513);
under tm that difference is the `~/.claude/agents` tier, which the harness never
reads once `CLAUDE_CONFIG_DIR` points at the managed directory. The section is
non-droppable — `validate_roster` rejects a package where the roster generator is
optional or absent (#4069).

### Trigger Phrases

| User says | PM writes to |
|---|---|
| "remember/always/never/for this project" | Plain prose in `CLAUDE.md` (no marker needed) |
| "use X agent for Y" / "route/change agent" | `<!-- TRUSTY-MPM: AGENT-DELEGATION START v=1 -->` block in `CLAUDE.md` |
| "add/change workflow phase" | `<!-- TRUSTY-MPM: WORKFLOW START v=1 -->` block in `CLAUDE.md` |
| "stop and check with me more/less often" | `<!-- TRUSTY-MPM: AUTONOMOUS-EXECUTION START v=1 -->` block in `CLAUDE.md` — e.g. tightening it to "ask before dispatching after a resume" (#8361) |
| "memory behavior" | `<!-- TRUSTY-MPM: MEMORY START v=1 -->` block in `CLAUDE.md` |
| "this project's voice / output style" | `.claude/output-styles/<id>.md`, selected by `[style] active = "<id>"` in `.trusty-mpm.toml` |

After writing an override, confirm the marker to the user and note it "takes
effect at next session startup" — the resolved prompt is assembled at
session-prepare time, not hot-reloaded.

### Inspecting the Resolved Prompt

```bash
tm sessions instructions       # prints the resolved prompt on stdout
cat .trusty-mpm/last-instructions.md   # the exact stash resolve_pm_prompt wrote
```

`tm sessions instructions` reports every applied, declined, and shadowed marker
on **stderr**, so `tm sessions instructions >/dev/null` alone answers "why didn't
my override apply?". Below the markers it prints one row per section — `core`,
`overridable`, or `overridden-by-project` — and the output style the launch
uses (#8533).

### Output Style Selection (#8533)

The style id resolves in this order: `tm launch --style <id>`, then
`[style] active` in the committed `.trusty-mpm.toml`, then the host config's
`[style] active`, then the harness manifest. A bundled id means the shipped
style; any other id names `.claude/output-styles/<id>.md` in the project. An
unknown or unreadable id is a visible warning, and the launch falls back to the
default style.

A project style keeps its prose, and the launch appends the floor to it: the
bundled style's **PRIMARY DIRECTIVE** and **Communication — Write Plainly**
sections. The floor is not overridable, and `tm sessions instructions` names
the style `<id> (project) + floor`. The launch writes that composite to
`.claude/output-styles/<id>.tm-floor.md` and names it in `outputStyle`, so a
bare `claude` in the project gets the floor too. `last-instructions.md` is written by `prepare_session` every
time a prompt is assembled, so the inspectable copy can never diverge from what
the PM received (#382).

## Related Skills

- `tm-ticketing` — whether an issue exists, and everything about its content and lifecycle
- `tm-git-file-tracking` — files must be tracked on the feature branch before PR creation
- `tm-delegation-patterns` — the agent-selection matrices this workflow routes into
- `tm-circuit-breaker` — CB#5 (delegation chain), CB#6 (forbidden `gh` usage), CB#8 (QA/review gate)
- `tm-verification-protocols` — the QA evidence standard every phase gate uses
- `tm-agent-architecture` — how the agents this workflow delegates to are built