@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.
- package/.agents/skills/deft/SKILL.md +6 -0
- package/.agents/skills/deft-directive-article-review/SKILL.md +11 -0
- package/.agents/skills/deft-directive-build/SKILL.md +10 -0
- package/.agents/skills/deft-directive-cost/SKILL.md +12 -0
- package/.agents/skills/deft-directive-debug/SKILL.md +13 -0
- package/.agents/skills/deft-directive-gh-arch/SKILL.md +11 -0
- package/.agents/skills/deft-directive-gh-slice/SKILL.md +10 -0
- package/.agents/skills/deft-directive-glossary/SKILL.md +10 -0
- package/.agents/skills/deft-directive-interview/SKILL.md +9 -0
- package/.agents/skills/deft-directive-pre-pr/SKILL.md +9 -0
- package/.agents/skills/deft-directive-refinement/SKILL.md +10 -0
- package/.agents/skills/deft-directive-release/SKILL.md +12 -0
- package/.agents/skills/deft-directive-review-cycle/SKILL.md +10 -0
- package/.agents/skills/deft-directive-setup/SKILL.md +10 -0
- package/.agents/skills/deft-directive-swarm/SKILL.md +10 -0
- package/.agents/skills/deft-directive-sync/SKILL.md +9 -0
- package/.agents/skills/deft-directive-triage/SKILL.md +9 -0
- package/.agents/skills/deft-directive-write-skill/SKILL.md +9 -0
- package/LICENSE.md +22 -0
- package/QUICK-START.md +167 -0
- package/UPGRADING.md +517 -0
- package/coding/build-output.md +28 -0
- package/coding/coding.md +235 -0
- package/coding/debugging.md +110 -0
- package/coding/holzmann.md +96 -0
- package/coding/hygiene.md +127 -0
- package/coding/security.md +158 -0
- package/coding/testing.md +162 -0
- package/coding/toolchain.md +44 -0
- package/commands.md +300 -0
- package/context/context.md +57 -0
- package/context/deterministic-split.md +67 -0
- package/context/examples.md +26 -0
- package/context/fractal-summaries.md +69 -0
- package/context/long-horizon.md +46 -0
- package/context/spec-deltas.md +177 -0
- package/context/tool-design.md +34 -0
- package/context/working-memory.md +62 -0
- package/contracts/boundary-maps.md +65 -0
- package/contracts/deterministic-questions.md +59 -0
- package/contracts/hierarchy.md +77 -0
- package/conventions/content-manifest.json +399 -0
- package/conventions/machine-generated-banner.md +130 -0
- package/conventions/references.md +120 -0
- package/conventions/rule-ownership.json +382 -0
- package/conventions/task-caching.md +43 -0
- package/conventions/vbrief-filenames.md +70 -0
- package/deployments/README.md +37 -0
- package/deployments/agentuity/README.md +138 -0
- package/deployments/agentuity/via-cli.md +380 -0
- package/deployments/agentuity/via-cloud.md +425 -0
- package/deployments/agentuity/via-github-actions.md +664 -0
- package/deployments/agentuity/via-gravity-network.md +606 -0
- package/deployments/agentuity/via-vpc.md +607 -0
- package/deployments/aws/README.md +38 -0
- package/deployments/aws/via-app-runner.md +612 -0
- package/deployments/aws/via-ecs-fargate.md +561 -0
- package/deployments/aws/via-elastic-beanstalk.md +628 -0
- package/deployments/aws/via-lambda.md +649 -0
- package/deployments/azure/README.md +37 -0
- package/deployments/azure/via-aks.md +390 -0
- package/deployments/azure/via-app-service.md +564 -0
- package/deployments/azure/via-container-apps.md +599 -0
- package/deployments/azure/via-functions.md +552 -0
- package/deployments/cloud-gov/README.md +63 -0
- package/deployments/cloud-gov/agents/compliance-docs.md +154 -0
- package/deployments/cloud-gov/agents.md +39 -0
- package/deployments/cloud-gov/cicd.md +64 -0
- package/deployments/cloud-gov/deployment.md +150 -0
- package/deployments/cloud-gov/logging.md +43 -0
- package/deployments/cloud-gov/manifest.md +121 -0
- package/deployments/cloud-gov/overview.md +58 -0
- package/deployments/cloud-gov/security.md +46 -0
- package/deployments/cloud-gov/services.md +72 -0
- package/deployments/cloud-gov/upstream/README.md +18 -0
- package/deployments/cloudflare/README.md +33 -0
- package/deployments/cloudflare/via-dashboard.md +83 -0
- package/deployments/cloudflare/via-git.md +90 -0
- package/deployments/cloudflare/via-github-actions.md +185 -0
- package/deployments/cloudflare/via-terraform.md +157 -0
- package/deployments/cloudflare/via-wrangler.md +165 -0
- package/deployments/fly-io/README.md +37 -0
- package/deployments/fly-io/via-dockerfile.md +648 -0
- package/deployments/fly-io/via-flyctl.md +653 -0
- package/deployments/fly-io/via-github-actions.md +695 -0
- package/deployments/fly-io/via-multi-region.md +598 -0
- package/deployments/google/README.md +34 -0
- package/deployments/google/via-app-engine.md +42 -0
- package/deployments/google/via-cloud-functions.md +23 -0
- package/deployments/google/via-cloud-run.md +330 -0
- package/deployments/google/via-gke.md +23 -0
- package/deployments/netlify/README.md +99 -0
- package/deployments/netlify/via-cli.md +17 -0
- package/deployments/netlify/via-functions.md +19 -0
- package/deployments/netlify/via-git.md +25 -0
- package/deployments/vercel/README.md +90 -0
- package/deployments/vercel/via-api.md +16 -0
- package/deployments/vercel/via-cli.md +17 -0
- package/deployments/vercel/via-git.md +24 -0
- package/docs/BROWNFIELD.md +179 -0
- package/docs/getting-started.md +137 -0
- package/docs/good-agents-md.md +137 -0
- package/events/README.md +89 -0
- package/events/event-record.schema.json +26 -0
- package/events/registry.json +166 -0
- package/events/registry.schema.json +71 -0
- package/glossary.md +145 -0
- package/incidents/README.md +81 -0
- package/incidents/_template.md +38 -0
- package/interfaces/cli.md +104 -0
- package/interfaces/rest.md +212 -0
- package/interfaces/tui.md +242 -0
- package/interfaces/web.md +123 -0
- package/languages/6502-DASM.md +132 -0
- package/languages/c.md +235 -0
- package/languages/commands.md +23 -0
- package/languages/cpp.md +132 -0
- package/languages/csharp.md +259 -0
- package/languages/dart.md +183 -0
- package/languages/delphi.md +218 -0
- package/languages/elixir.md +208 -0
- package/languages/go.md +78 -0
- package/languages/java.md +278 -0
- package/languages/javascript.md +163 -0
- package/languages/julia.md +175 -0
- package/languages/kotlin.md +193 -0
- package/languages/markdown.md +168 -0
- package/languages/mermaid.md +146 -0
- package/languages/officejs.md +392 -0
- package/languages/python.md +209 -0
- package/languages/r.md +163 -0
- package/languages/rust.md +216 -0
- package/languages/sql.md +216 -0
- package/languages/swift.md +153 -0
- package/languages/typescript.md +132 -0
- package/languages/vba.md +279 -0
- package/languages/vhdl.md +180 -0
- package/languages/visual-basic.md +151 -0
- package/languages/zig.md +196 -0
- package/meta/SOUL.md +27 -0
- package/meta/code-field.md +44 -0
- package/meta/morals.md +40 -0
- package/meta/philosophy.md +39 -0
- package/meta/project.md +49 -0
- package/meta/ralph.md +223 -0
- package/meta/security.md +80 -0
- package/meta/versioning.md +326 -0
- package/package.json +22 -0
- package/packs/lessons/lessons-pack-0.1.json +553 -0
- package/packs/patterns/patterns-pack-0.1.json +57 -0
- package/packs/rules/rules-pack-0.1.json +4767 -0
- package/packs/skills/skills-pack-0.1.json +262 -0
- package/packs/strategies/strategies-pack-0.1.json +167 -0
- package/packs/swarm-spec/swarm-spec-pack-0.1.json +17 -0
- package/patterns/executor-layer-credentials.md +227 -0
- package/patterns/llm-app.md +156 -0
- package/patterns/multi-agent.md +278 -0
- package/patterns/prompt-assembly-layer-ordering.md +154 -0
- package/patterns/role-as-overlay.md +179 -0
- package/platforms/2600.md +137 -0
- package/platforms/unity.md +329 -0
- package/references/composer-skill-porting.md +152 -0
- package/references/cost-models.md +163 -0
- package/references/ip-risk.md +246 -0
- package/references/plain-english-ux.md +275 -0
- package/resilience/context-pruning.md +67 -0
- package/resilience/continue-here.md +62 -0
- package/scm/changelog.md +276 -0
- package/scm/git.md +139 -0
- package/scm/github.md +265 -0
- package/secrets/.gitkeep +0 -0
- package/skills/deft-build/SKILL.md +20 -0
- package/skills/deft-directive-article-review/SKILL.md +156 -0
- package/skills/deft-directive-build/SKILL.md +302 -0
- package/skills/deft-directive-cost/SKILL.md +201 -0
- package/skills/deft-directive-debug/SKILL.md +140 -0
- package/skills/deft-directive-decompose/SKILL.md +96 -0
- package/skills/deft-directive-gh-arch/SKILL.md +160 -0
- package/skills/deft-directive-gh-slice/SKILL.md +199 -0
- package/skills/deft-directive-glossary/SKILL.md +118 -0
- package/skills/deft-directive-interview/SKILL.md +528 -0
- package/skills/deft-directive-pre-pr/SKILL.md +131 -0
- package/skills/deft-directive-probe/SKILL.md +127 -0
- package/skills/deft-directive-refinement/SKILL.md +403 -0
- package/skills/deft-directive-release/SKILL.md +266 -0
- package/skills/deft-directive-review-cycle/SKILL.md +401 -0
- package/skills/deft-directive-setup/SKILL.md +717 -0
- package/skills/deft-directive-swarm/SKILL.md +989 -0
- package/skills/deft-directive-sync/SKILL.md +288 -0
- package/skills/deft-directive-triage/SKILL.md +137 -0
- package/skills/deft-directive-write-skill/SKILL.md +169 -0
- package/skills/deft-interview/SKILL.md +16 -0
- package/skills/deft-pre-pr/SKILL.md +16 -0
- package/skills/deft-review-cycle/SKILL.md +16 -0
- package/skills/deft-roadmap-refresh/SKILL.md +16 -0
- package/skills/deft-setup/SKILL.md +20 -0
- package/skills/deft-swarm/SKILL.md +16 -0
- package/skills/deft-sync/SKILL.md +16 -0
- package/strategies/README.md +83 -0
- package/strategies/artifact-guards.md +85 -0
- package/strategies/bdd.md +115 -0
- package/strategies/brownfield.md +7 -0
- package/strategies/discuss.md +129 -0
- package/strategies/emit-hints.md +69 -0
- package/strategies/enterprise.md +193 -0
- package/strategies/interview.md +551 -0
- package/strategies/map.md +179 -0
- package/strategies/probe.md +151 -0
- package/strategies/rapid.md +155 -0
- package/strategies/research.md +155 -0
- package/strategies/roadmap.md +9 -0
- package/strategies/speckit.md +437 -0
- package/strategies/v0-20-contract.md +134 -0
- package/strategies/yolo.md +169 -0
- package/swarm/swarm.md +300 -0
- package/templates/COST-ESTIMATE.md +114 -0
- package/templates/PULL_REQUEST_TEMPLATE.md +35 -0
- package/templates/agent-prompt-preamble.md +409 -0
- package/templates/agents-entry.md +211 -0
- package/templates/agents-entry.placeholders.md +75 -0
- package/templates/embed.go +20 -0
- package/templates/embed_test.go +36 -0
- package/templates/make-spec-example.md +9 -0
- package/templates/make-spec.md +246 -0
- package/templates/project.md.template +52 -0
- package/templates/specification.md +1 -0
- package/templates/swarm-greptile-poller-prompt.md +556 -0
- package/templates/user.md.template +31 -0
- package/tools/RWLDL.md +80 -0
- package/tools/greptile.md +141 -0
- package/tools/installer.md +23 -0
- package/tools/taskfile-migration.md +32 -0
- package/tools/taskfile.md +185 -0
- package/tools/telemetry.md +285 -0
- package/vbrief/schemas/cache-meta.schema.json +137 -0
- package/vbrief/schemas/candidates.schema.json +130 -0
- package/vbrief/schemas/codebase-map.schema.json +213 -0
- package/vbrief/schemas/lessons-pack.schema.json +134 -0
- package/vbrief/schemas/patterns-pack.schema.json +84 -0
- package/vbrief/schemas/rules-pack.schema.json +105 -0
- package/vbrief/schemas/skills-pack.schema.json +94 -0
- package/vbrief/schemas/slices.schema.json +87 -0
- package/vbrief/schemas/strategies-pack.schema.json +89 -0
- package/vbrief/schemas/swarm-spec-pack.schema.json +84 -0
- package/vbrief/schemas/vbrief-core.schema.json +1022 -0
- package/vbrief/vbrief.md +684 -0
- package/verification/integration.md +76 -0
- package/verification/plan-checking.md +85 -0
- package/verification/uat.md +60 -0
- 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
|