@deftai/directive-content 0.55.0

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 (250) hide show
  1. package/.agents/skills/deft/SKILL.md +6 -0
  2. package/.agents/skills/deft-directive-article-review/SKILL.md +11 -0
  3. package/.agents/skills/deft-directive-build/SKILL.md +10 -0
  4. package/.agents/skills/deft-directive-cost/SKILL.md +12 -0
  5. package/.agents/skills/deft-directive-debug/SKILL.md +13 -0
  6. package/.agents/skills/deft-directive-gh-arch/SKILL.md +11 -0
  7. package/.agents/skills/deft-directive-gh-slice/SKILL.md +10 -0
  8. package/.agents/skills/deft-directive-glossary/SKILL.md +10 -0
  9. package/.agents/skills/deft-directive-interview/SKILL.md +9 -0
  10. package/.agents/skills/deft-directive-pre-pr/SKILL.md +9 -0
  11. package/.agents/skills/deft-directive-refinement/SKILL.md +10 -0
  12. package/.agents/skills/deft-directive-release/SKILL.md +12 -0
  13. package/.agents/skills/deft-directive-review-cycle/SKILL.md +10 -0
  14. package/.agents/skills/deft-directive-setup/SKILL.md +10 -0
  15. package/.agents/skills/deft-directive-swarm/SKILL.md +10 -0
  16. package/.agents/skills/deft-directive-sync/SKILL.md +9 -0
  17. package/.agents/skills/deft-directive-triage/SKILL.md +9 -0
  18. package/.agents/skills/deft-directive-write-skill/SKILL.md +9 -0
  19. package/LICENSE.md +22 -0
  20. package/QUICK-START.md +167 -0
  21. package/UPGRADING.md +517 -0
  22. package/coding/build-output.md +28 -0
  23. package/coding/coding.md +235 -0
  24. package/coding/debugging.md +110 -0
  25. package/coding/holzmann.md +96 -0
  26. package/coding/hygiene.md +127 -0
  27. package/coding/security.md +158 -0
  28. package/coding/testing.md +162 -0
  29. package/coding/toolchain.md +44 -0
  30. package/commands.md +300 -0
  31. package/context/context.md +57 -0
  32. package/context/deterministic-split.md +67 -0
  33. package/context/examples.md +26 -0
  34. package/context/fractal-summaries.md +69 -0
  35. package/context/long-horizon.md +46 -0
  36. package/context/spec-deltas.md +177 -0
  37. package/context/tool-design.md +34 -0
  38. package/context/working-memory.md +62 -0
  39. package/contracts/boundary-maps.md +65 -0
  40. package/contracts/deterministic-questions.md +59 -0
  41. package/contracts/hierarchy.md +77 -0
  42. package/conventions/content-manifest.json +399 -0
  43. package/conventions/machine-generated-banner.md +130 -0
  44. package/conventions/references.md +120 -0
  45. package/conventions/rule-ownership.json +382 -0
  46. package/conventions/task-caching.md +43 -0
  47. package/conventions/vbrief-filenames.md +70 -0
  48. package/deployments/README.md +37 -0
  49. package/deployments/agentuity/README.md +138 -0
  50. package/deployments/agentuity/via-cli.md +380 -0
  51. package/deployments/agentuity/via-cloud.md +425 -0
  52. package/deployments/agentuity/via-github-actions.md +664 -0
  53. package/deployments/agentuity/via-gravity-network.md +606 -0
  54. package/deployments/agentuity/via-vpc.md +607 -0
  55. package/deployments/aws/README.md +38 -0
  56. package/deployments/aws/via-app-runner.md +612 -0
  57. package/deployments/aws/via-ecs-fargate.md +561 -0
  58. package/deployments/aws/via-elastic-beanstalk.md +628 -0
  59. package/deployments/aws/via-lambda.md +649 -0
  60. package/deployments/azure/README.md +37 -0
  61. package/deployments/azure/via-aks.md +390 -0
  62. package/deployments/azure/via-app-service.md +564 -0
  63. package/deployments/azure/via-container-apps.md +599 -0
  64. package/deployments/azure/via-functions.md +552 -0
  65. package/deployments/cloud-gov/README.md +63 -0
  66. package/deployments/cloud-gov/agents/compliance-docs.md +154 -0
  67. package/deployments/cloud-gov/agents.md +39 -0
  68. package/deployments/cloud-gov/cicd.md +64 -0
  69. package/deployments/cloud-gov/deployment.md +150 -0
  70. package/deployments/cloud-gov/logging.md +43 -0
  71. package/deployments/cloud-gov/manifest.md +121 -0
  72. package/deployments/cloud-gov/overview.md +58 -0
  73. package/deployments/cloud-gov/security.md +46 -0
  74. package/deployments/cloud-gov/services.md +72 -0
  75. package/deployments/cloud-gov/upstream/README.md +18 -0
  76. package/deployments/cloudflare/README.md +33 -0
  77. package/deployments/cloudflare/via-dashboard.md +83 -0
  78. package/deployments/cloudflare/via-git.md +90 -0
  79. package/deployments/cloudflare/via-github-actions.md +185 -0
  80. package/deployments/cloudflare/via-terraform.md +157 -0
  81. package/deployments/cloudflare/via-wrangler.md +165 -0
  82. package/deployments/fly-io/README.md +37 -0
  83. package/deployments/fly-io/via-dockerfile.md +648 -0
  84. package/deployments/fly-io/via-flyctl.md +653 -0
  85. package/deployments/fly-io/via-github-actions.md +695 -0
  86. package/deployments/fly-io/via-multi-region.md +598 -0
  87. package/deployments/google/README.md +34 -0
  88. package/deployments/google/via-app-engine.md +42 -0
  89. package/deployments/google/via-cloud-functions.md +23 -0
  90. package/deployments/google/via-cloud-run.md +330 -0
  91. package/deployments/google/via-gke.md +23 -0
  92. package/deployments/netlify/README.md +99 -0
  93. package/deployments/netlify/via-cli.md +17 -0
  94. package/deployments/netlify/via-functions.md +19 -0
  95. package/deployments/netlify/via-git.md +25 -0
  96. package/deployments/vercel/README.md +90 -0
  97. package/deployments/vercel/via-api.md +16 -0
  98. package/deployments/vercel/via-cli.md +17 -0
  99. package/deployments/vercel/via-git.md +24 -0
  100. package/docs/BROWNFIELD.md +179 -0
  101. package/docs/getting-started.md +137 -0
  102. package/docs/good-agents-md.md +137 -0
  103. package/events/README.md +89 -0
  104. package/events/event-record.schema.json +26 -0
  105. package/events/registry.json +166 -0
  106. package/events/registry.schema.json +71 -0
  107. package/glossary.md +145 -0
  108. package/incidents/README.md +81 -0
  109. package/incidents/_template.md +38 -0
  110. package/interfaces/cli.md +104 -0
  111. package/interfaces/rest.md +212 -0
  112. package/interfaces/tui.md +242 -0
  113. package/interfaces/web.md +123 -0
  114. package/languages/6502-DASM.md +132 -0
  115. package/languages/c.md +235 -0
  116. package/languages/commands.md +23 -0
  117. package/languages/cpp.md +132 -0
  118. package/languages/csharp.md +259 -0
  119. package/languages/dart.md +183 -0
  120. package/languages/delphi.md +218 -0
  121. package/languages/elixir.md +208 -0
  122. package/languages/go.md +78 -0
  123. package/languages/java.md +278 -0
  124. package/languages/javascript.md +163 -0
  125. package/languages/julia.md +175 -0
  126. package/languages/kotlin.md +193 -0
  127. package/languages/markdown.md +168 -0
  128. package/languages/mermaid.md +146 -0
  129. package/languages/officejs.md +392 -0
  130. package/languages/python.md +209 -0
  131. package/languages/r.md +163 -0
  132. package/languages/rust.md +216 -0
  133. package/languages/sql.md +216 -0
  134. package/languages/swift.md +153 -0
  135. package/languages/typescript.md +132 -0
  136. package/languages/vba.md +279 -0
  137. package/languages/vhdl.md +180 -0
  138. package/languages/visual-basic.md +151 -0
  139. package/languages/zig.md +196 -0
  140. package/meta/SOUL.md +27 -0
  141. package/meta/code-field.md +44 -0
  142. package/meta/morals.md +40 -0
  143. package/meta/philosophy.md +39 -0
  144. package/meta/project.md +49 -0
  145. package/meta/ralph.md +223 -0
  146. package/meta/security.md +80 -0
  147. package/meta/versioning.md +326 -0
  148. package/package.json +22 -0
  149. package/packs/lessons/lessons-pack-0.1.json +553 -0
  150. package/packs/patterns/patterns-pack-0.1.json +57 -0
  151. package/packs/rules/rules-pack-0.1.json +4767 -0
  152. package/packs/skills/skills-pack-0.1.json +262 -0
  153. package/packs/strategies/strategies-pack-0.1.json +167 -0
  154. package/packs/swarm-spec/swarm-spec-pack-0.1.json +17 -0
  155. package/patterns/executor-layer-credentials.md +227 -0
  156. package/patterns/llm-app.md +156 -0
  157. package/patterns/multi-agent.md +278 -0
  158. package/patterns/prompt-assembly-layer-ordering.md +154 -0
  159. package/patterns/role-as-overlay.md +179 -0
  160. package/platforms/2600.md +137 -0
  161. package/platforms/unity.md +329 -0
  162. package/references/composer-skill-porting.md +152 -0
  163. package/references/cost-models.md +163 -0
  164. package/references/ip-risk.md +246 -0
  165. package/references/plain-english-ux.md +275 -0
  166. package/resilience/context-pruning.md +67 -0
  167. package/resilience/continue-here.md +62 -0
  168. package/scm/changelog.md +276 -0
  169. package/scm/git.md +139 -0
  170. package/scm/github.md +265 -0
  171. package/secrets/.gitkeep +0 -0
  172. package/skills/deft-build/SKILL.md +20 -0
  173. package/skills/deft-directive-article-review/SKILL.md +156 -0
  174. package/skills/deft-directive-build/SKILL.md +302 -0
  175. package/skills/deft-directive-cost/SKILL.md +201 -0
  176. package/skills/deft-directive-debug/SKILL.md +140 -0
  177. package/skills/deft-directive-decompose/SKILL.md +96 -0
  178. package/skills/deft-directive-gh-arch/SKILL.md +160 -0
  179. package/skills/deft-directive-gh-slice/SKILL.md +199 -0
  180. package/skills/deft-directive-glossary/SKILL.md +118 -0
  181. package/skills/deft-directive-interview/SKILL.md +528 -0
  182. package/skills/deft-directive-pre-pr/SKILL.md +131 -0
  183. package/skills/deft-directive-probe/SKILL.md +127 -0
  184. package/skills/deft-directive-refinement/SKILL.md +403 -0
  185. package/skills/deft-directive-release/SKILL.md +266 -0
  186. package/skills/deft-directive-review-cycle/SKILL.md +401 -0
  187. package/skills/deft-directive-setup/SKILL.md +717 -0
  188. package/skills/deft-directive-swarm/SKILL.md +989 -0
  189. package/skills/deft-directive-sync/SKILL.md +288 -0
  190. package/skills/deft-directive-triage/SKILL.md +137 -0
  191. package/skills/deft-directive-write-skill/SKILL.md +169 -0
  192. package/skills/deft-interview/SKILL.md +16 -0
  193. package/skills/deft-pre-pr/SKILL.md +16 -0
  194. package/skills/deft-review-cycle/SKILL.md +16 -0
  195. package/skills/deft-roadmap-refresh/SKILL.md +16 -0
  196. package/skills/deft-setup/SKILL.md +20 -0
  197. package/skills/deft-swarm/SKILL.md +16 -0
  198. package/skills/deft-sync/SKILL.md +16 -0
  199. package/strategies/README.md +83 -0
  200. package/strategies/artifact-guards.md +85 -0
  201. package/strategies/bdd.md +115 -0
  202. package/strategies/brownfield.md +7 -0
  203. package/strategies/discuss.md +129 -0
  204. package/strategies/emit-hints.md +69 -0
  205. package/strategies/enterprise.md +193 -0
  206. package/strategies/interview.md +551 -0
  207. package/strategies/map.md +179 -0
  208. package/strategies/probe.md +151 -0
  209. package/strategies/rapid.md +155 -0
  210. package/strategies/research.md +155 -0
  211. package/strategies/roadmap.md +9 -0
  212. package/strategies/speckit.md +437 -0
  213. package/strategies/v0-20-contract.md +134 -0
  214. package/strategies/yolo.md +169 -0
  215. package/swarm/swarm.md +300 -0
  216. package/templates/COST-ESTIMATE.md +114 -0
  217. package/templates/PULL_REQUEST_TEMPLATE.md +35 -0
  218. package/templates/agent-prompt-preamble.md +409 -0
  219. package/templates/agents-entry.md +211 -0
  220. package/templates/agents-entry.placeholders.md +75 -0
  221. package/templates/embed.go +20 -0
  222. package/templates/embed_test.go +36 -0
  223. package/templates/make-spec-example.md +9 -0
  224. package/templates/make-spec.md +246 -0
  225. package/templates/project.md.template +52 -0
  226. package/templates/specification.md +1 -0
  227. package/templates/swarm-greptile-poller-prompt.md +556 -0
  228. package/templates/user.md.template +31 -0
  229. package/tools/RWLDL.md +80 -0
  230. package/tools/greptile.md +141 -0
  231. package/tools/installer.md +23 -0
  232. package/tools/taskfile-migration.md +32 -0
  233. package/tools/taskfile.md +185 -0
  234. package/tools/telemetry.md +285 -0
  235. package/vbrief/schemas/cache-meta.schema.json +137 -0
  236. package/vbrief/schemas/candidates.schema.json +130 -0
  237. package/vbrief/schemas/codebase-map.schema.json +213 -0
  238. package/vbrief/schemas/lessons-pack.schema.json +134 -0
  239. package/vbrief/schemas/patterns-pack.schema.json +84 -0
  240. package/vbrief/schemas/rules-pack.schema.json +105 -0
  241. package/vbrief/schemas/skills-pack.schema.json +94 -0
  242. package/vbrief/schemas/slices.schema.json +87 -0
  243. package/vbrief/schemas/strategies-pack.schema.json +89 -0
  244. package/vbrief/schemas/swarm-spec-pack.schema.json +84 -0
  245. package/vbrief/schemas/vbrief-core.schema.json +1022 -0
  246. package/vbrief/vbrief.md +684 -0
  247. package/verification/integration.md +76 -0
  248. package/verification/plan-checking.md +85 -0
  249. package/verification/uat.md +60 -0
  250. package/verification/verification.md +117 -0
@@ -0,0 +1,278 @@
1
+ <!-- AUTO-GENERATED by task packs:render -- DO NOT EDIT MANUALLY -->
2
+ <!-- Purpose: rendered pattern -->
3
+ <!-- Source of truth: packs/patterns/patterns-pack-0.1.json -->
4
+ <!-- Regenerate with: task packs:render -->
5
+ <!-- Edit the source, not this file. Slice instead of loading every pattern: task packs:slice patterns by-trigger --trigger <kw> (or list) -->
6
+
7
+ # Multi-agent identity separation pattern (#983)
8
+
9
+ > Status: stable pattern, v1 docs-only. v1 deliberately scopes to a single
10
+ > shared bot/App credential for all workers; per-worker token rotation is
11
+ > deferred to a follow-up. Per #983 non-goals.
12
+
13
+ This pattern lays out *whose* GitHub identity dispatched swarm workers
14
+ should authenticate as, *why* that should not be the maintainer's PAT,
15
+ and *how* operators provision and rotate the worker credential.
16
+
17
+ It is the credential-separation track that was carved out of #976
18
+ (`fix(scm,tests): eliminate remaining GraphQL-backed scm.py / live SCM
19
+ smoke bucket drain after #961`). #976 owns the REST migration; this
20
+ pattern owns the orthogonal identity separation. The two tracks are
21
+ complementary: REST migration reduces GraphQL demand on the shared
22
+ bucket, identity separation prevents human and machine traffic from
23
+ sharing one bucket in the first place.
24
+
25
+ ## Why this matters
26
+
27
+ Today, swarm workers and the maintainer share a single GitHub Personal
28
+ Access Token (PAT). One PAT serves the maintainer's interactive review
29
+ flow AND every worker spawned by `task swarm:*` / `start_agent` /
30
+ `oz agent run`. The operational consequences observed in the
31
+ 2026-05-07 multi-agent session and again on 2026-05-08 are concrete:
32
+
33
+ - **Bucket coupling**: GitHub's GraphQL bucket is **5,000 req/hr per
34
+ identity**. A swarm of N workers + the maintainer's terminal share
35
+ one bucket. When workers exhaust it (the documented failure mode for
36
+ the PR #652 / #561-class merge cascades), the maintainer's
37
+ `gh issue create` and `gh pr view` start failing with
38
+ `GraphQL: API rate limit already exceeded` -- the human is rate-
39
+ limited by their own swarm.
40
+ - **Audit conflation**: every commit, comment, PR open, issue close,
41
+ and review submission attributes to the same `actor.login`. Forensic
42
+ separation ("which action was the maintainer vs. an autonomous
43
+ worker?") requires correlating timestamps and dispatch-envelope
44
+ prompts rather than reading the GitHub event stream directly.
45
+ - **Credential blast radius**: a worker's prompt envelope is by
46
+ definition reviewable / loggable / inspectable -- prompts leak more
47
+ readily than secrets. Today a leaked worker prompt acts with the
48
+ full scope of the maintainer's PAT (issue close, PR merge, release
49
+ publish, branch protection bypass), not the narrow scope a worker
50
+ actually needs (issues:write, pulls:write, contents:read).
51
+
52
+ Reducing GraphQL demand (#976 REST migration) only solves bucket
53
+ *pressure*. It does not solve bucket *coupling*, audit conflation, or
54
+ blast radius. Those require partitioning the identity itself.
55
+
56
+ ## The pattern
57
+
58
+ ### Identity model
59
+
60
+ - ! Workers MUST authenticate as a **distinct GitHub identity** from
61
+ the maintainer. Two acceptable shapes:
62
+ 1. **GitHub App installation token** (preferred). The deft project
63
+ installs a GitHub App granting the minimum scopes; workers
64
+ consume an installation token derived from the App's private key.
65
+ Tokens are short-lived (~1 hour) and auto-rotated by GitHub.
66
+ 2. **Bot account PAT**. A dedicated GitHub user account (e.g.
67
+ `deft-swarm-bot`) with its own PAT. Lower setup cost, but tokens
68
+ are long-lived until manually rotated, and the bot account
69
+ consumes a real GitHub seat.
70
+ - ! Maintainer PATs MUST be reserved for human-driven work: review,
71
+ merge, release publication, manual triage. Worker dispatch envelopes
72
+ MUST NOT inject a maintainer PAT.
73
+ - ⊗ Workers MUST NOT fall back to the host's `gh auth status` token.
74
+ The dispatch envelope is the contract; an implicit fallback re-
75
+ introduces the coupling this pattern eliminates.
76
+
77
+ ### Bucket partitioning model
78
+
79
+ | Bucket | Owner | Use |
80
+ |----------------------|----------------------|----------------------------------------------------|
81
+ | Maintainer GraphQL | Maintainer PAT | Human review, PR open/merge/ready, manual triage |
82
+ | Maintainer core REST | Maintainer PAT | `gh api` reads outside swarm context |
83
+ | Worker GraphQL | Bot account / App | `markPullRequestReadyForReview`, `addPullRequestReview` (the only GraphQL surfaces a worker should hit; see #976 + #961) |
84
+ | Worker core REST | Bot account / App | All other reads / mutations via `gh_rest.py` REST helpers |
85
+
86
+ Each bucket is 5,000 req/hr (REST `core` and GraphQL each). With
87
+ identity separation the maintainer's two buckets are untouched by
88
+ swarm activity, and N workers share *their own* two buckets.
89
+
90
+ The bucket partition only delivers value when paired with the #976
91
+ REST migration: workers that still route reads through GraphQL
92
+ (`gh issue view --json`, `gh pr view --json`, `gh pr ready`) will
93
+ exhaust their own GraphQL bucket within minutes under N concurrent
94
+ poll loops. REST-by-default per `templates/agent-prompt-preamble.md`
95
+ S5 keeps the worker GraphQL bucket reserved for the unavoidable PR
96
+ state-mutation operations.
97
+
98
+ ### Dispatch-envelope credential rule
99
+
100
+ The canonical preamble at `templates/agent-prompt-preamble.md` carries
101
+ this rule verbatim alongside the existing REST-default,
102
+ max-1-Draft-toggle, and rate-limit-throttle rules:
103
+
104
+ > Workers MUST consume the GitHub credential injected by the dispatcher
105
+ > (typically `GH_TOKEN` in the prompt-supplied env). Workers MUST NOT
106
+ > fall back to the host's `gh auth status` token. If `GH_TOKEN` is
107
+ > unset and no other dispatcher-supplied credential is present, the
108
+ > worker MUST fail loud with a clear error rather than silently
109
+ > running under the host identity.
110
+
111
+ The rule is enforced at two points:
112
+
113
+ 1. **Dispatch time** (orchestrator side). The orchestrator (swarm
114
+ skill, monitor agent, scheduled run) injects the worker
115
+ credential into the dispatch envelope -- usually as `GH_TOKEN` in
116
+ the env, with the maintainer's `gh auth` state untouched. Workers
117
+ inherit this env at spawn time.
118
+ 2. **Worker side**. The worker's first action (after AGENTS.md read)
119
+ verifies `GH_TOKEN` is set and matches the expected bot/App
120
+ identity (e.g. `gh api user --jq .login` returns the bot login).
121
+ Mismatch is a hard refusal: stop the tool loop, send `BLOCKED:
122
+ identity mismatch` to the parent.
123
+
124
+ The two-sided enforcement means a missing/malformed credential surfaces
125
+ at the earliest possible point rather than mid-dispatch when the worker
126
+ has already started writing files.
127
+
128
+ ### Permission scoping
129
+
130
+ ! The worker credential MUST be scoped down to the minimum needed:
131
+
132
+ - `issues:write` -- open / close / comment / label
133
+ - `pulls:write` -- open / merge / review / comment
134
+ - `contents:read` -- read repo contents (for diff context)
135
+ - `metadata:read` -- standard for any GitHub App
136
+ - ⊗ `contents:write` -- workers MUST NOT push commits directly under
137
+ the worker identity. Pushes happen via the maintainer (interactive
138
+ review of the worker's diff) or via the GitHub App's restricted
139
+ contents:write scope on a specific bot-owned ref pattern.
140
+ - ⊗ `admin:*` / `repo` (broad) -- workers MUST NOT have admin scopes.
141
+ Branch protection bypass, label/milestone management, secret
142
+ rotation -- all maintainer-only.
143
+
144
+ The maintainer PAT keeps the broader scope it has today (`repo`,
145
+ `workflow`, etc.). The narrowness of the worker scope is half the
146
+ blast-radius mitigation; the other half is short-lived credentials
147
+ (GitHub App installation tokens auto-rotate ~1 hour; bot PATs MUST be
148
+ rotated quarterly per the runbook below).
149
+
150
+ ## Operator runbook
151
+
152
+ ### Provisioning a worker identity
153
+
154
+ #### Option A: GitHub App (preferred)
155
+
156
+ 1. Create a new GitHub App at
157
+ `https://github.com/organizations/<org>/settings/apps/new`
158
+ (account-level path: `https://github.com/settings/apps/new`).
159
+ 2. Grant the **minimum** permissions enumerated in
160
+ "Permission scoping" above. Decline every other permission.
161
+ 3. Subscribe to no events (workers consume the API; they do not need
162
+ webhook delivery).
163
+ 4. Generate and download a private key (`.pem`). Store it in
164
+ `secrets/swarm-app.pem` outside the repo (the deft `secrets/`
165
+ convention is `.gitignore`d by default per the repo conventions).
166
+ 5. Install the App on the deft repository.
167
+ 6. Note the App ID and Installation ID; both are required to mint
168
+ installation tokens at dispatch time.
169
+ 7. The dispatcher mints an installation token via
170
+ `POST /app/installations/<installation-id>/access_tokens` (the
171
+ private-key-signed JWT flow). Each minted token lives ~1 hour;
172
+ spawn-burst refreshes are cheap.
173
+
174
+ #### Option B: bot account PAT
175
+
176
+ 1. Create a new GitHub user account (e.g. `deft-swarm-bot`). Email
177
+ address can be a `+swarmbot` alias on the maintainer's account.
178
+ 2. Add the bot account as a repository collaborator with **Write**
179
+ access (NOT Maintain or Admin).
180
+ 3. Generate a fine-grained PAT under the bot account with the scopes
181
+ enumerated in "Permission scoping". Set expiry to 90 days.
182
+ 4. Store the PAT in `secrets/swarm-bot.env` as
183
+ `SWARM_GH_TOKEN=<value>`. Reference from dispatcher via
184
+ `Get-Content secrets/swarm-bot.env` or equivalent at dispatch
185
+ time; never paste inline.
186
+
187
+ ### Wiring the dispatcher
188
+
189
+ The orchestrator (swarm skill, monitor agent, `start_agent`/`oz`
190
+ invocation, scheduled run) loads the worker credential and injects it
191
+ into the worker's env:
192
+
193
+ ```pwsh path=null start=null
194
+ # Option A (GitHub App): mint installation token at dispatch time.
195
+ $jwt = uv run python scripts/swarm_mint_jwt.py --app-id $env:DEFT_SWARM_APP_ID --pem secrets/swarm-app.pem
196
+ $inst_token = (gh api -X POST "/app/installations/$env:DEFT_SWARM_INSTALLATION_ID/access_tokens" -H "Authorization: Bearer $jwt" --jq .token)
197
+ start_agent ... -e GH_TOKEN=$inst_token
198
+
199
+ # Option B (bot PAT): inject from secrets/swarm-bot.env.
200
+ $bot_token = (Get-Content secrets/swarm-bot.env | Where-Object { $_ -like 'SWARM_GH_TOKEN=*' }) -replace '^SWARM_GH_TOKEN=', ''
201
+ start_agent ... -e GH_TOKEN=$bot_token
202
+ ```
203
+
204
+ Token-mint plumbing (`scripts/swarm_mint_jwt.py`) is intentionally not
205
+ shipped in v1 -- the runbook above is operator-facing guidance, not
206
+ deft-supplied automation. v1 is documentation-only per #983 non-goals.
207
+ Operators MAY automate token minting in their own dispatcher; the
208
+ contract this pattern pins is the env-var injection.
209
+
210
+ ### Rotation and recovery
211
+
212
+ #### Routine rotation
213
+
214
+ - **GitHub App tokens**: auto-rotate ~1 hour. No operator action
215
+ needed unless the App's private key is compromised.
216
+ - **Bot PAT**: quarterly rotation. Update `secrets/swarm-bot.env`
217
+ and revoke the prior PAT. Verify the new token via
218
+ `gh api user --jq .login` returns the bot login.
219
+
220
+ #### Compromise recovery (leaked worker token)
221
+
222
+ 1. Immediately revoke the leaked credential:
223
+ - GitHub App: rotate the private key (Settings -> Apps -> Your
224
+ deft-swarm App -> Generate a new private key, then delete the
225
+ compromised key).
226
+ - Bot PAT: revoke at
227
+ `https://github.com/settings/tokens` under the bot account.
228
+ 2. Audit the bot's recent activity: `gh api
229
+ /users/<bot-login>/events/public --jq
230
+ '.[] | {repo, type, created_at}'`. Investigate any action outside
231
+ the expected swarm workflow.
232
+ 3. Provision a replacement credential per the provisioning steps
233
+ above and update the dispatcher.
234
+ 4. File an incident vBRIEF in `vbrief/proposed/` documenting the
235
+ compromise, scope of activity, and remediation. The vBRIEF
236
+ becomes the audit artifact.
237
+
238
+ The maintainer PAT MUST NOT be used as a fallback during the
239
+ compromise window -- doing so re-couples the buckets and re-introduces
240
+ the blast-radius problem this pattern solves.
241
+
242
+ ## Cross-references
243
+
244
+ - #976 -- remaining GraphQL drain in `scripts/scm.py` + smoke (the
245
+ REST-migration track this pattern complements; this pattern was
246
+ carved out of #976's "Complementary mitigation" section)
247
+ - #588 -- agent identity pattern (distinct attestable identity per
248
+ agent; the conceptual frame this pattern operationalises)
249
+ - #806 -- executor-layer-credentials (secrets bound at invocation
250
+ layer, not in agent context; the architectural pattern this
251
+ pattern lands)
252
+ - #585 -- credential hygiene standard for cloud agent dispatch
253
+ (overlapping with v1, but #585 covers the broader cloud-agent
254
+ surface; this pattern scopes specifically to swarm workers)
255
+ - #519 -- rate limiting as a first-class architectural constraint
256
+ for multi-agent guidance (the "shared bucket is the bottleneck"
257
+ framing that motivates partitioning by identity)
258
+ - #520 -- multi-provider routing as the production solution for
259
+ large-scale swarms (the LLM-side analogue of this pattern -- LLM
260
+ providers also rate-limit per identity, and the same partitioning
261
+ argument applies upstream)
262
+ - #954 / #966 -- canonical orchestrator preamble; the
263
+ `templates/agent-prompt-preamble.md` rule enforcing the
264
+ dispatch-envelope credential contract is part of #954's preamble
265
+ surface and is updated by this issue (#983)
266
+ - AGENTS.md `## Multi-agent orchestration discipline (#954)` -- the
267
+ REST-default / no-Draft-retoggle / rate-limit-throttle rules this
268
+ pattern complements
269
+
270
+ ## Non-goals (per #983)
271
+
272
+ - Per-worker token rotation infrastructure -- a single shared
273
+ bot/App credential for all workers is acceptable in v1; per-worker
274
+ identities are deferred to a follow-up.
275
+ - Cross-provider identity (Anthropic, OpenAI, etc.) -- this pattern
276
+ is GitHub-specific; the LLM-side analogue is #520.
277
+ - Replacing the SCM/REST migration -- #976 owns that track; this
278
+ pattern is complementary, not a substitute.
@@ -0,0 +1,154 @@
1
+ # Prompt assembly layer ordering (#836)
2
+
3
+ Architectural standard for agents that issue multi-turn LLM API calls
4
+ (OpenAI, Anthropic, Cohere, local models, etc.). It pins the contract
5
+ between the **cached system prefix** (assembled once at session start,
6
+ stable for the session) and the **ephemeral per-turn injections**
7
+ (rebuilt on every API call, never cached). Mixing these two layers
8
+ collapses prefix-cache effectiveness and silently multiplies token
9
+ cost by the number of turns.
10
+
11
+ Legend (from RFC2119): !=MUST, ~=SHOULD, ≉=SHOULD NOT, ⊗=MUST NOT, ?=MAY.
12
+
13
+ **Load when:** the project assembles a system prompt from more than one
14
+ fragment, or relies on provider-side prompt caching (Anthropic prompt
15
+ caching, OpenAI cache hits via stable prefix, vLLM/Anthropic-compatible
16
+ local model caches, etc.), or operates an agent across more than one
17
+ user turn per session.
18
+
19
+ **Source material:** NousResearch Hermes agent architecture
20
+ (`prompt-assembly.md`); the same separation is documented by every
21
+ major provider's prompt-caching guidance. The rule below codifies it
22
+ as a deft-side standard so agents built with directive inherit the
23
+ discipline by default.
24
+
25
+ **⚠️ See also**:
26
+ - [./llm-app.md](./llm-app.md) -- LLM application standards (#481); the **Prompt construction** and **Trust tiers** sections of that file are the foundational rules this pattern extends. The trust-tier contract is orthogonal to the cache-tier contract -- a fragment can be system-trust and still be ephemeral (e.g. a per-turn safety overlay), and a fragment can be cached and still be lower-trust (e.g. a frozen RAG snippet); both axes MUST be tracked separately
27
+ - [../context/context.md](../context/context.md) -- the **Write** and **Compress** strategies operationalise the same insight (externalise stable content; reduce per-turn re-injection)
28
+
29
+ ## The invariant
30
+
31
+ > If it changes per-turn, it is ephemeral. If it is stable for the
32
+ > session, it is cached.
33
+
34
+ - ! MUST classify every fragment that enters the prompt as either *cached prefix* or *ephemeral injection* at construction time -- a fragment that cannot be classified is ambiguous and ambiguity is itself a defect (the same posture `patterns/llm-app.md` `## Trust tiers` takes on tier classification)
35
+ - ⊗ MUST NOT place a fragment whose content varies per-turn into the cached prefix -- one variable byte invalidates the entire suffix of the cache and forces a cache miss on every turn, multiplying token cost by `N_turns`
36
+ - ⊗ MUST NOT place a fragment that is stable for the session into the ephemeral injection layer -- this re-pays the token cost on every turn for content the provider would otherwise serve from cache
37
+ - ! MUST surface the layer classification alongside the fragment in the assembled prompt log so a future reviewer can audit which fragments lived in which layer (see `## Observability` below)
38
+
39
+ ## Cached prefix -- assembled once at session start
40
+
41
+ The cached prefix is the prompt's stable, session-scoped opening. It is
42
+ constructed exactly once per session, hashed, and sent verbatim on every
43
+ turn so the provider can serve cache hits against the prefix.
44
+
45
+ Canonical content for the cached prefix:
46
+
47
+ 1. **Agent identity** -- persona / SOUL.md / system role declaration
48
+ 2. **Tool-aware behaviour guidance** -- tool catalogue, calling conventions, refusal rules
49
+ 3. **Frozen memory snapshot** -- the session-start memory excerpt (see #832); MUST NOT be edited mid-session
50
+ 4. **Skills index** -- the discoverable skills surface (file names, one-line descriptions)
51
+ 5. **Context files** -- AGENTS.md, project configuration, repository conventions
52
+ 6. **Session timestamp** -- the single timestamp recorded at session start (NOT per-turn)
53
+
54
+ - ! MUST assemble the cached prefix exactly once at session start; every subsequent API call sends the byte-identical prefix
55
+ - ! MUST hash the assembled prefix and log the hash with the session start event so cache-miss debugging is grounded in a verifiable artefact
56
+ - ⊗ MUST NOT mutate the cached prefix mid-session -- a mutation invalidates the cache for every remaining turn in the session
57
+ - ⊗ MUST NOT inject per-turn metadata (current task, current timestamp, current user message echo, current tool call result) into the cached prefix -- these are ephemeral by definition
58
+ - ~ SHOULD treat the cached prefix as code: version-controlled, code-reviewed, and re-rendered into a queryable log line on every session-start so prefix drift between deploys is visible
59
+
60
+ ### Why ordering within the cached prefix matters
61
+
62
+ Provider prefix caches are *byte-prefix* caches: the cache hit length is
63
+ the longest byte-identical prefix of the current request against the
64
+ cached entry. A byte that differs at position `k` invalidates the cache
65
+ for every byte from `k` onward. Therefore, within the cached prefix,
66
+ **fragments that change least frequently MUST come earliest**.
67
+
68
+ Canonical ordering, most-stable to least-stable:
69
+
70
+ ```text
71
+ 1. Agent identity <- changes least (frozen until next deploy)
72
+ 2. Tool-aware behaviour guidance
73
+ 3. Frozen memory snapshot <- changes only between sessions (#832)
74
+ 4. Skills index <- changes when skills are added/removed
75
+ 5. Context files <- changes per-project
76
+ 6. Session timestamp <- changes per-session (last in cached layer)
77
+ ```
78
+
79
+ - ! MUST order cached-prefix fragments most-stable-first so the cacheable byte-prefix is maximised across deploys, sessions, and projects
80
+ - ⊗ MUST NOT place a per-session fragment (e.g. session timestamp) ahead of a per-project fragment (e.g. AGENTS.md) -- the per-session change would invalidate everything below it on every new session
81
+ - ⊗ MUST NOT place a per-project fragment ahead of a per-deploy fragment (e.g. agent identity) -- the same inversion penalty applies across the deploy axis
82
+ - ~ SHOULD measure cache hit rate per turn and per session as part of the LLM-specific observability surface (see `patterns/llm-app.md` `## LLM-specific observability`); a falling cache hit rate is the canonical drift signal that ordering or layering has regressed
83
+
84
+ ## Ephemeral injection -- rebuilt on every API call
85
+
86
+ The ephemeral layer is everything the model needs *for this turn* that
87
+ the model did not need on prior turns. It is injected as part of the
88
+ per-turn user message (or as a turn-scoped system addendum where the
89
+ provider supports it) and is never cached.
90
+
91
+ Canonical content for the ephemeral layer:
92
+
93
+ - **Current-turn context overlays** -- task-scoped instructions, role overlays (see #816), per-turn safety reminders
94
+ - **Gateway-derived session context** -- request id, user id, locale, feature flags surfaced for this turn
95
+ - **Later-turn memory recall** -- memory snippets surfaced *after* session start (the frozen-snapshot rule from #832 forbids editing the cached prefix's memory; recalls go here)
96
+ - **Prefill messages** -- assistant-side prefills used to steer the next decode
97
+ - **Current user input** -- the user's turn, wrapped in the trust-tier envelope from `patterns/llm-app.md` `## Prompt construction`
98
+ - **Tool results from prior turns** -- `<tool_result>...</tool_result>` blocks injected as observations for the next decode
99
+
100
+ - ! MUST rebuild the ephemeral layer on every API call from authoritative per-turn state -- never reuse a stale serialisation across turns
101
+ - ! MUST inject later-turn memory recall into the **user message** (not the system prompt) -- writing recall into the system prompt forces a cache miss AND violates the frozen-snapshot rule from #832
102
+ - ⊗ MUST NOT splice ephemeral content into the cached prefix at construction time as an optimisation -- the optimisation reverses on the second turn and gets strictly worse from there
103
+ - ⊗ MUST NOT carry ephemeral fragments forward as cached state across turns -- a per-turn task instruction that becomes part of the prefix locks the agent into that instruction for the rest of the session
104
+ - ~ SHOULD wrap ephemeral fragments in explicit delimiters (`<turn_context>`, `<recall>`, `<tool_result>`) so the model can be instructed to treat them as turn-scoped rather than session-scoped
105
+
106
+ ## Why this matters for directive
107
+
108
+ Directive-built agents typically assemble their system prompt from
109
+ multiple sources (AGENTS.md, USER.md, project-definition narratives,
110
+ skill bodies, vBRIEF excerpts, memory snippets). Without an explicit
111
+ layering contract:
112
+
113
+ - Agent-written context-file content gets mixed into system-prompt fragments, fragmenting cache hits
114
+ - Memory updates land in the system prompt mid-session, invalidating every cached byte after the insertion point
115
+ - Per-turn metadata (timestamps, current task name, current vBRIEF being worked) gets templated into the system prompt instead of the user message
116
+ - Token cost scales `O(N_turns × prompt_size)` instead of `O(prompt_size + N_turns × delta)` -- a 10x cost regression at 10 turns/session, 100x at 100
117
+
118
+ This pattern is the structural fix. The frozen-memory-snapshot rule
119
+ (#832) is the load-bearing consequence: memory that lives in the cached
120
+ prefix MUST be frozen at session start; subsequent recall happens in
121
+ the ephemeral layer.
122
+
123
+ ## Observability
124
+
125
+ The cache-tier classification is invisible to standard request/response
126
+ tracing -- a cache miss looks like a normal request, just slower and
127
+ more expensive. The LLM-specific observability surface
128
+ (`patterns/llm-app.md` `## LLM-specific observability`) is the canonical
129
+ home for cache-tier telemetry; this pattern adds the cache-specific
130
+ fields.
131
+
132
+ - ! MUST log the cached-prefix hash on every session-start event so prefix drift between deploys, projects, or sessions is detectable post-hoc
133
+ - ! MUST log cache hit rate per turn (provider-reported where available; derived from token counts otherwise) so layering regressions surface as a falling metric, not as a silent cost increase
134
+ - ~ SHOULD log per-fragment layer classification (cached vs ephemeral) at construction time so post-hoc audits can see exactly which fragments lived in which layer
135
+ - ~ SHOULD alert on a sustained cache hit rate drop -- a cache hit rate that falls without a deploy is the canonical signal that an ephemeral fragment leaked into the cached prefix
136
+ - ⊗ MUST NOT log raw prompt bodies into general application logs (the `patterns/llm-app.md` audit-log-separation rule applies equally to cached-prefix bodies; the hash is queryable, the body lives in the audit log)
137
+
138
+ ## Anti-patterns
139
+
140
+ - ⊗ Re-rendering the cached prefix on every API call (the canonical cache-miss footgun -- the prefix is supposed to be byte-identical across turns)
141
+ - ⊗ Injecting current timestamp, current task, or current user message echo into the cached prefix (per-turn variability in a session-scoped layer)
142
+ - ⊗ Editing the frozen memory snapshot mid-session (the #832 frozen-snapshot rule has the same root cause; this pattern names it)
143
+ - ⊗ Placing per-session fragments (session timestamp) ahead of per-deploy fragments (agent identity) in the cached prefix (ordering inversion that invalidates the deploy axis)
144
+ - ⊗ Storing ephemeral per-turn role overlays (#816) into the cached system prompt instead of the user message
145
+ - ⊗ Treating prompt caching as a provider-side optimisation the application can ignore (it is a first-class application-side contract; the application controls every byte that determines the cache hit length)
146
+ - ⊗ Reporting "the prompt fits the context window" as the success metric (the success metric is `cache_hit_rate × N_turns`, not `prompt_length < context_limit`)
147
+
148
+ ## Cross-references
149
+
150
+ - `patterns/llm-app.md` -- the parent LLM application standards (#481); this file extends `## Prompt construction` and the `## LLM-specific observability` audit-log requirements with the cache-tier contract
151
+ - `context/context.md` -- the **Write** strategy externalises ephemeral state to scratchpad files; the **Compress** strategy reduces per-turn token cost; both are operationalised here for the LLM-call boundary
152
+ - #832 -- frozen-memory-snapshot rule (the load-bearing consequence of this pattern; memory in the cached prefix MUST be frozen at session start)
153
+ - #816 -- role-as-overlay (roles are ephemeral, not cached; they live in the per-turn injection layer)
154
+ - #788 -- token cost as design constraint (the parent framing this pattern enforces at the prompt-assembly layer)
@@ -0,0 +1,179 @@
1
+ # Role as overlay (#816)
2
+
3
+ Architectural standard for how persona and role instructions are applied
4
+ to LLM calls in directive's skills, agents, and downstream consumers.
5
+ Role instructions MUST be implemented as **call-scoped system-prompt
6
+ overlays**, never injected into the persisted message history.
7
+
8
+ Legend (from RFC2119): !=MUST, ~=SHOULD, ≉=SHOULD NOT, ⊗=MUST NOT, ?=MAY.
9
+
10
+ **Load when:** the project applies a persona, role, or stance to an LLM
11
+ call (skill-defined reviewer / builder / summarizer roles, agent-level
12
+ identities, per-call stance overrides) or designs a multi-turn agent
13
+ that persists message history across turns.
14
+
15
+ **Source material:** Flue SDK ([withastro/flue](https://github.com/withastro/flue))
16
+ README -- "Role instructions are applied as call-scoped system prompt
17
+ overlays, not injected into the persisted user message history."
18
+ Precedence: call role > session role > agent role.
19
+
20
+ **⚠️ See also**:
21
+ - [./llm-app.md](./llm-app.md) -- the trust-tier framework this pattern reinforces (the system prompt is the trust boundary; user-tier messages are untrusted)
22
+ - [../coding/security.md](../coding/security.md) -- `## Agent-Specific Threats` (prompt-injection isolation, internal-prompt non-exposure -- the security-side framing of the same boundary)
23
+ - [./multi-agent.md](./multi-agent.md) -- sub-agent dispatch envelope (the orchestration analogue: per-dispatch role overlay, never aggregated into shared history)
24
+
25
+ ## The principle
26
+
27
+ When a skill or agent applies a persona to an LLM call ("you are a
28
+ security reviewer", "you are a senior engineer", "you are a concise
29
+ summarizer"), that instruction is **configuration**, not **content**.
30
+ It describes *how* to process the work, not *what* work happened.
31
+
32
+ - ! MUST apply persona / role instructions as system-prompt overlays at the call boundary -- never as `user` or `assistant` messages in the persisted history
33
+ - ! MUST treat the message history as the durable record of *what happened*: user turns, tool calls, tool results, assistant turns. Role instructions are not events; they are dispositions
34
+ - ⊗ MUST NOT prepend a `user` message of the form `"You are a security reviewer. ..."` to the conversation array as a way to apply a role -- this is the canonical anti-pattern this rule eliminates
35
+ - ⊗ MUST NOT prepend a synthetic `assistant` message of the form `"Understood, I am a security reviewer. ..."` either -- mirrored variant of the same anti-pattern
36
+ - ! MUST keep the overlay **ephemeral**: it applies for the duration of the call and disappears; the next call's overlay is computed fresh from the precedence chain
37
+ - ~ SHOULD render the effective system prompt (agent base + session overlay + call overlay) into a structured audit-log entry alongside the call, per [`patterns/llm-app.md`](./llm-app.md) `## LLM-specific observability` -- the overlay is a configuration artifact and is traced as one
38
+
39
+ ## Why this matters
40
+
41
+ The failure mode is concrete and recurrent. An agent that injects role
42
+ instructions as messages accumulates them across turns:
43
+
44
+ ```text path=null start=null
45
+ # Anti-pattern: role-as-message
46
+ messages = [
47
+ {"role": "user", "content": "You are a security reviewer. Review this code."},
48
+ {"role": "user", "content": "<the actual code>"},
49
+ {"role": "assistant", "content": "<the review>"},
50
+ {"role": "user", "content": "You are a security reviewer. Now check the tests."},
51
+ {"role": "user", "content": "<the tests>"},
52
+ ...
53
+ ]
54
+ ```
55
+
56
+ Versus the correct shape:
57
+
58
+ ```text path=null start=null
59
+ # Correct: role-as-overlay
60
+ system = "<agent base prompt> + <session role> + <call role: security reviewer>"
61
+ messages = [
62
+ {"role": "user", "content": "Review this code."},
63
+ {"role": "user", "content": "<the actual code>"},
64
+ {"role": "assistant", "content": "<the review>"},
65
+ {"role": "user", "content": "Now check the tests."},
66
+ {"role": "user", "content": "<the tests>"},
67
+ ...
68
+ ]
69
+ ```
70
+
71
+ Concrete consequences of the anti-pattern:
72
+
73
+ - **History pollution.** A resuming agent (or a human reading the
74
+ transcript later) sees spurious `"you are a reviewer"` user turns
75
+ interleaved with real work. The record is no longer a faithful
76
+ description of the conversation -- it conflates *who said what* with
77
+ *how the model was configured*. Forensic review on a faulty output
78
+ cannot distinguish operator intent from runtime configuration.
79
+ - **Retrieval corruption.** If the message history feeds a downstream
80
+ retrieval index (chat search, conversation summarization, vBRIEF
81
+ ingestion, RAG re-use of prior turns), the role-injection turns
82
+ become indexable content. A search for "security reviewer" then
83
+ surfaces configuration noise instead of actual reviewer findings.
84
+ This is the persistence-layer analogue of the RAG-poisoning failure
85
+ the [`patterns/llm-app.md`](./llm-app.md) `## RAG and retrieval`
86
+ section warns against.
87
+ - **Context rot acceleration.** Role-injection turns count against the
88
+ context window every turn. Long sessions burn token budget on
89
+ repeated `"you are a reviewer"` strings that contribute zero
90
+ information beyond the first occurrence -- and the model's attention
91
+ drifts under the accumulated repetition. This is a known structural
92
+ cause of context degradation (#674).
93
+ - **False memory propagation.** When the polluted history is summarized
94
+ or compacted, the summarizer treats role-injection turns as real
95
+ content and may propagate the persona claim into the summary
96
+ ("the user repeatedly emphasized that the model is a security
97
+ reviewer"). The persona then enters a parallel persistence channel
98
+ with no provenance back to the actual configuration source. This is
99
+ the structural origin of #479 (false memory propagation in vBRIEF
100
+ trust levels).
101
+ - **Resumption breakage.** A session resumed from polluted history
102
+ re-applies the historical role-injection turns AND the fresh
103
+ overlay, doubling the role assertion. Subtle behavioral drift
104
+ follows: the model sees two `"you are a reviewer"` signals with
105
+ potentially different framings and resolves the conflict
106
+ unpredictably.
107
+
108
+ The overlay shape avoids all five failure modes by construction. The
109
+ history records work; the overlay records configuration; the two never
110
+ mix.
111
+
112
+ ## Precedence
113
+
114
+ When multiple roles apply, resolve them in this order, highest to
115
+ lowest:
116
+
117
+ ```text path=null start=null
118
+ call role <- HIGHEST (per-invocation override, the most specific intent)
119
+ |
120
+ session role <- session-scoped default (e.g. "this whole review pass is a security audit")
121
+ |
122
+ agent role <- LOWEST (the agent's baseline identity, e.g. "you are a Deft Directive skill executor")
123
+ ```
124
+
125
+ - ! MUST resolve role precedence call > session > agent at every LLM call. Lower-tier roles are defaults; higher-tier roles override without modifying the lower-tier's record
126
+ - ! MUST compose the effective system prompt as `agent_base + session_overlay + call_overlay`, in that exact order, so the call overlay is the last instruction the model reads
127
+ - ⊗ MUST NOT mutate the agent or session role from a call-level overlay -- the call overlay is ephemeral by definition; persisting it changes the precedence semantics into a "last call wins" model that is silently history-dependent
128
+ - ⊗ MUST NOT silently drop a higher-tier overlay because a lower-tier role disagrees -- the higher-tier value is authoritative; surface the conflict in the audit log if useful for debugging
129
+ - ~ SHOULD log the resolved precedence chain alongside each call: `(agent="deft-build", session=null, call="security-reviewer") -> effective="security-reviewer"`. This makes the overlay's effect on a given call queryable post-hoc
130
+
131
+ ## Implementation contract for skills and agents
132
+
133
+ Directive's skills implicitly carry roles -- `deft-directive-review-cycle`
134
+ is a reviewer, `deft-directive-build` is a builder, `deft-directive-pre-pr`
135
+ is a quality-checker. Each skill's role MUST be applied as an overlay
136
+ when the skill dispatches an LLM call (or instructs a sub-agent to do
137
+ so), never as an injected message turn.
138
+
139
+ - ! Skills that apply a role to an LLM call MUST surface the role as a system-prompt overlay parameter (or the equivalent provider-supported field) at the call boundary
140
+ - ! Skills that dispatch sub-agents MUST pass the role through the dispatch envelope as a configuration field (e.g. `role: "security-reviewer"`), not as a prepended user message in the sub-agent's seed history
141
+ - ⊗ Skills MUST NOT generate role-injection prose into the work product (PR body, commit message, scope vBRIEF narrative, lessons.md entry) -- the role is internal configuration, not output content
142
+ - ! When a sub-agent receives a role via the dispatch envelope, it MUST treat the role as authoritative call-scoped configuration; it MUST NOT persist the role string into its own conversation history as a synthetic user turn
143
+ - ~ Skill authors SHOULD document the skill's default role (agent-tier) in the SKILL.md frontmatter or a dedicated `## Role` section so consumers can override it consciously at session or call tier
144
+ - ? Skills MAY accept a `--role` override at invocation time; if accepted, the override resolves at call tier per the precedence chain above
145
+
146
+ ### Provider mapping
147
+
148
+ | Provider | System overlay surface |
149
+ |---------------------------|---------------------------------------------------------------------------------------|
150
+ | Anthropic Messages API | `system` parameter on the request (string or content-block array) |
151
+ | OpenAI Chat Completions | `messages[0]` with `role: "system"` -- one entry, never duplicated across the array |
152
+ | OpenAI Responses API | `instructions` parameter on the request |
153
+ | Google Gemini | `system_instruction` field on the request |
154
+ | Local / open-source LLMs | The provider-specific system prompt slot in the request schema |
155
+
156
+ - ! MUST use the provider's native system-prompt surface for the overlay -- do not fall back to a user-tier message because the provider's system surface "is harder to template"; the surface IS the trust boundary
157
+ - ⊗ MUST NOT emit more than one system-role entry in providers that expect a single system slot (e.g. OpenAI Chat Completions) -- compose the overlay into a single string before the call
158
+ - ~ When the provider supports structured system content (Anthropic content-block array), SHOULD render `agent_base` / `session_overlay` / `call_overlay` as separate blocks so the audit log can split them; the resulting effective prompt is still call-scoped and ephemeral
159
+
160
+ ## Anti-patterns
161
+
162
+ - ⊗ Prepending a `user` turn `"You are a <role>. <task>"` instead of using the system overlay surface -- the canonical anti-pattern this rule eliminates
163
+ - ⊗ Prepending a synthetic `assistant` turn `"Understood, I am a <role>."` to acknowledge the role -- mirrored variant of the same anti-pattern
164
+ - ⊗ Embedding role instructions in the first real user turn (`"As a security reviewer, please review the following: ..."`) -- still pollutes the history; the model and any downstream consumer see the role as user-tier content
165
+ - ⊗ Persisting the call-tier overlay back into the agent or session role -- collapses the precedence chain into silent history-dependent behavior
166
+ - ⊗ Aggregating role instructions across turns ("you are a reviewer", "you are also concise", "you are also formal") via repeated user-turn injection -- compose the overlay at call time instead, so the model sees one coherent role specification per call
167
+ - ⊗ Leaking the overlay into the work product (PR body / commit message / scope vBRIEF narrative) -- the role is internal configuration; the work product describes outcomes, not the model's stance
168
+ - ⊗ Treating a sub-agent's seed history as a place to "configure its persona" -- pass the role through the dispatch envelope as configuration and let the sub-agent apply it as its own call-tier overlay
169
+
170
+ ## Cross-references
171
+
172
+ - #674 -- context rot (role-injection-as-messages is a documented structural contributor)
173
+ - #479 -- false memory propagation (the persistence-layer analogue of role pollution when polluted history is summarized)
174
+ - #808 -- share state, isolate cognition (the orchestration cousin of this rule)
175
+ - #662 -- context degradation recovery
176
+ - [`patterns/llm-app.md`](./llm-app.md) `## Prompt construction` and `## Trust tiers` -- the system prompt is the trust boundary this overlay parameter writes to; user-tier messages are explicitly untrusted, so role-as-user-message inverts the trust contract
177
+ - [`coding/security.md`](../coding/security.md) `## Agent-Specific Threats` -- prompt-injection isolation and the rule against exposing internal system prompts to untrusted surfaces (the security-side framing of the same boundary)
178
+ - [`patterns/multi-agent.md`](./multi-agent.md) -- multi-agent dispatch envelope; role overlays travel as dispatch-time configuration, not as injected history
179
+ - Flue SDK README -- source attribution for the call > session > agent precedence model