@bridge_gpt/mcp-server 0.2.42 → 0.2.43

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.
@@ -42,6 +42,292 @@ export const PIPELINES = {
42
42
  }
43
43
  ]
44
44
  },
45
+ "greenfield-setup": {
46
+ "name": "greenfield-setup",
47
+ "description": "Onboard a brand-new project that has no code to learn from yet. Offers a bounded interview (or generic defaults), ratifies the open non-functional requirements on a decision page, settles version control, convenes a general council, synthesizes one coherent set of project standards behind a single approval, writes the unset standards fields, and closes with the one authoritative install-manifest apply plus the read-after-write manifest /install-bridge resumes from.",
48
+ "variables": [
49
+ "docs_dir",
50
+ "repo_name"
51
+ ],
52
+ "steps": [
53
+ {
54
+ "type": "mcp_call",
55
+ "tool": "ping",
56
+ "params": {},
57
+ "description": "Verify Bridge API connectivity"
58
+ },
59
+ {
60
+ "type": "agent_task",
61
+ "instruction": "Create the required output directory by running: mkdir -p {docs_dir}/greenfield/{repo_name}/standards",
62
+ "description": "Create the greenfield working directory"
63
+ },
64
+ {
65
+ "type": "mcp_call",
66
+ "id": "read_install_manifest",
67
+ "tool": "get_install_manifest",
68
+ "params": {},
69
+ "description": "Read the install manifest once and keep its snapshot token for the final apply"
70
+ },
71
+ {
72
+ "type": "mcp_call",
73
+ "id": "fetch_architecture_instructions",
74
+ "tool": "config_field",
75
+ "params": {
76
+ "field_name": "architecture_instructions",
77
+ "operation": "get"
78
+ },
79
+ "description": "Check whether architecture_instructions already has content",
80
+ "on_error": "warn_and_continue"
81
+ },
82
+ {
83
+ "type": "mcp_call",
84
+ "id": "fetch_review_instructions",
85
+ "tool": "config_field",
86
+ "params": {
87
+ "field_name": "review_instructions",
88
+ "operation": "get"
89
+ },
90
+ "description": "Check whether review_instructions already has content",
91
+ "on_error": "warn_and_continue"
92
+ },
93
+ {
94
+ "type": "mcp_call",
95
+ "id": "fetch_documentation_instructions",
96
+ "tool": "config_field",
97
+ "params": {
98
+ "field_name": "documentation_instructions",
99
+ "operation": "get"
100
+ },
101
+ "description": "Check whether documentation_instructions already has content",
102
+ "on_error": "warn_and_continue"
103
+ },
104
+ {
105
+ "type": "mcp_call",
106
+ "id": "fetch_unit_testing_instructions",
107
+ "tool": "config_field",
108
+ "params": {
109
+ "field_name": "unit_testing_instructions",
110
+ "operation": "get"
111
+ },
112
+ "description": "Check whether unit_testing_instructions already has content",
113
+ "on_error": "warn_and_continue"
114
+ },
115
+ {
116
+ "type": "mcp_call",
117
+ "id": "fetch_e2e_testing_instructions",
118
+ "tool": "config_field",
119
+ "params": {
120
+ "field_name": "e2e_testing_instructions",
121
+ "operation": "get"
122
+ },
123
+ "description": "Check whether e2e_testing_instructions already has content",
124
+ "on_error": "warn_and_continue"
125
+ },
126
+ {
127
+ "type": "mcp_call",
128
+ "id": "fetch_frontend_correctness_standards",
129
+ "tool": "config_field",
130
+ "params": {
131
+ "field_name": "frontend_correctness_standards",
132
+ "operation": "get"
133
+ },
134
+ "description": "Check whether frontend_correctness_standards already has content",
135
+ "on_error": "warn_and_continue"
136
+ },
137
+ {
138
+ "type": "mcp_call",
139
+ "id": "fetch_backend_correctness_standards",
140
+ "tool": "config_field",
141
+ "params": {
142
+ "field_name": "backend_correctness_standards",
143
+ "operation": "get"
144
+ },
145
+ "description": "Check whether backend_correctness_standards already has content",
146
+ "on_error": "warn_and_continue"
147
+ },
148
+ {
149
+ "type": "mcp_call",
150
+ "id": "fetch_template_correctness_standards",
151
+ "tool": "config_field",
152
+ "params": {
153
+ "field_name": "template_correctness_standards",
154
+ "operation": "get"
155
+ },
156
+ "description": "Check whether template_correctness_standards already has content",
157
+ "on_error": "warn_and_continue"
158
+ },
159
+ {
160
+ "type": "mcp_call",
161
+ "id": "fetch_style_correctness_standards",
162
+ "tool": "config_field",
163
+ "params": {
164
+ "field_name": "style_correctness_standards",
165
+ "operation": "get"
166
+ },
167
+ "description": "Check whether style_correctness_standards already has content",
168
+ "on_error": "warn_and_continue"
169
+ },
170
+ {
171
+ "type": "mcp_call",
172
+ "id": "fetch_design_principles",
173
+ "tool": "config_field",
174
+ "params": {
175
+ "field_name": "design_principles",
176
+ "operation": "get"
177
+ },
178
+ "description": "Check whether design_principles already has content",
179
+ "on_error": "warn_and_continue"
180
+ },
181
+ {
182
+ "type": "agent_task",
183
+ "id": "greenfield_interview",
184
+ "instruction_file": "greenfield-interview.md",
185
+ "description": "Offer the interview, settle the project framing and version control, and commit the decisions"
186
+ },
187
+ {
188
+ "type": "agent_task",
189
+ "id": "synthesize_standards",
190
+ "instruction_file": "greenfield-synthesize-standards.md",
191
+ "description": "Draft the project standards from the committed framing and take one approval"
192
+ },
193
+ {
194
+ "type": "mcp_call",
195
+ "id": "upload_architecture_instructions",
196
+ "tool": "config_field",
197
+ "params": {
198
+ "field_name": "architecture_instructions",
199
+ "file_path": "{docs_dir}/greenfield/{repo_name}/standards/architecture_instructions.md",
200
+ "operation": "update",
201
+ "only_if_null": true
202
+ },
203
+ "description": "Save the architecture standard",
204
+ "on_error": "warn_and_continue"
205
+ },
206
+ {
207
+ "type": "mcp_call",
208
+ "id": "upload_review_instructions",
209
+ "tool": "config_field",
210
+ "params": {
211
+ "field_name": "review_instructions",
212
+ "file_path": "{docs_dir}/greenfield/{repo_name}/standards/review_instructions.md",
213
+ "operation": "update",
214
+ "only_if_null": true
215
+ },
216
+ "description": "Save the code-review standard",
217
+ "on_error": "warn_and_continue"
218
+ },
219
+ {
220
+ "type": "mcp_call",
221
+ "id": "upload_documentation_instructions",
222
+ "tool": "config_field",
223
+ "params": {
224
+ "field_name": "documentation_instructions",
225
+ "file_path": "{docs_dir}/greenfield/{repo_name}/standards/documentation_instructions.md",
226
+ "operation": "update",
227
+ "only_if_null": true
228
+ },
229
+ "description": "Save the documentation standard",
230
+ "on_error": "warn_and_continue"
231
+ },
232
+ {
233
+ "type": "mcp_call",
234
+ "id": "upload_unit_testing_instructions",
235
+ "tool": "config_field",
236
+ "params": {
237
+ "field_name": "unit_testing_instructions",
238
+ "file_path": "{docs_dir}/greenfield/{repo_name}/standards/unit_testing_instructions.md",
239
+ "operation": "update",
240
+ "only_if_null": true
241
+ },
242
+ "description": "Save the unit-testing standard",
243
+ "on_error": "warn_and_continue"
244
+ },
245
+ {
246
+ "type": "mcp_call",
247
+ "id": "upload_e2e_testing_instructions",
248
+ "tool": "config_field",
249
+ "params": {
250
+ "field_name": "e2e_testing_instructions",
251
+ "file_path": "{docs_dir}/greenfield/{repo_name}/standards/e2e_testing_instructions.md",
252
+ "operation": "update",
253
+ "only_if_null": true
254
+ },
255
+ "description": "Save the end-to-end testing standard",
256
+ "on_error": "warn_and_continue"
257
+ },
258
+ {
259
+ "type": "mcp_call",
260
+ "id": "upload_frontend_correctness_standards",
261
+ "tool": "config_field",
262
+ "params": {
263
+ "field_name": "frontend_correctness_standards",
264
+ "file_path": "{docs_dir}/greenfield/{repo_name}/standards/frontend_correctness_standards.md",
265
+ "operation": "update",
266
+ "only_if_null": true
267
+ },
268
+ "description": "Save the frontend correctness standard",
269
+ "on_error": "warn_and_continue"
270
+ },
271
+ {
272
+ "type": "mcp_call",
273
+ "id": "upload_backend_correctness_standards",
274
+ "tool": "config_field",
275
+ "params": {
276
+ "field_name": "backend_correctness_standards",
277
+ "file_path": "{docs_dir}/greenfield/{repo_name}/standards/backend_correctness_standards.md",
278
+ "operation": "update",
279
+ "only_if_null": true
280
+ },
281
+ "description": "Save the backend correctness standard",
282
+ "on_error": "warn_and_continue"
283
+ },
284
+ {
285
+ "type": "mcp_call",
286
+ "id": "upload_template_correctness_standards",
287
+ "tool": "config_field",
288
+ "params": {
289
+ "field_name": "template_correctness_standards",
290
+ "file_path": "{docs_dir}/greenfield/{repo_name}/standards/template_correctness_standards.md",
291
+ "operation": "update",
292
+ "only_if_null": true
293
+ },
294
+ "description": "Save the template correctness standard",
295
+ "on_error": "warn_and_continue"
296
+ },
297
+ {
298
+ "type": "mcp_call",
299
+ "id": "upload_style_correctness_standards",
300
+ "tool": "config_field",
301
+ "params": {
302
+ "field_name": "style_correctness_standards",
303
+ "file_path": "{docs_dir}/greenfield/{repo_name}/standards/style_correctness_standards.md",
304
+ "operation": "update",
305
+ "only_if_null": true
306
+ },
307
+ "description": "Save the style correctness standard",
308
+ "on_error": "warn_and_continue"
309
+ },
310
+ {
311
+ "type": "mcp_call",
312
+ "id": "upload_design_principles",
313
+ "tool": "config_field",
314
+ "params": {
315
+ "field_name": "design_principles",
316
+ "file_path": "{docs_dir}/greenfield/{repo_name}/standards/design_principles.md",
317
+ "operation": "update",
318
+ "only_if_null": true
319
+ },
320
+ "description": "Save the design principles",
321
+ "on_error": "warn_and_continue"
322
+ },
323
+ {
324
+ "type": "agent_task",
325
+ "id": "apply_and_report",
326
+ "instruction_file": "greenfield-apply-and-report.md",
327
+ "description": "Save the project setup and report where the project stands"
328
+ }
329
+ ]
330
+ },
45
331
  "idea-to-ticket": {
46
332
  "name": "Idea to Ticket",
47
333
  "description": "Convert a short human idea into either a single Jira Task/Spike or a Jira Epic plus decomposed child tickets.",
@@ -696,11 +982,11 @@ export const INSTRUCTIONS = {
696
982
  "commit-and-push.md": "Stage, commit, and push implementation changes for ticket {ticket_key}.\n\nBefore executing, assess the git state and present a clear plan for user approval.\n\n## Step 1 — Assess Git State\n\nRun these commands and note the results:\n- `git branch --show-current` — record the current branch name\n- `git status --porcelain` — identify all modified, added, and untracked files\n\n### If the working tree is clean (nothing to commit)\n\nAn empty `git status --porcelain` is not one situation — it is two, and they must\nbe told apart before anything else happens. Check the published state of the\nbranch this ticket's work would live on (the current branch if it references\n`{ticket_key}`, otherwise `feature/{ticket_key}`):\n\n- `git ls-remote --heads origin <branch>` — non-empty output means the branch is\n already pushed.\n- `gh pr list --head <branch> --state all` — reports any pull request already\n opened from that branch.\n\nThen apply the matching rule:\n\n1. **Clean tree, and no pushed branch or pull request exists for this ticket** —\n the implementation phase produced nothing. **Stop the pipeline here** and\n report the failure. Proceeding would open an empty pull request; an empty\n implementation must halt.\n2. **Clean tree, but the branch is already pushed and/or a pull request already\n exists** — this is a legitimate **re-entry**, not an empty implementation.\n Recovery runs (`ci_fix`, `remediate`, `rebase`) re-enter this pipeline on a\n branch whose commits were pushed by an earlier session, so there is often\n nothing new to commit. **Do not halt.** There is nothing to stage, so skip\n Steps 2–5 entirely: run `git rev-parse HEAD`, record the result as\n `last_commit_sha`, and return successfully so the pipeline continues to the\n downstream steps (pull request handling, verification, CI monitoring) the\n re-entry was dispatched to run. Before returning, compare `git rev-parse HEAD`\n against the remote tip from `git ls-remote` — if local commits exist that were\n never pushed, push them (`git push origin <branch>`) first, preserving the\n finalization boundary.\n\nEither signal — a pushed branch or an existing pull request — selects the\nre-entry case. Only when both are absent does the clean tree mean an empty\nimplementation.\n\n## Step 2 — Determine Branch\n\nDecide the branching strategy and be prepared to state it explicitly. Cover:\n\n- Whether you will commit on the current branch, or create a new branch.\n- If creating a new branch: the exact new branch name, and which branch it will be created from (current branch vs. `main`).\n- If branching from `main`: whether `main` needs to be pulled/updated first, and the command you will run.\n- Whether the target branch already exists remotely (and if so, whether you will push to the existing remote branch).\n\nDefault rules:\n\n- If the current branch already contains `{ticket_key}` (case-insensitive), plan to commit on the current branch.\n- Otherwise, plan to create a new branch named `feature/{ticket_key}` from the current branch.\n\n## Step 3 — Prepare Commit Details\n\n- Separate implementation files from unrelated changes. Only stage files related to the ticket.\n- Compose a commit message: `{ticket_key}: <brief description of what was implemented>`\n\n## Step 4 — Present Plan for Approval (commit, push, and PR)\n\nFor this run, `auto_approve` = `{auto_approve}`.\n\n**Auto-approve mode.** If `auto_approve` is `true`, do NOT present the approval plan and do NOT wait for user input. Apply the default branching rule from Step 2 (commit on the current branch if it contains `{ticket_key}` case-insensitively; otherwise create `feature/{ticket_key}` from the current branch). Stage all files reported by `git status --porcelain` that you assess as related to the ticket per Step 3's \"Only stage files related to the ticket\" rule (when uncertain, prefer including over excluding — auto-approve trades caution for momentum, and the user has explicitly opted in). Use the commit-message format from Step 3. Skip directly to Step 5 and execute.\n\nOtherwise (any value of `auto_approve` other than the literal `true` — including empty, `false`, or missing), proceed with the existing approval flow below.\n\nPresent a single approval plan covering the commit, push, and pull request creation before proceeding:\n\n```\nCommit Plan for {ticket_key}\n─────────────────────────────\nCurrent branch: <current branch name>\nBranching: - <\"Commit on current branch\" | \"Create new branch `<name>` from `<source branch>`\">\n - <if branching from main: \"Pull latest main first via `git checkout main && git pull`\" | omit if N/A>\n - <\"Remote branch already exists — will push to existing\" | \"New remote branch — will push with -u\" | omit if N/A>\nFiles to stage: <count> files\n - path/to/file1.py\n - path/to/file2.py\nExcluded: <any unrelated changed files, or \"None\">\nCommit message: {ticket_key}: <description>\nPush to: origin/<target branch>\nPR title: <commit subject — derived automatically after commit>\nPR base: main\n```\n\nWait for the user to approve, request changes, or reject. The user may adjust the branch name, file inclusion, commit message, PR title, PR base, or give other instructions. The PR title defaults to the commit subject after the commit is made, and the PR base defaults to `main`.\n\nDo not proceed until the user explicitly approves.\n\n## Step 5 — Execute\n\nThis step is the **finalization boundary**: the commit and the push must both\ncomplete, synchronously and successfully, before this instruction returns. Nothing\ndownstream — pull request creation, verification, CI monitoring — may begin while\nthe implementation exists only in the local worktree.\n\n1. If creating a new branch, run `git checkout -b <branch name>`.\n2. Stage approved files with `git add <file1> <file2> ...` — do not use `git add -A` or `git add .`.\n3. Commit with the approved message.\n4. Push with `git push -u origin <branch>`. Use the plain push command — do **not**\n add `--no-verify`. Normal git hook behavior is preserved here; a Conductor\n worker already receives `BRIDGE_SKIP_PREPUSH=1` from the executor, so the\n instruction never needs to bypass hooks itself.\n5. If the commit fails, or the push fails or is rejected, **stop the pipeline\n here** and report the failure. Do not continue toward pull request creation or\n verification with work that exists only locally. One exception: a commit that\n fails only because there is genuinely nothing to commit means Step 1's\n clean-tree rule applies — resolve it there (halt when nothing was ever\n published; proceed when the branch/PR already exists) instead of treating the\n empty commit itself as the terminal failure.\n6. After the push succeeds, run `git rev-parse HEAD` and record the resulting SHA\n as `last_commit_sha` — the durable head that was actually published to the\n remote.\n\n## Return\n\nReport that the commit was made and pushed: the selected branch name, the pushed\nremote (e.g. `origin/feature/{ticket_key}`), the commit subject line, and the\npost-push `git rev-parse HEAD` value labeled `last_commit_sha`. Note any files that\nwere intentionally excluded from the commit. If commit or push failed, report the\nfailure instead — the pipeline stops rather than proceeding.\n\nIn the clean-tree re-entry case (Step 1, rule 2), report that no new commit was\nneeded: the branch name, the existing pushed remote, and the current\n`git rev-parse HEAD` value labeled `last_commit_sha` — and state that the pipeline\nis continuing to the downstream steps.\n",
697
983
  "comp-analysis.md": "Perceive any attached/referenced design comp with your OWN vision and map it to the existing codebase BEFORE the `jira-ticket-writer` drafts. This is the pre-writer perception step: the orchestrating recipe agent (already a frontier vision model) opens the comp, classifies it against the shared fidelity taxonomy, researches the code, and writes a structured comp→codebase map the writer consumes. The writer stays text-only and never opens images — it only reads the map you produce here.\n\nThis step is gated and backend-safe. It runs the perception with the ORCHESTRATING agent's own vision — a local image via the Read tool, or an already-attached Jira comp fetched as raw bytes via the BAPI-562 binary-safe `attachment` download (operation `download`) into a worktree `file_path`. It does NOT call `describe_image()`, does NOT depend on `src/python/llms/vision.py`, and does NOT require the `CHEAP_MODEL` vision path or any vision-model upgrade or procurement. The heavy visual reasoning is done for free by the agent that already runs the recipe.\n\n## Inputs\n\n- Run manifest: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/run-manifest.json` (source of truth for `idea`, `scope`, `readiness`). Read whichever prior artifacts exist under `{docs_dir}/idea-to-ticket/{slug}-{run_id}/` (research pack, resolved uncertainties, goals-and-nfrs, standards checklist) and proceed without the ones that do not.\n- Output artifact (this step writes it): `{docs_dir}/idea-to-ticket/{slug}-{run_id}/comp-analysis.json` — the structured comp→codebase map the drafting step reads.\n\n## Instructions\n\n1. **Gate first — evaluate only text/material metadata, never open an image yet.** Proceed to any image work ONLY when BOTH conditions hold:\n - a design comp is **referenced or attached** (a local image path in the idea/materials, or an already-attached Jira comp with an `attachment_id`), AND\n - the requested work is **design/UI work** (a frontend/fullstack change with a visual surface).\n\n This gate is deliberately conservative. A **backend-only** request, a **no-comp** request, or a **non-design** request fails the gate.\n\n2. **Not-applicable branch (gate fails) — short-circuit immediately.** When either gate condition is not met, write `comp-analysis.json` with `applicable: false`, a short `reason`, and the `gate` evidence fields, then **return immediately** — do NOT open any image, do NOT download any attachment, do NOT inject any fidelity/comp/visual language, and add no measurable latency. A backend-only or no-comp authoring input must be byte-for-byte unaffected downstream. Concretely, the not-applicable artifact is:\n\n ```json\n { \"applicable\": false, \"reason\": \"backend-only work; no design comp referenced\", \"gate\": { \"comp_referenced\": false, \"design_ui_work\": false }, \"warnings\": [] }\n ```\n\n3. **Applicable branch — obtain and OPEN the comp with your own vision.** When the gate passes:\n - **Local image path** → open it directly with the Read tool.\n - **Already-attached Jira comp** → fetch it as raw bytes using the BAPI-562 binary-safe `attachment` capability with operation `download`, passing the comp's `attachment_id` and a worktree `file_path`; the download saves the PNG/JPEG bytes to that `file_path` inside the project root, then open the saved file with the Read tool.\n - You are the orchestrating vision model — reason over the actual pixels yourself. Do NOT call `describe_image()`, do NOT use `src/python/llms/vision.py` / `CHEAP_MODEL`, and do NOT delegate perception to the text-only `jira-ticket-writer` subagent.\n\n4. **Degraded paths are skip-clean and warn-not-halt.** If the comp is missing, unreadable, an unsupported format, external-only (an `http(s)` URL you cannot fetch into a worktree `file_path`), or turns out not to be a usable design comp, write a valid `comp-analysis.json` with `applicable: false`, a `reason`, and a populated `warnings` array describing what failed, then return. A missing, unreadable, or non-comp image NEVER blocks downstream drafting and NEVER requires further image work after the failure.\n\n5. **Classify the opened comp using the shared fidelity taxonomy (do not fork it).** Using your own vision, classify the comp as exactly one of these four classes — the same labels the downstream final plan reviewer uses (`src/python/llms/agents/planner_agent/final_plan_review_agent.py`, `_get_runtime_verification_instructions`), so authoring, planning, and implementation all agree:\n - `full comp`\n - `wireframe`\n - `annotated-screenshot-of-existing-UI`\n - `unknown`\n\n Record `fidelity_classification` with a `class` (one of the four), a `confidence` value, and a short `reasoning` string.\n\n6. **Apply class-appropriate mapping depth — no over-specification.** Match the per-class rules exactly:\n - **full comp** (confident) → map exact existing components, Jinja2 templates, SCSS/CSS tokens, and routes; strict/exact component + token depth is used ONLY here.\n - **wireframe** → map layout and structure only (regions, order, rough proportions, responsive behavior); defer color, type, spacing, and component polish to the repo design system, NOT to the wireframe.\n - **annotated-screenshot-of-existing-UI** → map ONLY the delta against the current UI; preserve everything outside the annotated region and do not reproduce the screenshot wholesale.\n - **unknown / low confidence** → fall back to the design-system floor rather than mapping pixels.\n\n **Hard rule:** strict/exact mapping depth is enabled ONLY for a confidently-classified full comp. Fail toward the design system, never toward reproducing an ambiguous image.\n\n7. **Research the codebase and map each region/element to concrete existing code.** Inspect the working tree (search, grep, file reads) for the existing UI implementation surfaces: reusable components, Jinja2 templates, plain CSS/SCSS tokens and design-system styles, routes, and reusable frontend patterns. Only cite files you actually inspected — do not invent file paths, component names, tokens, or routes. For each region/element of the comp, produce a `mappings[]` entry containing:\n - `region` — the comp region or element name.\n - `visual_description` — a short description of what it looks like.\n - `components` — mapped existing component file(s).\n - `templates` — mapped existing Jinja2 template(s).\n - `tokens` — style/token references (SCSS/CSS tokens or design-system styles).\n - `routes` — route reference(s) where the element lives or should wire.\n - `confidence` — confidence for this mapping.\n - `implementation_guidance` — concrete guidance (e.g. \"reuse component X\", \"extend template Y\", \"use token Z\", \"wire route R\").\n\n8. **Write the structured map to a stable schema.** Write `comp-analysis.json` at `{docs_dir}/idea-to-ticket/{slug}-{run_id}/comp-analysis.json` with these fields:\n - `applicable` — boolean; `true` only after a comp was both detected AND successfully opened.\n - `reason` — short string explaining the applicability decision.\n - `gate` — the two-condition gate evidence (`comp_referenced`, `design_ui_work`).\n - `comp` — provenance of the opened comp (source kind, path or `attachment_id`, filename, MIME type when known).\n - `fidelity_classification` — `{ class, confidence, reasoning }`, present only when `applicable` is true.\n - `mappings` — array of the per-region entries defined in step 7 (present only when `applicable` is true).\n - `design_system_floor` — the design-system fallback guidance to use for wireframe/unknown/low-confidence regions.\n - `warnings` — array of degraded-path notes (may be empty).\n - `writer_guidance` — instructions for the downstream drafting step: when `applicable` is `true`, Requirements must cite the mapped components/templates/tokens/routes as concrete implementation guidance; when `applicable` is `false` or the map is missing, the writer must ignore this artifact and mention no comp analysis, design comp, or visual-fidelity language unless the original request independently requires it.\n\n9. **Never over-write.** Write the artifact exactly once at the path above. Downstream steps (`draft-and-critique.md`) read it; do not move it.\n\n## Return\n\nConfirm the path written (`{docs_dir}/idea-to-ticket/{slug}-{run_id}/comp-analysis.json`), whether the analysis was `applicable`, the fidelity `class` when applicable, and any `warnings`.\n",
698
984
  "create-pr.md": "# Create a pull request for the just-pushed branch\n\nThe implementation has been committed and pushed. Open a PR for the current branch against the PR base resolved in Step 3 — which is `main` only when nothing higher in that order of precedence supplies a base — with a descriptive title derived from the commit you just made.\n\n## Step 1 — Read the commit subject line\n\nRun `git log -1 --pretty=%s` to get the most recent commit subject. The implement-ticket pipeline asks the commit step to use the form `{ticket_key}: <description>`, so this line is normally already a good PR title.\n\n## Step 2 — Determine the head branch\n\nUse `git branch --show-current`. This is the head branch.\n\n## Step 2.5 — Run the file-scope guard (warn-only, before opening the PR)\n\nBefore opening or updating the PR, run the conductor file-scope guard so an\nout-of-scope diff is surfaced in the PR-creation context:\n\n```bash\nnode \"$BAPI_CONDUCTOR_CLI_FILE\" file-scope-guard\n```\n\n(or `conductor file-scope-guard` if the packaged binary is on PATH).\n\nThis guard is **warn-only and fail-open** — it **always exits 0 and never blocks\nPR creation** in v1. Behavior:\n\n- If `BAPI_CONDUCTOR_DECLARED_TOUCHED_FILES_JSON` is **absent, empty, or invalid**,\n the guard is a **no-op** — it prints nothing and you **continue** to Step 3\n normally. Do not treat a missing declaration as an error.\n- If the guard prints a warning that your branch diff touched files **outside** the\n ticket's declared touched-file set, **surface that warning in the PR-creation\n context** (include it in your summary to the user / PR notes) but **do not block**\n — proceed to open the PR. The warning is advisory: it flags a possible\n sibling-scope over-reach for human review, it does not stop the workflow.\n\n## Step 3 — Resolve the base, compose the body, and call create_pull_request\n\n### Resolve the PR base\n\nResolve `base_branch` through this ordered precedence, taking the first tier that yields a usable value:\n\n1. **`BAPI_BASE_BRANCH` from the environment, when set and non-empty.** Read it explicitly with Bash — never infer the base from branch ancestry or from the repository default branch:\n\n ```bash\n echo \"${BAPI_BASE_BRANCH:-}\"\n ```\n\n The non-failing `:-` form returns an empty line when the variable is unset, so the read never errors. `start-tickets` exports this variable into the worker's shell whenever the run has a non-default base (an epic branch, for example), which makes it the run's own answer and the highest-priority tier.\n2. **A PR base the user supplied at the commit step.** The commit step advertises PR base as an adjustable field, so an override given there carries forward into this call rather than being silently replaced by a lower tier.\n3. **The repository's configured base branch.** Call `config_field` with `operation: \"get\"` and `field_name: \"base_branch\"`.\n4. **`main`** — the normal fallback default when no tier above supplied a base.\n\nTreat a null, empty, or whitespace-only configured value, an HTTP `400`, or any lookup error as **unset** and continue quietly to the `main` fallback. Missing configuration is an ordinary outcome here, not a failure of this step.\n\n### Compose the PR body\n\nBuild `body` in this reading order:\n\n1. A concise summary line for the ticket — what this pull request changes.\n2. A plainly labeled reference to the local implementation plan, as plain text rather than a markdown hyperlink (the path is local to whoever pulls the branch): `Implementation plan available locally at <docs_dir>/plans/{ticket_key}-plan.md`. Use the plan location this recipe already knows when it has one; otherwise resolve `<docs_dir>` with the `get_docs_dir` MCP tool.\n3. The checklist text of `.github/PULL_REQUEST_TEMPLATE.md`, read from the current worktree when that file exists, appended after the plan reference without rewriting its markdown structure. Omit this part when the file is absent.\n\nGitHub's REST API does not automatically apply the repository pull request template — that template is a web-UI affordance only. A pull request opened through this tool therefore carries an empty description unless the recipe inlines the checklist into `body` itself, which is exactly why the checklist is read from the worktree and appended here.\n\n**This is enforced, not advisory (BAPI-824).** The server rejects a missing, empty, or whitespace-only `body` with a `422` and creates no pull request. Composing the body is a hard prerequisite of this step, not a courtesy: skipping it fails the call outright rather than opening a pull request with a blank description — which is what happened twice before the check existed. The response reports the length actually sent as `detail.body_len`.\n\n### Call the tool and report the PR URL\n\nCall the `create_pull_request` MCP tool directly with:\n\n- `head_branch`: value from `git branch --show-current`\n- `base_branch`: the base resolved above\n- `title`: the commit subject from Step 1 (the derived PR title) — unless the user supplied a different PR title at the commit step, in which case use that value instead.\n- `body`: the body composed above\n\nHonor any PR title / PR base overrides the user gave at the commit step's plan; the commit step advertises those fields as adjustable, so any override the user gave there must carry forward into this tool call rather than being silently replaced by a lower-precedence value.\n\n`create_pull_request` is the only mechanism this recipe uses to open a pull request. Do not shell out to a VCS command-line client to create it.\n\nReport the returned `pr_url` to the user.\n\n### The PR URL is a hard prerequisite for everything downstream\n\nThis step completes the durable artifact that the rest of the pipeline — and, under\nthe Conductor, the reconciler — binds to. It therefore has a strict success\ncondition: the call must yield a **non-empty PR URL**, either for a pull request it\nnewly opened or for an already-open pull request on this head branch that the\nresponse identifies.\n\nIf the tool is unavailable, errors, or returns neither a usable new PR URL nor an\nexisting one, **stop here and report the failure**. Do not advance to the\npost-finalization verification phase, which exists only to add work on top of an\nopen pull request.\n\nThen run `git rev-parse HEAD` and record the current pushed head SHA, so the\ndownstream phases report the same durable head this pull request points at.\n\n## Return\n\nReturn the head branch, the base branch that was used, the pull request URL, and\nthe current pushed HEAD SHA. If no usable pull request URL was obtained, return\nthat failure instead — the pipeline stops rather than proceeding to verification.\n",
699
- "decompose-epic-candidate.md": "Decompose an Epic parent draft into ordered child tickets with idempotency and per-child duplicate checks.\n\n## Inputs\n\n- Epic parent draft: `{docs_dir}/tickets/EPIC-{slug}.md`.\n- Research pack: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/research-pack.md` / `.json`.\n- Standards checklist: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/standards-checklist.json`.\n- Resolved uncertainties: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/resolved-uncertainties.md`.\n- Goals & NFR framing: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/goals-and-nfrs.md` (its System Goals/NFRs and any provisional Recommended Implementation Order should inform the child breakdown and ordering).\n- Hard cap variable `{max_children}` (string integer; default `\"10\"` when not set by the caller). The default `\"10\"` is a **hard ceiling / upper bound, not a target** child count — it caps how many children are allowed, and is **not a goal to fill**. The normal target child count is smaller (fewer, larger M/L slices); see the sizing heuristics in step 2.\n\n## Instructions\n\n1. Read the Epic parent draft, research pack, standards checklist, and resolved uncertainties. Use only this context plus optional narrow web search; do not call deep research from this step.\n\n2. Propose ordered child tickets that, together, fully implement the Epic.\n\n **Sizing heuristics (maintainer-owned defaults).** Size each proposed child by its expected **file-touch breadth and depth plus rough lines of code (LOC) changed**, using these exact thresholds:\n - `S = 1–2 files / <~80 LOC`\n - `M = ~3–8 files / ~80–400 LOC (ideal target)`\n - `L = ~8–15 files / ~400–900 LOC (acceptable)`\n - `XL = >15 files / >~900 LOC split further; never emit an XL child`\n\n Target size priority: `M (ideal) L (acceptable) S (only if unavoidable); never XL`.\n\n Bias the decomposition toward **fewer, larger, independently implementable vertical slices** rather than many tiny one-feature children. The Bridge implementation tooling works better on M–L vertical slices, and a swarm of tiny S children magnifies sibling merge risk under parallel execution. Each child should be an independently implementable vertical slice; if a proposed child would be XL, split it further until each piece is M or L.\n\n Each proposed child must include:\n - `summary` — Jira title.\n - `issue_type` — typically `Task`; use `Spike` only for primarily discovery children.\n - `rationale` — short explanation of why this child exists and what it produces. Include a brief size estimate inside this existing field (do **not** add a new `size` field), e.g. `Estimated size: M (~4 files / ~150 LOC)`.\n - `labels` — must include `ai-generated`, `idea-to-ticket`, `idea-to-ticket-child`, and the unique child idempotency label `bapi-idea-to-ticket-{run_id}-child-<N>` where `<N>` is the 1-based child index in the final ordered list.\n - `idempotency_label` — the same `bapi-idea-to-ticket-{run_id}-child-<N>` string.\n - `draft_path` — `{docs_dir}/tickets/TICKET-{slug}-child-<N>.md` (drafts written by `jira-ticket-writer` later).\n - `depends_on` — array of the 1-based child indexes that are **hard prerequisites** (must land first), or empty. Keep this list minimal and real.\n - `recommended_after` — array of child indexes that are **soft sequencing** preferences (nicer to do after, but not blockers), or empty.\n - `order_rationale` — one line explaining why this child sits at this point in the order.\n\n Keep hard prerequisites (`depends_on`) strictly separate from soft sequencing (`recommended_after`). These fields drive the recommended implementation order posted to the epic later; they do **not** create Jira dependency links.\n\n3. Hard cap enforcement. First attempt a normal, smaller M/L-biased decomposition per the step 2 sizing heuristics. Then count proposed children: `{max_children}` is a hard ceiling that **halts on exceed**, not a target to fill. If the count exceeds `{max_children}` (parsed as an integer), halt locally with a clear \"split first\" message: ask the user to split the idea into multiple smaller Epics or to raise `--max-children` deliberately. Do not silently truncate.\n\n4. Per-child duplicate lookup. For each proposed child (in order), call `get_tickets` once with a title/keyword search built from the child's summary. If a clear duplicate exists, drop that child from the plan and record the drop reason; never halt the whole run because a child has a duplicate. Re-number `<N>` only after all drops are finalized so child indexes are contiguous.\n\n5. Per-child research is restricted to the parent research pack plus optional narrow web search. Do not call deep research per child.\n\n6. Write `{docs_dir}/idea-to-ticket/{slug}-{run_id}/decomposition-plan.json` with at minimum:\n - `parent_summary` — copy from the parent draft.\n - `max_children` — the resolved integer value used for the cap.\n - `children` — ordered array of surviving children with all fields from step 2 (including `depends_on`, `recommended_after`, and `order_rationale`). After re-numbering in step 4, fix up the `depends_on`/`recommended_after` indexes so they still point at the correct surviving children.\n - `dropped_children` — array of `{proposed_summary, reason}` for children removed by duplicate lookup.\n\n## Return\n\nConfirm `decomposition-plan.json` was written, report the final child count and the number of children dropped for duplicate reasons.\n",
700
- "decompose-epic.md": "Decompose the epic into manageable sub-tasks and get user approval.\n\n## Epic Description\n\n{epic_description}\n\n## Instructions\n\n1. Read the following artifacts to establish full context. If a file does not exist or is empty, proceed without it:\n - `{docs_dir}/epic-plans/{epic_slug}/research-findings.md`\n - `{docs_dir}/epic-plans/{epic_slug}/codebase-exploration.md`\n\n2. Reason about the epic and produce a decomposition. Consider:\n - Logical groupings of work that can be implemented and tested independently\n - Dependencies between sub-tasks (what must be built first)\n - A reasonable scope for each sub-task (each should be achievable in a single implementation session)\n\n3. Write the decomposition to `{docs_dir}/epic-plans/{epic_slug}/epic-plan.md` with this format:\n\n```markdown\n# Epic Decomposition\n\n## Sub-tasks\n\n### 1. {Sub-task title}\n- **Scope**: {What this sub-task covers}\n- **Key files/areas**: {Files and code areas involved}\n- **Dependencies**: {Other sub-task numbers this depends on, or \"None\"}\n\n### 2. {Sub-task title}\n...\n```\n\n In addition, you MUST also write a structured JSON sidecar at\n `{docs_dir}/epic-plans/{epic_slug}/epic-plan.dag.json`.\n This file is the machine-readable intermediate consumed by the `plan-epic`\n pipeline to store and approve the plan in the backend — it must be written\n from your structured decomposition data, NEVER by re-parsing the markdown.\n\n The sidecar format is:\n\n```json\n{\n \"plan_version\": 1,\n \"nodes\": [\n {\n \"ticket_key\": \"BAPI-XXX\",\n \"status\": \"planned\",\n \"depends_on\": [],\n \"touched_files\": [\"api/routes/example.py\", \"src/python/llms/ai_client.py\"],\n \"automations\": [\n { \"kind\": \"start-tickets\" }\n ]\n }\n ],\n \"edges\": [\n { \"from\": \"BAPI-XXX\", \"to\": \"BAPI-YYY\" }\n ]\n}\n```\n\n Rules for the sidecar:\n - `plan_version` must be 1 for a new plan (an integer, never a float).\n - Each node `ticket_key` must match the Jira key of the created sub-task\n (populated after Jira ticket creation in a later pipeline step; use the\n planned Jira key if known, or a placeholder like \"TBD-1\" if not yet created).\n - `ticket_key` values must be unique and non-empty after trimming.\n - `depends_on` lists the `ticket_key` values this node depends on (mirrors\n the markdown Dependencies field). `depends_on` is the **canonical**\n dependency declaration.\n - `status` must be `\"planned\"` for newly-created sub-tasks.\n - `touched_files` is **required on every node** — the sorted, deduplicated,\n repository-relative POSIX paths this sub-task is predicted to change.\n Populate it from your current code research and the sub-task's ownership,\n the same analysis that tells you which sub-tasks collide.\n - No leading `./`, no absolute paths, no `..` traversal segments, no\n temporary-worktree paths, and no prose. Declare **concrete files**: the\n backend intersects these paths as exact strings, so a directory or glob\n entry matches nothing and protects nothing.\n - `[]` is permitted **only** when the decomposition explicitly establishes\n that the sub-task changes no repository files. Unknown file ownership is\n **blocking** — resolve it or flag it; never represent it as an empty\n array, which silently disables file-overlap protection.\n - `automations` lists automation kinds to run on the ticket — valid values\n are `\"start-tickets\"` and `\"review-tickets\"`. Use an empty array if none.\n - `edges` is an explicit list of directed dependency edges (from → to, where\n `from` is the predecessor). It may be empty if all dependencies are captured\n in `depends_on`, but any edge you do write must encode the same graph as\n `depends_on` — a contradictory graph blocks readiness.\n - Do **not** set `base_lineage` on any node. It affects the plan hash but has\n no consumer in either conductor, so setting it invalidates the approved hash\n for no behavioral gain.\n - The DAG must be acyclic (no circular dependencies).\n - The deterministic Jira-dependency-link DAG builder is the documented\n fallback/recovery path if this sidecar is lost or corrupted (not built here).\n\n **Placeholders are finalized later, not here.** `TBD-N` remains the correct\n node identity for the window before the epic's Jira tickets exist. The\n standalone `emit-conductor-bundle.md` task replaces every placeholder with the\n real ticket key and finalizes `touched_files` after the tickets are created and\n before the plan is stored or approved. Write your best `touched_files`\n prediction here anyway — that task reconciles it against the whole epic.\n\n4. **Soft limit check**: If the decomposition results in more than 8 sub-tasks, you must verbally warn the user: \"This decomposition has N sub-tasks, which exceeds the recommended limit of 8. Consider splitting this feature into multiple epics.\" Then proceed with the approval flow.\n\n5. Present the decomposition to the user and ask for their feedback. Explain the reasoning behind the breakdown and the dependency ordering.\n\n6. You MUST stop and wait for the user to respond. Do NOT assume approval. Do NOT proceed to the next step.\n\n7. If the user provides feedback or rejects the decomposition:\n - Incorporate their feedback\n - Rewrite `{docs_dir}/epic-plans/{epic_slug}/epic-plan.md` with the revised version\n - Present the revised decomposition and ask for approval again\n - Repeat until the user explicitly approves\n\n8. Only after explicit user approval, confirm: \"Decomposition approved. Proceeding to sub-task exploration.\"\n\n## Return\n\nConfirm \"Decomposition approved.\" and report the final sub-task count plus the path to `{docs_dir}/epic-plans/{epic_slug}/epic-plan.md`. Flag if the count exceeded the recommended limit of 8.\n",
701
- "draft-and-critique.md": "Draft the ticket(s) for this idea, run a BAPI-320 hygiene pass, and emit structured draft metadata.\n\n## Inputs\n\n- Run manifest: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/run-manifest.json`.\n- Research pack: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/research-pack.md` / `.json`.\n- Duplicate assessment: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/duplicate-assessment.json`.\n- Standards checklist: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/standards-checklist.json`.\n- Resolved uncertainties: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/resolved-uncertainties.md`.\n- Goals & NFR framing: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/goals-and-nfrs.md`.\n- Comp→codebase map (optional): `{docs_dir}/idea-to-ticket/{slug}-{run_id}/comp-analysis.json`. Produced upstream by the gated `comp-analysis.md` perception step. A missing, unreadable, or `applicable: false` map is treated exactly like `applicable: false` — a no-op: it never halts drafting and this step injects no visual-fidelity/comp language on its own.\n\n## Instructions\n\n1. **Resolve the regression-completeness gate once, before drafting.** Call the `config_field` MCP tool with `operation` set to `\"get\"` and `field_name` set to `enable_regression_checks`. Normalize the response: only the exact value `true` (a JSON boolean `true`, or the case-insensitive string `\"true\"`) counts as enabled. Treat a missing tool, a tool error, `null`, `false`, or any other/malformed value as disabled — this lookup is fail-open and defaults OFF. Store the normalized boolean as `enable_regression_checks` and reuse the same stored value for every `jira-ticket-writer` invocation this flow makes (including any later critique/revision call) — do not re-resolve it per call. The `jira-ticket-writer` sub-agent must not call `config_field` itself.\n\n2. Read the six required input artifacts in full before drafting, plus the comp-analysis map when it is present. The manifest's `scope` (`task`, `spike`, or `epic_candidate`) determines the drafting path.\n\n The goals-and-nfrs.md framing must shape every draft. Lead each draft with the **business goal** and **desired end-state**, and include an explicit **Non-Functional Requirements** section (and, where it clarifies behavior, the required **system behavior**). For the **epic_candidate** parent, these belong in the Epic description itself (the parent's `slim_description` should at least name the business goal + end-state, and the attached full draft must carry the Goals / Desired End-State / Non-Functional Requirements sections). Any NFR still marked `open` in the framing must be written into the draft as an explicit assumption plus an open-risk note — never silently dropped. This flow is documentary: do not generate a decision page and do not pause for clarification here.\n\n3. Drafting path by scope:\n - **task** or **spike**:\n - Call the `jira-ticket-writer` sub-agent with an explicit output path of `{docs_dir}/tickets/TICKET-{slug}.md`. The sub-agent must write the full markdown draft to that exact file. Pass the comp-analysis map path (`{docs_dir}/idea-to-ticket/{slug}-{run_id}/comp-analysis.json`) into the sub-agent prompt alongside the six existing input artifacts (run manifest, research pack, duplicate assessment, standards checklist, resolved uncertainties, goals-and-nfrs) and the stored `enable_regression_checks` value from step 1.\n - **epic_candidate**:\n - Call `jira-ticket-writer` to draft only the Epic parent. Use the explicit output path `{docs_dir}/tickets/EPIC-{slug}.md`. Pass the same comp-analysis map path into the sub-agent prompt alongside the six existing input artifacts and the stored `enable_regression_checks` value from step 1. Child tickets are produced later by `decompose-epic-candidate.md`; do not draft them here.\n\n Comp-analysis map consumption (both paths): when `comp-analysis.json` has `applicable: true`, tell the writer its Requirements MUST use the mapped components/templates/tokens/routes as concrete implementation guidance (e.g. \"reuse component X\", \"use token Z\", \"extend template Y\", \"wire route R\"). When the map has `applicable: false` or is missing/unreadable, tell the writer to ignore the artifact entirely and avoid mentioning comp analysis, design comps, or visual fidelity unless the original request independently requires those materials. This step only feeds the text JSON map to the writer — it never opens images, calls `describe_image()`, or performs vision analysis itself.\n\n Regression-gate consumption (both paths): state `enable_regression_checks: {stored value from step 1}` explicitly in the sub-agent prompt, and tell the writer this value was already resolved by the caller and must not be looked up again. The writer only runs its Regression Completeness Pass when this value is exactly `true`.\n\n4. Issue type policy:\n - Default ambiguous ideas to `Task`.\n - Choose `Spike` only when the work is primarily discovery/research/learning with no clear acceptance criteria yet.\n - The Epic parent uses Jira issue type `Epic`.\n\n5. Hygiene pass (BAPI-320 forbidden tokens). After the sub-agent writes the draft, read it back and ensure none of these tokens are present:\n - markdown tables (any `|`-separated header row).\n - escaped pipe-table patterns (e.g. `\\|`).\n - task-list checkboxes such as `- [ ]` or `- [x]`.\n - angle-bracket placeholder tokens (any `<placeholder>` form, even inside backticks).\n - raw HTML blocks (`<div>`, `<br>`, `<table>`, etc.).\n When a forbidden token is found, rewrite the surrounding paragraph in plain prose or bullet form and save the cleaned draft over the same path.\n\n6. Write `{docs_dir}/idea-to-ticket/{slug}-{run_id}/draft-metadata.json` describing what Jira should later create.\n\n For **task** / **spike** scope, the metadata shape is:\n - `summary` — Jira ticket title.\n - `issue_type` — `Task` or `Spike`.\n - `labels` — array of Jira labels. Must include `ai-generated`, `idea-to-ticket`, the per-run label `bapi-idea-to-ticket-{run_id}`, and the stable idea-hash label `bapi-idea-hash-{idea_hash}` (so a future run of the same idea is caught by label).\n - `idempotency_label` — `bapi-idea-to-ticket-{run_id}` (matches the label used by the duplicate-and-context-scan step).\n - `slim_description` — short Jira-safe description (no forbidden tokens). The full draft is uploaded as an attachment. It must include a CONCISE, high-level summary of the draft's `## Materials & Access` inventory (Planning Assets, Downloadable Assets, Planning & Downloadable Assets vs. unphased record-only material), noting that the exhaustive list lives in the attached full draft. This keeps the missing-materials record visible to human reviewers and to description-reading review/critique flows.\n - `attachment_path` — `{docs_dir}/tickets/TICKET-{slug}.md` (or the equivalent path used above).\n\n For **epic_candidate** scope, the metadata shape is:\n - `parent.summary` — Epic title.\n - `parent.issue_type` — `Epic`.\n - `parent.labels` — must include `ai-generated`, `idea-to-ticket`, `bapi-idea-to-ticket-{run_id}-parent`, and the stable idea-hash label `bapi-idea-hash-{idea_hash}`.\n - `parent.idempotency_label` — `bapi-idea-to-ticket-{run_id}-parent`.\n - `parent.slim_description` — short Epic description. It must include a CONCISE, high-level summary of the Epic draft's `## Materials & Access` inventory (Planning Assets, Downloadable Assets, Planning & Downloadable Assets vs. unphased record-only material), noting that the exhaustive list lives in the attached full draft.\n - `parent.attachment_path` — `{docs_dir}/tickets/EPIC-{slug}.md`.\n - `children` — placeholder array. Populated later by `decompose-epic-candidate.md`; leave as an empty array here.\n\n7. Save the metadata exactly once. Downstream steps read this file; do not move it.\n\n## Return\n\nConfirm the draft path, the metadata path, and the chosen scope (`task`, `spike`, or `epic_candidate`).\n",
985
+ "decompose-epic-candidate.md": "Decompose an Epic parent draft into ordered child tickets with idempotency and per-child duplicate checks.\n\n## Inputs\n\n- Epic parent draft: `{docs_dir}/tickets/EPIC-{slug}.md`.\n- Research pack: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/research-pack.md` / `.json`.\n- Standards checklist: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/standards-checklist.json`.\n- Resolved uncertainties: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/resolved-uncertainties.md`.\n- Goals & NFR framing: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/goals-and-nfrs.md` (its System Goals/NFRs and any provisional Recommended Implementation Order should inform the child breakdown and ordering).\n- Hard cap variable `{max_children}` (string integer; default `\"10\"` when not set by the caller). The default `\"10\"` is a **hard ceiling / upper bound, not a target** child count — it caps how many children are allowed, and is **not a goal to fill**. The normal target child count is smaller (fewer, larger M/L slices); see the sizing heuristics in step 2.\n\n## Instructions\n\n1. Read the Epic parent draft, research pack, standards checklist, and resolved uncertainties. Use only this context plus optional narrow web search; do not call deep research from this step.\n\n2. Propose ordered child tickets that, together, fully implement the Epic.\n\n **Sizing.** The canonical ladder is the posture block at the end of this file. Size each proposed child by its expected **file-touch breadth and depth plus rough lines of code (LOC) changed**, and apply the posture's target priority: **L (target) -> XL (when the work does not fit in L) -> M (third choice) -> S (only when unavoidable)**. An XL child is allowed and preferred over splitting a coherent slice to fit `L`. Only a child running past roughly 40 files / ~3000 LOC is rejected before output, and it is rejected rather than shipped with a note.\n\n Bias the decomposition toward **fewer, larger, independently implementable vertical slices** rather than many tiny one-feature children. The Bridge implementation tooling works better on large vertical slices, and a swarm of tiny S children magnifies sibling merge risk under parallel execution while spending the run's budget on coordination. Each child should be an independently implementable vertical slice; if a proposed child outgrows L, emit it as one XL child rather than halving it.\n\n Each proposed child must include:\n - `summary` — Jira title.\n - `issue_type` — typically `Task`; use `Spike` only for primarily discovery children.\n - `rationale` — short explanation of why this child exists and what it produces. Include a brief size estimate inside this existing field (do **not** add a new `size` field), e.g. `Estimated size: M (~4 files / ~150 LOC)`.\n - `labels` — must include `ai-generated`, `idea-to-ticket`, `idea-to-ticket-child`, and the unique child idempotency label `bapi-idea-to-ticket-{run_id}-child-<N>` where `<N>` is the 1-based child index in the final ordered list.\n - `idempotency_label` — the same `bapi-idea-to-ticket-{run_id}-child-<N>` string.\n - `draft_path` — `{docs_dir}/tickets/TICKET-{slug}-child-<N>.md` (drafts written by `jira-ticket-writer` later).\n - `depends_on` — array of the 1-based child indexes that are **hard prerequisites** (must land first), or empty. Keep this list minimal and real.\n - `recommended_after` — array of child indexes that are **soft sequencing** preferences (nicer to do after, but not blockers), or empty.\n - `order_rationale` — one line explaining why this child sits at this point in the order.\n\n Keep hard prerequisites (`depends_on`) strictly separate from soft sequencing (`recommended_after`). These fields drive the recommended implementation order posted to the epic later; they do **not** create Jira dependency links.\n\n3. Hard cap enforcement. First attempt a normal, smaller M/L-biased decomposition per the step 2 sizing heuristics. Then count proposed children: `{max_children}` is a hard ceiling that **halts on exceed**, not a target to fill. If the count exceeds `{max_children}` (parsed as an integer), halt locally with a clear \"split first\" message: ask the user to split the idea into multiple smaller Epics or to raise `--max-children` deliberately. Do not silently truncate.\n\n4. Per-child duplicate lookup. For each proposed child (in order), call `get_tickets` once with a title/keyword search built from the child's summary. If a clear duplicate exists, drop that child from the plan and record the drop reason; never halt the whole run because a child has a duplicate. Re-number `<N>` only after all drops are finalized so child indexes are contiguous.\n\n5. Per-child research is restricted to the parent research pack plus optional narrow web search. Do not call deep research per child.\n\n6. **This ordered plan is the frozen manifest.** Once written it is the sole source of child summaries, rationale, size bands, `depends_on`, `recommended_after`, order rationale, idempotency labels, and `draft_path`. Body rendering fans out against it (`render-ticket-manifest.md`) and must not re-decide any of those fields. Do not decompose a second time anywhere downstream: two independent decisions about one split disagree, and the disagreement surfaces as children that overlap or contradict their parent.\n\n7. Write `{docs_dir}/idea-to-ticket/{slug}-{run_id}/decomposition-plan.json` with at minimum:\n - `parent_summary` — copy from the parent draft.\n - `max_children` — the resolved integer value used for the cap.\n - `children` — ordered array of surviving children with all fields from step 2 (including `depends_on`, `recommended_after`, and `order_rationale`). After re-numbering in step 4, fix up the `depends_on`/`recommended_after` indexes so they still point at the correct surviving children.\n - `dropped_children` — array of `{proposed_summary, reason}` for children removed by duplicate lookup.\n\n\n<!-- BEGIN BRIDGE TICKET-AUTHORING POSTURE -->\n<!-- Canonical source: docs/bridge-ticket-authoring.md.\n This block is duplicated byte-identically onto every carrier. Never edit a\n copy: edit the canonical source and re-copy it verbatim. A cross-surface\n byte-equality test fails the build if any copy drifts by a single byte. -->\n\n## Ticket-authoring posture\n\nDeep reference: `docs/bridge-ticket-authoring.md`.\n\n**Draft through the writer.** Every ticket body — an epic parent, an epic child,\nand an ordinary sibling alike — is drafted by the `jira-ticket-writer` agent\nbefore `create_ticket` is called. Do not compose a ticket description inline.\n\n**Size the work.** Size each ticket by file-touch breadth and depth plus rough\nlines of code (LOC) changed:\n\n- `S = 1-2 files / <~80 LOC`\n- `M = ~3-8 files / ~80-400 LOC`\n- `L = ~8-15 files / ~400-900 LOC`\n- `XL = >15 files / >~900 LOC`\n\nTarget size priority: **L (target) -> XL (when the work does not fit in L) -> M\n(third choice) -> S (only when unavoidable)**. This applies equally to a\nstandalone ticket and to an epic child.\n\nAim each slice at L. When one will not fit, grow it to XL rather than splitting\nit — split only when the slice is genuinely two independent pieces of work,\nnever merely to land inside a band. Bridge's grooming and implementation process\nhandles a large vertical slice well and is overkill on small ones: every extra\nticket is another worktree, another PR, another rebase, and another chance for\ntwo workers to touch the same file. Reach for M because the work genuinely is\nthat size, not to avoid an XL.\n\nBeyond roughly 40 files or ~3000 LOC, split anyway. Past that point review\nturnaround and rebase cost dominate the run's budget, and a review that wedges\nholds the gate to its full retry ceiling before anyone notices.\n\n**Group at three.** Three or more implementable tickets is an epic: propose an\nepic parent plus an ordered child manifest, and resolve this surface's own\napproval gate before anything is created. One or two tickets are ordinary\nsiblings — no epic parent, no manifest. The threshold is exactly three.\n\n**Hand off once.** An epic handoff names exactly one conductor entry point,\n`drive-epic`, which selects the runnable path itself. Never present a choice\nbetween conductors.\n\n**Departure is closed-list only.** These three exceptions, and no others, permit\ndeparting from the rules above. Invoking one requires no announcement.\n\n- **E1 External-tracker mirroring** — a recorded upstream identifier exists and\n its granularity is contractual. Bypasses sizing and the epic threshold.\n- **E2 Discovery-only spike** — no committed production-code deliverable.\n Bypasses sizing only; does not bypass drafting through the writer.\n- **E3 Authorized incident containment** — tied to an active incident record,\n not to schedule pressure. Bypasses sizing and the epic threshold.\n\nThe list is closed. Anything outside it is an escalation to the operator, not a\njudgement call. Explicitly refused as grounds for departure: a single-file\ntrivial fix (that is `S` reached through the normal path, not an exception),\ngeneric time pressure, \"already well specified\", \"faster without the writer\",\ndeveloper discretion, minor refactor, unattended mode, context limits, and \"hard\nto decompose\" (XL is the normal overflow, so that is the ordinary path and not a\ndeparture). Writer unavailability escalates; it never silently authorizes inline\ndrafting.\n\n<!-- END BRIDGE TICKET-AUTHORING POSTURE -->\n\n## Return\n\nConfirm `decomposition-plan.json` was written, report the final child count and the number of children dropped for duplicate reasons.\n",
986
+ "decompose-epic.md": "Decompose the epic into manageable sub-tasks and get user approval.\n\n## Epic Description\n\n{epic_description}\n\n## Instructions\n\n1. Read the following artifacts to establish full context. If a file does not exist or is empty, proceed without it:\n - `{docs_dir}/epic-plans/{epic_slug}/research-findings.md`\n - `{docs_dir}/epic-plans/{epic_slug}/codebase-exploration.md`\n\n2. Reason about the epic and produce a decomposition. Consider:\n - Logical groupings of work that can be implemented and tested independently\n - Dependencies between sub-tasks (what must be built first)\n - A reasonable scope for each sub-task (each should be achievable in a single implementation session)\n\n **Size every sub-task with the canonical ladder** in the posture block at the end of this file, by file-touch breadth and depth plus rough LOC changed. Apply its target priority: **L (target) -> XL (when the work does not fit in L) -> M (third choice) -> S (only when unavoidable)**. **A sub-task may be XL.** When one will not fit in `L`, grow it to `XL` rather than splitting it; split only when it is genuinely two independent pieces of work, or when it runs past roughly 40 files / ~3000 LOC. Bias toward fewer, larger, independently implementable vertical slices: a swarm of tiny children magnifies sibling merge risk under parallel conductor execution and spends the run's budget on coordination.\n\n **Decompose once.** This pass freezes the split. Emit the same ordered-manifest shape the `idea-to-ticket` path uses (`decompose-epic-candidate.md`), so that per sub-task you record: title, scope boundary, **size band**, `depends_on` (hard prerequisites that must land first), `recommended_after` (soft sequencing preferences that are not blockers), and a one-line order rationale. Keep hard prerequisites strictly separate from soft sequencing — the recommended implementation order is derived from them, and conflating the two serializes work that could have run in parallel. No later stage re-derives a sub-task's scope while rendering its body.\n\n **Three or more sub-tasks is an epic; one or two would ordinarily be siblings.** The threshold is exactly three. This surface is the one place that threshold does *not* decide the shape: `/plan-epic` runs against an epic ticket that already exists, so the grouping decision was made upstream and is not yours to reverse here. If the epic genuinely decomposes into only one or two sub-tasks, **say so plainly to the user** — that is a signal the epic was over-scoped and may be better as ordinary sibling tickets — and then continue with the steps below as written. Do not manufacture filler sub-tasks to reach three, and do not skip the artifacts step 3 requires; the `plan-epic` step sequence is fixed and every later step reads them.\n\n3. Write the decomposition to `{docs_dir}/epic-plans/{epic_slug}/epic-plan.md` with this format:\n\n```markdown\n# Epic Decomposition\n\n## Sub-tasks\n\n### 1. {Sub-task title}\n- **Scope**: {What this sub-task covers}\n- **Size**: {S | M | L | XL}\n- **Key files/areas**: {Files and code areas involved}\n- **Dependencies**: {Other sub-task numbers this depends on, or \"None\"}\n- **Recommended after**: {Soft sequencing preferences that are not blockers, or \"None\"}\n- **Order rationale**: {One line on why this sub-task sits at this point in the order}\n\n### 2. {Sub-task title}\n...\n```\n\n In addition, you MUST also write a structured JSON sidecar at\n `{docs_dir}/epic-plans/{epic_slug}/epic-plan.dag.json`. This is unconditional —\n it applies to a one- or two-sub-task decomposition exactly as it does to a larger\n one. `plan-epic.json`'s step sequence is fixed (the orchestrator supports no\n conditional steps), so every later step reads these artifacts regardless of the\n sub-task count, and omitting them strands the run rather than simplifying it.\n This file is the machine-readable intermediate consumed by the `plan-epic`\n pipeline to store and approve the plan in the backend — it must be written\n from your structured decomposition data, NEVER by re-parsing the markdown.\n\n The sidecar format is:\n\n```json\n{\n \"plan_version\": 1,\n \"nodes\": [\n {\n \"ticket_key\": \"BAPI-XXX\",\n \"status\": \"planned\",\n \"depends_on\": [],\n \"touched_files\": [\"api/routes/example.py\", \"src/python/llms/ai_client.py\"],\n \"automations\": [\n { \"kind\": \"start-tickets\" }\n ]\n }\n ],\n \"edges\": [\n { \"from\": \"BAPI-XXX\", \"to\": \"BAPI-YYY\" }\n ]\n}\n```\n\n Rules for the sidecar:\n - `plan_version` must be 1 for a new plan (an integer, never a float).\n - Each node `ticket_key` must match the Jira key of the created sub-task\n (populated after Jira ticket creation in a later pipeline step; use the\n planned Jira key if known, or a placeholder like \"TBD-1\" if not yet created).\n - `ticket_key` values must be unique and non-empty after trimming.\n - `depends_on` lists the `ticket_key` values this node depends on (mirrors\n the markdown Dependencies field). `depends_on` is the **canonical**\n dependency declaration.\n - `status` must be `\"planned\"` for newly-created sub-tasks.\n - `touched_files` is **required on every node** — the sorted, deduplicated,\n repository-relative POSIX paths this sub-task is predicted to change.\n Populate it from your current code research and the sub-task's ownership,\n the same analysis that tells you which sub-tasks collide.\n - No leading `./`, no absolute paths, no `..` traversal segments, no\n temporary-worktree paths, and no prose. Declare **concrete files**: the\n backend intersects these paths as exact strings, so a directory or glob\n entry matches nothing and protects nothing.\n - `[]` is permitted **only** when the decomposition explicitly establishes\n that the sub-task changes no repository files. Unknown file ownership is\n **blocking** — resolve it or flag it; never represent it as an empty\n array, which silently disables file-overlap protection.\n - `automations` lists automation kinds to run on the ticket — valid values\n are `\"start-tickets\"` and `\"review-tickets\"`. Use an empty array if none.\n - `edges` is an explicit list of directed dependency edges (from → to, where\n `from` is the predecessor). It may be empty if all dependencies are captured\n in `depends_on`, but any edge you do write must encode the same graph as\n `depends_on` — a contradictory graph blocks readiness.\n - Do **not** set `base_lineage` on any node. It affects the plan hash but has\n no consumer in either conductor, so setting it invalidates the approved hash\n for no behavioral gain.\n - The DAG must be acyclic (no circular dependencies).\n - The deterministic Jira-dependency-link DAG builder is the documented\n fallback/recovery path if this sidecar is lost or corrupted (not built here).\n\n **Placeholders are finalized later, not here.** `TBD-N` remains the correct\n node identity for the window before the epic's Jira tickets exist. The\n standalone `emit-conductor-bundle.md` task replaces every placeholder with the\n real ticket key and finalizes `touched_files` after the tickets are created and\n before the plan is stored or approved. Write your best `touched_files`\n prediction here anyway — that task reconciles it against the whole epic.\n\n4. **Soft limit check**: If the decomposition results in more than 8 sub-tasks, you must verbally warn the user: \"This decomposition has N sub-tasks, which exceeds the recommended limit of 8. Consider splitting this feature into multiple epics.\" Then proceed with the approval flow.\n\n5. Present the decomposition to the user and ask for their feedback. Explain the reasoning behind the breakdown and the dependency ordering.\n\n6. You MUST stop and wait for the user to respond. Do NOT assume approval. Do NOT proceed to the next step.\n\n7. If the user provides feedback or rejects the decomposition:\n - Incorporate their feedback\n - Rewrite `{docs_dir}/epic-plans/{epic_slug}/epic-plan.md` with the revised version\n - Present the revised decomposition and ask for approval again\n - Repeat until the user explicitly approves\n\n8. Only after explicit user approval, confirm: \"Decomposition approved. Proceeding to sub-task exploration.\"\n\n\n<!-- BEGIN BRIDGE TICKET-AUTHORING POSTURE -->\n<!-- Canonical source: docs/bridge-ticket-authoring.md.\n This block is duplicated byte-identically onto every carrier. Never edit a\n copy: edit the canonical source and re-copy it verbatim. A cross-surface\n byte-equality test fails the build if any copy drifts by a single byte. -->\n\n## Ticket-authoring posture\n\nDeep reference: `docs/bridge-ticket-authoring.md`.\n\n**Draft through the writer.** Every ticket body — an epic parent, an epic child,\nand an ordinary sibling alike — is drafted by the `jira-ticket-writer` agent\nbefore `create_ticket` is called. Do not compose a ticket description inline.\n\n**Size the work.** Size each ticket by file-touch breadth and depth plus rough\nlines of code (LOC) changed:\n\n- `S = 1-2 files / <~80 LOC`\n- `M = ~3-8 files / ~80-400 LOC`\n- `L = ~8-15 files / ~400-900 LOC`\n- `XL = >15 files / >~900 LOC`\n\nTarget size priority: **L (target) -> XL (when the work does not fit in L) -> M\n(third choice) -> S (only when unavoidable)**. This applies equally to a\nstandalone ticket and to an epic child.\n\nAim each slice at L. When one will not fit, grow it to XL rather than splitting\nit — split only when the slice is genuinely two independent pieces of work,\nnever merely to land inside a band. Bridge's grooming and implementation process\nhandles a large vertical slice well and is overkill on small ones: every extra\nticket is another worktree, another PR, another rebase, and another chance for\ntwo workers to touch the same file. Reach for M because the work genuinely is\nthat size, not to avoid an XL.\n\nBeyond roughly 40 files or ~3000 LOC, split anyway. Past that point review\nturnaround and rebase cost dominate the run's budget, and a review that wedges\nholds the gate to its full retry ceiling before anyone notices.\n\n**Group at three.** Three or more implementable tickets is an epic: propose an\nepic parent plus an ordered child manifest, and resolve this surface's own\napproval gate before anything is created. One or two tickets are ordinary\nsiblings — no epic parent, no manifest. The threshold is exactly three.\n\n**Hand off once.** An epic handoff names exactly one conductor entry point,\n`drive-epic`, which selects the runnable path itself. Never present a choice\nbetween conductors.\n\n**Departure is closed-list only.** These three exceptions, and no others, permit\ndeparting from the rules above. Invoking one requires no announcement.\n\n- **E1 External-tracker mirroring** — a recorded upstream identifier exists and\n its granularity is contractual. Bypasses sizing and the epic threshold.\n- **E2 Discovery-only spike** — no committed production-code deliverable.\n Bypasses sizing only; does not bypass drafting through the writer.\n- **E3 Authorized incident containment** — tied to an active incident record,\n not to schedule pressure. Bypasses sizing and the epic threshold.\n\nThe list is closed. Anything outside it is an escalation to the operator, not a\njudgement call. Explicitly refused as grounds for departure: a single-file\ntrivial fix (that is `S` reached through the normal path, not an exception),\ngeneric time pressure, \"already well specified\", \"faster without the writer\",\ndeveloper discretion, minor refactor, unattended mode, context limits, and \"hard\nto decompose\" (XL is the normal overflow, so that is the ordinary path and not a\ndeparture). Writer unavailability escalates; it never silently authorizes inline\ndrafting.\n\n<!-- END BRIDGE TICKET-AUTHORING POSTURE -->\n\n## Return\n\nConfirm \"Decomposition approved.\" and report the final sub-task count plus the path to `{docs_dir}/epic-plans/{epic_slug}/epic-plan.md`. Flag if the count exceeded the recommended limit of 8.\n",
987
+ "draft-and-critique.md": "Draft the ticket(s) for this idea, run a BAPI-320 hygiene pass, and emit structured draft metadata.\n\n## Inputs\n\n- Run manifest: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/run-manifest.json`.\n- Research pack: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/research-pack.md` / `.json`.\n- Duplicate assessment: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/duplicate-assessment.json`.\n- Standards checklist: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/standards-checklist.json`.\n- Resolved uncertainties: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/resolved-uncertainties.md`.\n- Goals & NFR framing: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/goals-and-nfrs.md`.\n- Comp→codebase map (optional): `{docs_dir}/idea-to-ticket/{slug}-{run_id}/comp-analysis.json`. Produced upstream by the gated `comp-analysis.md` perception step. A missing, unreadable, or `applicable: false` map is treated exactly like `applicable: false` — a no-op: it never halts drafting and this step injects no visual-fidelity/comp language on its own.\n\n## Instructions\n\n1. **Resolve the regression-completeness gate once, before drafting.** Call the `config_field` MCP tool with `operation` set to `\"get\"` and `field_name` set to `enable_regression_checks`. Normalize the response: only the exact value `true` (a JSON boolean `true`, or the case-insensitive string `\"true\"`) counts as enabled. Treat a missing tool, a tool error, `null`, `false`, or any other/malformed value as disabled — this lookup is fail-open and defaults OFF. Store the normalized boolean as `enable_regression_checks` and reuse the same stored value for every `jira-ticket-writer` invocation this flow makes (including any later critique/revision call) — do not re-resolve it per call. The `jira-ticket-writer` sub-agent must not call `config_field` itself.\n\n2. Read the six required input artifacts in full before drafting, plus the comp-analysis map when it is present. The manifest's `scope` (`task`, `spike`, or `epic_candidate`) determines the drafting path.\n\n The goals-and-nfrs.md framing must shape every draft. Lead each draft with the **business goal** and **desired end-state**, and include an explicit **Non-Functional Requirements** section (and, where it clarifies behavior, the required **system behavior**). For the **epic_candidate** parent, these belong in the Epic description itself (the parent's `slim_description` should at least name the business goal + end-state, and the attached full draft must carry the Goals / Desired End-State / Non-Functional Requirements sections). Any NFR still marked `open` in the framing must be written into the draft as an explicit assumption plus an open-risk note — never silently dropped. This flow is documentary: do not generate a decision page and do not pause for clarification here.\n\n3. Drafting path by scope:\n - **task** or **spike**:\n - Call the `jira-ticket-writer` sub-agent with an explicit output path of `{docs_dir}/tickets/TICKET-{slug}.md`. The sub-agent must write the full markdown draft to that exact file. Pass the comp-analysis map path (`{docs_dir}/idea-to-ticket/{slug}-{run_id}/comp-analysis.json`) into the sub-agent prompt alongside the six existing input artifacts (run manifest, research pack, duplicate assessment, standards checklist, resolved uncertainties, goals-and-nfrs) and the stored `enable_regression_checks` value from step 1.\n - **epic_candidate**:\n - Call `jira-ticket-writer` to draft only the Epic parent. Use the explicit output path `{docs_dir}/tickets/EPIC-{slug}.md`. Pass the same comp-analysis map path into the sub-agent prompt alongside the six existing input artifacts and the stored `enable_regression_checks` value from step 1. Child tickets are produced later by `decompose-epic-candidate.md`; do not draft them here.\n\n Comp-analysis map consumption (both paths): when `comp-analysis.json` has `applicable: true`, tell the writer its Requirements MUST use the mapped components/templates/tokens/routes as concrete implementation guidance (e.g. \"reuse component X\", \"use token Z\", \"extend template Y\", \"wire route R\"). When the map has `applicable: false` or is missing/unreadable, tell the writer to ignore the artifact entirely and avoid mentioning comp analysis, design comps, or visual fidelity unless the original request independently requires those materials. This step only feeds the text JSON map to the writer — it never opens images, calls `describe_image()`, or performs vision analysis itself.\n\n Regression-gate consumption (both paths): state `enable_regression_checks: {stored value from step 1}` explicitly in the sub-agent prompt, and tell the writer this value was already resolved by the caller and must not be looked up again. The writer only runs its Regression Completeness Pass when this value is exactly `true`.\n\n4. **Every body goes through the writer, and the split is not yours to change.** The ticket-authoring posture (`docs/bridge-ticket-authoring.md`, carried verbatim by `decompose-epic-candidate.md` in this same flow) requires that every Jira body — Epic parent, child, and ordinary sibling alike — is drafted by `jira-ticket-writer`. State that posture explicitly in each sub-agent prompt, together with the approved manifest entry the invocation is bound to when one exists, so the writer knows it is rendering a frozen entry rather than deciding one.\n\n This applies identically to the **initial draft** and to any **revision** the hygiene/critique pass triggers. A revision re-invokes `jira-ticket-writer` with the *same* manifest entry and the *same* posture. Critique may improve a body's prose, evidence, and precision. It may not change the approved ticket count, the ordering, the parent relationship, an entry's dependency lists, its scope boundary, or its sizing classification — those were settled by the decomposition pass, and a critique pass sees one body at a time, so a split re-decided here is re-decided on strictly less information. A critique that believes the split is wrong reports that to the caller; it does not act on it.\n\n5. Issue type policy:\n - Default ambiguous ideas to `Task`.\n - Choose `Spike` only when the work is primarily discovery/research/learning with no clear acceptance criteria yet.\n - The Epic parent uses Jira issue type `Epic`.\n\n6. Hygiene pass (BAPI-320 forbidden tokens). After the sub-agent writes the draft, read it back and ensure none of these tokens are present:\n - markdown tables (any `|`-separated header row).\n - escaped pipe-table patterns (e.g. `\\|`).\n - task-list checkboxes such as `- [ ]` or `- [x]`.\n - angle-bracket placeholder tokens (any `<placeholder>` form, even inside backticks).\n - raw HTML blocks (`<div>`, `<br>`, `<table>`, etc.).\n When a forbidden token is found, rewrite the surrounding paragraph in plain prose or bullet form and save the cleaned draft over the same path.\n\n7. Write `{docs_dir}/idea-to-ticket/{slug}-{run_id}/draft-metadata.json` describing what Jira should later create.\n\n For **task** / **spike** scope, the metadata shape is:\n - `summary` — Jira ticket title.\n - `issue_type` — `Task` or `Spike`.\n - `labels` — array of Jira labels. Must include `ai-generated`, `idea-to-ticket`, the per-run label `bapi-idea-to-ticket-{run_id}`, and the stable idea-hash label `bapi-idea-hash-{idea_hash}` (so a future run of the same idea is caught by label).\n - `idempotency_label` — `bapi-idea-to-ticket-{run_id}` (matches the label used by the duplicate-and-context-scan step).\n - `slim_description` — short Jira-safe description (no forbidden tokens). The full draft is uploaded as an attachment. It must include a CONCISE, high-level summary of the draft's `## Materials & Access` inventory (Planning Assets, Downloadable Assets, Planning & Downloadable Assets vs. unphased record-only material), noting that the exhaustive list lives in the attached full draft. This keeps the missing-materials record visible to human reviewers and to description-reading review/critique flows.\n - `attachment_path` — `{docs_dir}/tickets/TICKET-{slug}.md` (or the equivalent path used above).\n\n For **epic_candidate** scope, the metadata shape is:\n - `parent.summary` — Epic title.\n - `parent.issue_type` — `Epic`.\n - `parent.labels` — must include `ai-generated`, `idea-to-ticket`, `bapi-idea-to-ticket-{run_id}-parent`, and the stable idea-hash label `bapi-idea-hash-{idea_hash}`.\n - `parent.idempotency_label` — `bapi-idea-to-ticket-{run_id}-parent`.\n - `parent.slim_description` — short Epic description. It must include a CONCISE, high-level summary of the Epic draft's `## Materials & Access` inventory (Planning Assets, Downloadable Assets, Planning & Downloadable Assets vs. unphased record-only material), noting that the exhaustive list lives in the attached full draft.\n - `parent.attachment_path` — `{docs_dir}/tickets/EPIC-{slug}.md`.\n - `children` — placeholder array. Populated later by `decompose-epic-candidate.md`; leave as an empty array here.\n\n8. Save the metadata exactly once. Downstream steps read this file; do not move it.\n\n## Return\n\nConfirm the draft path, the metadata path, and the chosen scope (`task`, `spike`, or `epic_candidate`).\n",
702
988
  "duplicate-and-context-scan.md": "Detect existing Jira tickets that duplicate or relate to this idea before any Jira mutation.\n\n## Inputs\n\n- Run manifest: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/run-manifest.json`.\n- Research pack: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/research-pack.md` (if produced).\n- Pipeline variable `allow_duplicate` controls override behavior (for this run, `allow_duplicate` = `{allow_duplicate}`). Treat the literal string `\"true\"` as override; any other value (including `\"false\"`, missing, or empty) is non-override.\n\n## Instructions\n\n> **Orchestrator-directed step.** This agent task is part of the full-automation chain and is authorized to call `get_tickets` as directed below — performing an orchestrator-directed tool call is not \"re-orchestrating\".\n\n1. Build at least two Jira search queries from the manifest:\n - **Title/keyword query**: use the most salient nouns from `idea` and `slug` as title/text keywords. Prefer 2-4 concrete terms over long natural-language sentences. Run via `get_tickets`.\n - **Stable idea-hash query** (the reliable cross-run dedup): run `get_tickets` with its `labels` parameter set to `bapi-idea-hash-{idea_hash}`. This label is identical for every run of the same idea, so it catches a PRIOR run that already created a ticket for this idea — even one created days ago. A hit here is a strong `duplicate` signal.\n - **Idempotency-label query**: run `get_tickets` with its `labels` parameter set to `bapi-idea-to-ticket-{run_id}` (the tool builds the `labels in (...)` JQL for you — do not pass a raw JQL string). This per-run label only matches a partial run of THIS same run, so it supports resume behavior.\n\n2. For each returned ticket, capture: ticket key, summary, status, and a short reason it matched (which query, which keyword).\n\n3. Classify the overall verdict as one of:\n - `duplicate` — at least one returned ticket clearly describes the same work as `idea`.\n - `related` — returned tickets are adjacent or partial overlaps but not the same work.\n - `none_found` — no meaningful matches.\n - `unable_to_check` — the Jira search itself failed (network error, auth error, JQL rejection). Record the failure and pick this verdict.\n\n4. Write `{docs_dir}/idea-to-ticket/{slug}-{run_id}/duplicate-assessment.json` with at minimum:\n - `verdict` — one of the four values above.\n - `matches` — array of `{ticket_key, summary, status, reason}` objects (may be empty).\n - `queries_used` — array of the actual JQL/search strings sent.\n - `allow_duplicate` — the resolved value of `{allow_duplicate}` for this run.\n\n5. Halt behavior:\n - If `verdict` is `duplicate` and `allow_duplicate` is not `\"true\"`, halt locally. Do not continue the pipeline. Tell the user that the duplicate halt is strict and that re-running with `--allow-duplicate` overrides it.\n - If `verdict` is `duplicate` and `allow_duplicate` is `\"true\"`, continue the pipeline but keep the duplicate evidence in the assessment file so downstream steps can reference it (e.g., to add a \"supersedes\" note to the draft).\n - For `related`, `none_found`, and `unable_to_check`, continue without halting.\n\n## Return\n\nConfirm `duplicate-assessment.json` was written, report `verdict`, and report whether the run is halting or continuing.\n",
703
- "emit-conductor-bundle.md": "Harmonize an epic's sibling tickets against each other and emit the artifacts the conductor needs, after the tickets exist and before the plan is approved.\n\nEach sibling ticket was drafted by a separate writer session that could not see\nits siblings. Each independently re-researched the codebase and re-derived\ninterfaces, names, and file ownership, so siblings routinely disagree exactly at\nthe seams where they must agree — the dependency edges. The decomposition itself\nwas coherent (one agent, one context, wrote `epic-plan.md` and every exploration\ndocument); that coherence is discarded at the handoff. This task reads every\nsibling in one context and restores it.\n\nThe window is forced. Real ticket keys exist only after creation, and description\nrewrites are free before approval but trip `spec_stale` after it.\n\n**This is a standalone task, deliberately not a step in `plan-epic.json`.**\n`plan-epic` creates no tickets, so wiring this in there would guarantee it runs\nbefore any sibling exists. The local agent invokes it directly, immediately after\ncreating the epic's tickets, passing the ordered node→key mapping it already\nholds.\n\n## Inputs\n\n- `epic_key` — the Jira key of the epic (e.g. `BAPI-634`). **Required.**\n `setup-epic --dry-run` needs it, it cannot be derived safely from the slug, and\n it cannot be recovered from Jira: `get_tickets` filters only by free-text\n `search` and `labels`, with no `parent_key` filter.\n- `epic_slug` — the sanitized slug naming the epic-plan directory.\n- An **ordered array of mapping entries**, one per sidecar node, each with:\n - `plan_node_id` — the node's identity in `epic-plan.dag.json` (a `TBD-N`\n placeholder on the initial run, or the real key on a rerun).\n - `ticket_key` — the real Jira key created for that node.\n - `exploration_path` — that sub-task's exploration document, relative to the\n epic-plan directory.\n - `draft_path` — that sub-task's draft, relative to the epic-plan directory.\n\n**Node identity comes only from `plan_node_id`.** Never infer which node a ticket\nbelongs to from mapping order, ticket titles, description text, or a Jira search.\nA key list plus `epic_slug` cannot determine which real key replaces which\n`TBD-N`, and guessing attaches one ticket's dependencies to another.\n\n## Instructions\n\nResolve `docs_dir` by calling the `get_docs_dir` MCP tool (no parameters). The\nsole workspace for this task is `{docs_dir}/epic-plans/{epic_slug}`. Never read or\nwrite outside it.\n\nExecute the following phases **in this order**. The ordering is the safety\nproperty: every external write is gated behind local validation, so a failure\nleaves Jira untouched rather than half-mutated. Pipeline ordering cannot protect a\nmanual or recovery run, so this task owns it.\n\n### Phase 1 — Non-mutating run-state precheck\n\nBefore reading artifacts, confirm the epic is still in the pre-approval window:\n\n```bash\nnpx -y -p @bridge_gpt/mcp-server conductor epic-status --epic-key <epic_key> --json\n```\n\nThis is read-only. Interpret the result:\n\n- `{\"status\": \"unknown\", \"state\": null}` — no run exists yet. **Proceed**; the\n dry-run and a later `store-and-approve-epic-plan` will create it.\n- `epic_run.status` is `planning` **and** its plan version is not yet stored —\n a single unapproved planning run. **Proceed**; it may be reused.\n- `epic_run.status` is `active`, `blocked`, `done`, or `abandoned` — **halt.**\n- `epic_run.approved_plan_hash` is non-null (the plan is approved) — **halt.**\n- The sidecar's `plan_version` is already stored — **halt.** Plan blobs are\n immutable and post-approval description rewrites are forbidden; this needs an\n explicit re-plan.\n- Multiple runs, or any ambiguous/unreadable state — **halt.** Never guess.\n\nHalting here means reporting the state and stopping. Do not attempt a repair.\n\n### Phase 2 — Validate inputs and source artifacts\n\nAssemble the mapping into a JSON input document:\n\n```json\n{\n \"epic_key\": \"<epic_key>\",\n \"epic_slug\": \"<epic_slug>\",\n \"decomposition_fingerprint\": \"<stable fingerprint of this decomposition>\",\n \"mappings\": [\n {\n \"plan_node_id\": \"TBD-1\",\n \"ticket_key\": \"BAPI-635\",\n \"exploration_path\": \"explorations/01-<subtask-slug>.md\",\n \"draft_path\": \"drafts/01-<subtask-slug>.md\"\n }\n ]\n}\n```\n\nThen run the deterministic entry validation **before reading any mapped file,\nfetching any ticket, or writing any artifact**:\n\n```bash\nnpx -y @bridge_gpt/mcp-server emit-conductor-bundle validate \\\n --input <path to the input document> \\\n --json\n```\n\nThis writes nothing. A non-zero exit halts the task; relay its error verbatim.\nThese are entry-point checks, not agent judgment. It enforces:\n\n- `epic_key` and every `ticket_key` match the project's Jira key pattern, are\n non-empty and unique, and no child key equals `epic_key`.\n- `epic_slug` matches the sanitized slug grammar.\n- The mapping is **complete and one-to-one**: every sidecar node has exactly one\n entry, and every entry matches exactly one node — by `plan_node_id` on the\n initial run, or by its already-mapped `ticket_key` on a rerun.\n- An existing `sibling-ticket-manifest.json` agrees with the supplied mapping on\n epic identity, slug, plan version, decomposition fingerprint, and every\n node→key pair. **On disagreement, halt** — never silently prefer either source.\n- Every mapped path resolves beneath `{docs_dir}/epic-plans/{epic_slug}`.\n Absolute paths, `..` traversal, paths into another epic's directory, unknown\n mapping paths, and symlink escapes are all rejected before their content is read.\n\nThen load these **authoritative artifacts** into one working context:\n\n- `{docs_dir}/epic-plans/{epic_slug}/goals-and-nfrs.md`\n- `{docs_dir}/epic-plans/{epic_slug}/epic-plan.md`\n- `{docs_dir}/epic-plans/{epic_slug}/epic-plan.dag.json`\n- every mapped exploration document\n- every mapped draft\n\n**Halt** if any of them is missing, malformed, or unmatched. These are\nauthoritative inputs; proceeding without one voids the reconciliation guarantee.\n\nRe-fetch every mapped child ticket by its supplied key (`get_ticket`) and keep\nthe complete sibling set in one context. Reading them one at a time defeats the\nentire purpose of this task.\n\nDelimit every dynamically inserted Jira, plan, draft, or exploration excerpt with\ntriple quotes before analyzing it, and strip secret-like content before it can\nreach a prompt, a rendered description, decision material, a report, or an\nattachment.\n\n### Phase 3 — Harmonize the whole epic (in memory)\n\n**Authority order when sources conflict:**\n\n1. Exploration documents and current code research — primary evidence.\n2. Intended scope in `epic-plan.md` — what the decomposition meant to build.\n3. Current ticket descriptions — **outputs to be corrected, never authoritative\n inputs.**\n\nReview the complete sibling set once against this taxonomy, rather than a vague\n\"look for problems\" pass:\n\n- **interface drift** — the same function, endpoint, table, or field described\n differently across siblings.\n- **ownership collision** — two tickets both claiming to create the same file,\n symbol, or migration.\n- **orphan consumer** — a ticket consuming a seam no sibling produces.\n- **order violation** — a ticket assuming work a later sibling does.\n- **scope overlap** — two tickets covering the same work.\n- **vocabulary drift** — the same concept named inconsistently.\n- **NFR conflict** — a ticket contradicting `goals-and-nfrs.md`.\n\nEvery dependency edge must end up with an explicit producer, an explicit\nconsumer, an owned seam, an exact interface or artifact name, and a required\nordering.\n\n#### Canonical contract blocks\n\nRender each shared seam **once**, and insert byte-identical text into both the\nproducer's and the consumer's description. This is what makes \"described\nidentically at the edge\" checkable rather than a matter of opinion.\n\nUse stable delimiters and a stable seam identifier:\n\n```\n<!-- BEGIN CONTRACT: {seam_id} -->\n**Contract: {seam_id}**\n\n- **Producer**: {producer_ticket_key}\n- **Consumer(s)**: {consumer_ticket_keys}\n- **Interface**: {exact name and signature/shape}\n- **Ordering**: {what must exist before what}\n<!-- END CONTRACT: {seam_id} -->\n```\n\n`{seam_id}` is derived deterministically from the seam's identity (the interface\nor artifact name), so the same seam keeps the same id across runs. Everything\noutside a contract block stays ticket-specific: each ticket keeps its own\nsummary, scope, requirements, acceptance criteria, constraints, and risks.\n\n#### Fold in the exploration document\n\nThe description is the **only** channel a conductor worker can see. The\nreconciler resolves the spec and ships it in the job payload; the worker reads the\nticket, not the repo. `docs/tmp/epic-plans/` is gitignored and worker worktrees\nare cut from a pinned `origin/<base>` SHA, so exploration documents are never on\ndisk for the implementing agent.\n\nFold each sub-task's exploration document into its ticket under a deterministic\ndelimited section:\n\n```\n<!-- BEGIN EXPLORATION: {ticket_key} -->\n...\n<!-- END EXPLORATION: {ticket_key} -->\n```\n\nPreserve every implementation-relevant file, symbol, interface, constraint,\ndecision, and open risk. Resolve every mapped `TBD-N` token to its real key in\nticket prose, exploration content, drafts, and contract blocks — not only in the\nsidecar. Drafts authored before the tickets existed refer to siblings as `TBD-N`;\na worker receiving that text has no actionable dependency reference.\n\n#### The spec length bound — fail loud\n\nValidate every complete rendered description against the bound. The conductor's\npayload bound is **64,000 characters** (the reconciler no longer truncates\nsilently — overflow beyond its own bound is visibly marked). For Jira-backed\ntickets the **practical** limit binds far earlier: Jira caps a description at\n**32,767 characters**, so treat that as the real-world ceiling there.\n\nCount **Unicode code points**, not UTF-16 code units — the server's bound is a\nPython `len()`, so an astral character (an emoji, say) counts once there but\ntwice under a naive JavaScript `.length`, which would reject a description that\nactually fits.\n\n- Compact repetition and non-actionable prose **first**, without dropping\n actionable content.\n- If the required content still will not fit, **fail loud and escalate** as a\n decomposition/scope finding.\n- An attachment is **not** a workaround — the worker cannot see it.\n- Raising the server-side bound is out of scope.\n\n#### Auto-resolve vs escalate\n\n**Auto-resolve only** when the authoritative artifacts make the intended contract\nunambiguous: normalizing vocabulary, or copying a producer's already-defined\ninterface name to its consumer.\n\n**Escalate** any proposed change to scope, decomposition, ownership boundaries,\ndependencies, public contracts, migrations, NFRs, or acceptance criteria. A\ncontradiction implying a scope or decomposition change is not a wording fix;\nauto-resolving one silently is worse than the contradiction, because it launders a\nreal decision into prose nobody reviewed.\n\n#### Build `touched_files`\n\nThe file-ownership map you built to detect **ownership collision** *is*\n`touched_files`. This step is the only place with a whole-epic view, and nothing\nelse emits the field today.\n\n- Sorted, deduplicated, repository-relative POSIX paths — no leading `./`, no\n absolute paths, no `..` traversal, no temporary-worktree paths, no prose. The\n `finalize` step rejects anything else rather than dropping it. Concrete files\n only: the backend intersects these as exact strings, so a directory or glob\n entry protects nothing.\n- Every node carries the field. `[]` is valid **only** where the ticket genuinely\n touches no repository files and that is explicit.\n- **Unknown ownership is escalated, never encoded as `[]`** — an empty array\n silently disables overlap protection.\n- When two tickets legitimately touch the same file, **both** keep it. If one\n consumes an interface the other produces, add the semantic dependency\n explicitly rather than relying on overlap serialization for ordering.\n\n##### What `touched_files` can and cannot prove (BAPI-848)\n\n`touched_files` is a **pre-implementation prediction**, written before the work\nexists. Overlap serialization intersects those declared lists as exact strings,\nso it proves ordering **only for paths both nodes actually declared**. Its\ncoverage scope is `declared_files_only`, and the server now reports that scope\nalongside the counts.\n\nTwo consequences, both load-bearing:\n\n- **A passing dry run and `inserted_edges: 0` prove nothing about the\n implementation.** Zero edges means \"no two nodes declared the same path\". It\n does not mean the implementations will avoid each other. In epic BAPI-776 the\n plan reported `inserted_edges: 0` and all three tickets converged on one line\n of one undeclared file.\n- **An undeclared file is invisible to serialization**, whatever the work ends\n up touching. This is a **deliberately accepted limitation**: an implementation\n that touches a genuinely undeclared, non-discoverable file is not\n automatically serialized against its siblings. Merge-conflict detection plus\n `JOB_TYPE_REBASE` remains the reactive backstop.\n\nThe obvious \"fix\" — attaching a blanket set of hotspot paths to every node — is\n**wrong and must not be done.** It would serialize unrelated siblings and\neffectively disable the parallelism the conductor exists to provide.\n\n**Prefer removing a hotspot structurally over scheduling around it.** A test\nrunner, build manifest, or route table that enumerates its members by hand is a\nrepository-wide serialization point: every ticket that adds a member must edit\nthat one shared line, so any two such tickets conflict by construction, and a\nmember left off it silently runs nowhere. Replacing the enumeration with\ndiscovery removes the conflict class outright. Where that has been done, adding a\nmember no longer implies touching the shared file — so do not declare it as\nthough it does. Check the current state of the repository you are planning for\nrather than assuming either shape.\n\n### Phase 4 — Escalate and halt, if there are escalations\n\nAssign this invocation a local `harmonization_run_id`, and give each finding a\nstable id derived deterministically from its normalized class, its sorted\naffected ticket keys, and its seam or file identity — so the same contradiction\nkeeps the same id across runs and a resumed decision still applies.\n\nIf any finding is escalated, build **one consolidated decision page** using the\ninteraction established in `clarify-open-nfrs.md` — call `generate_decision_page`\nwith `artifact_type: \"pre_ticket_planning\"`, `ticket_key: {epic_slug}`,\n`output_subdir: \"epic-plans/{epic_slug}\"`, and\n`output_filename: \"{epic_slug}-harmonization-decisions.html\"`. Give each item its\ntaxonomy class, affected tickets, bounded conflicting evidence, options, and a\nrecommended resolution.\n\nBefore halting, atomically persist the validated manifest, the provisional report,\nand the decision-page reference. Then **halt without changing the sidecar or\nJira**. Stop and wait for the user; do not assume a resolution.\n\n**On resume**: read the manifest, re-fetch all tickets, re-evaluate the complete\nsibling set, apply the recorded decision, and continue from this phase. Do not\nrestart `plan-epic` — that risks recreating tickets or losing the key mapping. Do\nnot post a duplicate decision page or order comments.\n\n### Phase 5 — Stage the local bundle\n\nAdd the whole-epic ownership map to the input document as\n`touched_files_by_key` (real Jira key → its predicted files), then finalize:\n\n```bash\nnpx -y @bridge_gpt/mcp-server emit-conductor-bundle finalize \\\n --input <path to the input document> \\\n --json\n```\n\nThis atomically writes, in one deterministic step:\n\n1. The finalized `{docs_dir}/epic-plans/{epic_slug}/epic-plan.dag.json` — the\n validated node→key map applied across `nodes[].ticket_key`,\n `nodes[].depends_on`, and `edges[].from`/`to`, with each node's normalized\n `touched_files` attached and nothing else mutated. It rejects residual `TBD-`,\n duplicate keys, unknown references, self-dependencies, cycles, and ordinary\n edges that contradict the canonical `depends_on` graph.\n2. `{docs_dir}/epic-plans/{epic_slug}/sibling-ticket-manifest.json` — versioned\n schema, epic identity, sidecar plan version, decomposition fingerprint, the\n ordered mapping, run phase, decisions, and completed-mutation markers. It\n records both the validated source-sidecar identity and the finalized-sidecar\n identity, so a rerun can tell a legitimate finalized sidecar from a different\n decomposition, and it excludes descriptions, credentials, exploration bodies,\n and raw Jira responses.\n\nEvery write is a temp file in the destination directory followed by a rename, so\nan interrupted run cannot leave a partial recovery source. A non-zero exit halts\nthe task; relay its error verbatim.\n\nThen write the report artifacts:\n\n3. `{docs_dir}/epic-plans/{epic_slug}/harmonization-report.json` — versioned\n schema, run metadata, and bounded findings.\n4. A run-scoped archive beside it\n (`harmonization-report.{harmonization_run_id}.json`), **without replacing\n earlier archives**. The fixed path holds only the latest run, so without the\n archive one idempotent rerun would erase the only measurement of what this gate\n ever caught.\n\n- **Preserve `plan_version`.** Do not bump it: these changes finalize version 1\n *before* it is stored.\n- **Never set `base_lineage`.** It affects the plan hash but has no consumer in\n either conductor, so setting it invalidates the approved hash for no gain.\n\n### Phase 6 — The dry-run gate\n\n```bash\nnpx -y @bridge_gpt/mcp-server setup-epic \\\n --epic-key <epic_key> \\\n --plan-file <docs_dir>/epic-plans/<epic_slug>/epic-plan.dag.json \\\n --dry-run \\\n --json\n```\n\n`validateEpicPlanSidecar()` is a pure local function over the parsed sidecar —\nplan version, key uniqueness, `touched_files`, acyclicity — with no Jira\ndependency. Running it first costs nothing and removes the partial-mutation\nfailure mode entirely.\n\n- **Any non-zero exit halts.** Relay the command's failure text verbatim; it is\n already actionable.\n- **Any `touched_files` warning halts**, even on a successful exit. That warning\n means a node's ownership is undeclared, which is the exact gap this task exists\n to close: an undeclared node is never pre-serialized against an overlapping\n sibling, so it relies entirely on reactive conflict detection at the merge gate.\n Go back and declare it before proceeding.\n\n This halt is **this task's own planning-quality bar, not a server rejection.**\n Plan storage *accepts* an omitted `touched_files` as an explicit opt-out\n (BAPI-722), so nothing downstream will stop you — which is precisely why the\n standard has to be held here. A successful dry run reports\n `undeclared_nodes` and `undeclared_pairs_skipped` next to `inserted_edges`\n (BAPI-848); read those before treating a clean exit as coverage. (A *malformed* `touched_files` is different: the\n server does reject that outright with `VALIDATION`.)\n- **Never fall back** to POSTing `/jira/epic-runs/runs` directly, storing the\n plan, or approving it. A duplicate active run wedges the epic permanently.\n\nIf staging or the dry-run fails, leave Jira descriptions and attachments\nuntouched.\n\n### Phase 7 — Apply the external mutations\n\nOnly on a passing dry-run. Repeat the **Phase 1 run-state precheck immediately\nbefore the first Jira write**, and halt if it now shows approval, activation,\nblocking, plan storage, multiple runs, or an ambiguous state. On a resume,\nre-fetch all sibling tickets and re-evaluate the complete set before applying any\nrecorded decision.\n\n**Descriptions.** Compare each complete proposed description against the ticket's\ncurrent Jira description using the same normalization as\n`normalize_ticket_spec()`: collapse every run of whitespace to a single space and\ntrim, so cosmetic diffs never trigger a rewrite. A pure reflow or indentation\nedit is deliberately **not** a material change — the server treats it the same\nway, and diverging here would false-freeze tickets via `spec_stale`. Call\n`update_ticket_description` **only when the normalized descriptions differ\nmaterially**, and pass the **complete rendered description** — this is a full\nrewrite, not an append. Record each successful description mutation in the\nmanifest before moving to the next one, so a retry converges without duplicating\nwork.\n\n**Drafts and attachments.** Refresh the local draft with the reconciled complete\ndocument, then upload it with the `attachment` tool\n(`operation: \"upload\"`, `replace_existing: true`), which replaces any existing\nattachment of the same filename. Note it is delete-then-create rather than an\natomic revision, so the attachment id changes. If replacement is unavailable,\nmark the superseded local draft prominently and upload one deterministically named\nreconciled document instead. Skip the upload when an equivalent revision is\nalready present. **Do not add a Jira comment solely to refresh or supersede an\nattachment.** Record each draft/attachment mutation in the manifest so a retry\nskips completed equivalent work.\n\n**Finalize the reports.** After the required mutations converge, atomically\nrewrite the fixed and run-scoped reports with the actual mutation outcomes.\n\n### Clean reruns\n\nA rerun over an already-harmonized epic is a **clean no-op**. It must not call\n`update_ticket_description`, upload a duplicate attachment, add a comment, alter\nplan semantics, bump `plan_version`, approve anything, or create status rows. It\nstill runs the final dry-run validation, still emits the fixed report (with an\nempty `findings` array), and still preserves every prior archive. It may rewrite\nidentical local JSON whose bytes differ only by normalization.\n\n### Never do these\n\n- Never create or seed `epic_ticket_status` rows, and never add a fallback\n seeding path. Seeding is owned by `approve_epic_plan`, which seeds one\n `planned` row per plan node idempotently before flipping the run to `active`.\n- Never approve a plan, activate a run, or POST to `/jira/epic-runs/runs`.\n- Never set `base_lineage`.\n- Never rewrite a description after the run reaches `active` — that trips\n `spec_stale`.\n\n## Return\n\nReport:\n\n- The manifest path.\n- The fixed report path and the run-scoped archive path.\n- The ticket keys whose descriptions **changed**, and those left **unchanged**.\n- Attachment outcomes per ticket.\n- The dry-run result.\n- Exactly one status: **`ready_for_approval`** or **`decision_required`**.\n\nState explicitly that **approval remains a separate subsequent action** — this\ntask never approves the plan. On `ready_for_approval`, the operator runs\n`store-and-approve-epic-plan` next.\n",
989
+ "emit-conductor-bundle.md": "Harmonize an epic's sibling tickets against each other and emit the artifacts the conductor needs, after the tickets exist and before the plan is approved.\n\nEach sibling ticket was drafted by a separate writer session that could not see\nits siblings. Each independently re-researched the codebase and re-derived\ninterfaces, names, and file ownership, so siblings routinely disagree exactly at\nthe seams where they must agree — the dependency edges. The decomposition itself\nwas coherent (one agent, one context, wrote `epic-plan.md` and every exploration\ndocument); that coherence is discarded at the handoff. This task reads every\nsibling in one context and restores it.\n\nThe window is forced. Real ticket keys exist only after creation, and description\nrewrites are free before approval but trip `spec_stale` after it.\n\n**This is a standalone task, deliberately not a step in `plan-epic.json`.**\n`plan-epic` creates no tickets, so wiring this in there would guarantee it runs\nbefore any sibling exists. The local agent invokes it directly, immediately after\ncreating the epic's tickets, passing the ordered node→key mapping it already\nholds.\n\n## Inputs\n\n- `epic_key` — the Jira key of the epic (e.g. `BAPI-634`). **Required.**\n `setup-epic --dry-run` needs it, it cannot be derived safely from the slug, and\n it cannot be recovered from Jira: `get_tickets` filters only by free-text\n `search` and `labels`, with no `parent_key` filter.\n- `epic_slug` — the sanitized slug naming the epic-plan directory.\n- An **ordered array of mapping entries**, one per sidecar node, each with:\n - `plan_node_id` — the node's identity in `epic-plan.dag.json` (a `TBD-N`\n placeholder on the initial run, or the real key on a rerun).\n - `ticket_key` — the real Jira key created for that node.\n - `exploration_path` — that sub-task's exploration document, relative to the\n epic-plan directory.\n - `draft_path` — that sub-task's draft, relative to the epic-plan directory.\n\n**Node identity comes only from `plan_node_id`.** Never infer which node a ticket\nbelongs to from mapping order, ticket titles, description text, or a Jira search.\nA key list plus `epic_slug` cannot determine which real key replaces which\n`TBD-N`, and guessing attaches one ticket's dependencies to another.\n\n## Instructions\n\nResolve `docs_dir` by calling the `get_docs_dir` MCP tool (no parameters). The\nsole workspace for this task is `{docs_dir}/epic-plans/{epic_slug}`. Never read or\nwrite outside it.\n\nExecute the following phases **in this order**. The ordering is the safety\nproperty: every external write is gated behind local validation, so a failure\nleaves Jira untouched rather than half-mutated. Pipeline ordering cannot protect a\nmanual or recovery run, so this task owns it.\n\n### Phase 1 — Non-mutating run-state precheck\n\nBefore reading artifacts, confirm the epic is still in the pre-approval window:\n\n```bash\nnpx -y -p @bridge_gpt/mcp-server conductor epic-status --epic-key <epic_key> --json\n```\n\nThis is read-only. Interpret the result:\n\n- `{\"status\": \"unknown\", \"state\": null}` — no run exists yet. **Proceed**; the\n dry-run and a later `store-and-approve-epic-plan` will create it.\n- `epic_run.status` is `planning` **and** its plan version is not yet stored —\n a single unapproved planning run. **Proceed**; it may be reused.\n- `epic_run.status` is `active`, `blocked`, `done`, or `abandoned` — **halt.**\n- `epic_run.approved_plan_hash` is non-null (the plan is approved) — **halt.**\n- The sidecar's `plan_version` is already stored — **halt.** Plan blobs are\n immutable and post-approval description rewrites are forbidden; this needs an\n explicit re-plan.\n- Multiple runs, or any ambiguous/unreadable state — **halt.** Never guess.\n\nHalting here means reporting the state and stopping. Do not attempt a repair.\n\n### Phase 2 — Validate inputs and source artifacts\n\nAssemble the mapping into a JSON input document. Both the ordered mapping and\nthe `decomposition_fingerprint` are derived from the **approved ordered manifest**\n— the split the decomposition pass froze and the approval gate accepted — and\nnever from a fresh decomposition performed here. This task harmonizes bodies\nagainst each other; it does not re-decide the split, and a fingerprint computed\nover a newly derived decomposition would silently detach the bundle from the plan\nthat was actually approved.\n\n```json\n{\n \"epic_key\": \"<epic_key>\",\n \"epic_slug\": \"<epic_slug>\",\n \"decomposition_fingerprint\": \"<stable fingerprint of this decomposition>\",\n \"mappings\": [\n {\n \"plan_node_id\": \"TBD-1\",\n \"ticket_key\": \"BAPI-635\",\n \"exploration_path\": \"explorations/01-<subtask-slug>.md\",\n \"draft_path\": \"drafts/01-<subtask-slug>.md\"\n }\n ]\n}\n```\n\nThen run the deterministic entry validation **before reading any mapped file,\nfetching any ticket, or writing any artifact**:\n\n```bash\nnpx -y @bridge_gpt/mcp-server emit-conductor-bundle validate \\\n --input <path to the input document> \\\n --json\n```\n\nThis writes nothing. A non-zero exit halts the task; relay its error verbatim.\nThese are entry-point checks, not agent judgment. It enforces:\n\n- `epic_key` and every `ticket_key` match the project's Jira key pattern, are\n non-empty and unique, and no child key equals `epic_key`.\n- `epic_slug` matches the sanitized slug grammar.\n- The mapping is **complete and one-to-one**: every sidecar node has exactly one\n entry, and every entry matches exactly one node — by `plan_node_id` on the\n initial run, or by its already-mapped `ticket_key` on a rerun.\n- An existing `sibling-ticket-manifest.json` agrees with the supplied mapping on\n epic identity, slug, plan version, decomposition fingerprint, and every\n node→key pair. **On disagreement, halt** — never silently prefer either source.\n- Every mapped path resolves beneath `{docs_dir}/epic-plans/{epic_slug}`.\n Absolute paths, `..` traversal, paths into another epic's directory, unknown\n mapping paths, and symlink escapes are all rejected before their content is read.\n\nThen load these **authoritative artifacts** into one working context:\n\n- `{docs_dir}/epic-plans/{epic_slug}/goals-and-nfrs.md`\n- `{docs_dir}/epic-plans/{epic_slug}/epic-plan.md`\n- `{docs_dir}/epic-plans/{epic_slug}/epic-plan.dag.json`\n- every mapped exploration document\n- every mapped draft\n\n**Halt** if any of them is missing, malformed, or unmatched. These are\nauthoritative inputs; proceeding without one voids the reconciliation guarantee.\n\nRe-fetch every mapped child ticket by its supplied key (`get_ticket`) and keep\nthe complete sibling set in one context. Reading them one at a time defeats the\nentire purpose of this task.\n\nDelimit every dynamically inserted Jira, plan, draft, or exploration excerpt with\ntriple quotes before analyzing it, and strip secret-like content before it can\nreach a prompt, a rendered description, decision material, a report, or an\nattachment.\n\n### Phase 3 — Harmonize the whole epic (in memory)\n\n**Authority order when sources conflict:**\n\n1. Exploration documents and current code research — primary evidence.\n2. Intended scope in `epic-plan.md` — what the decomposition meant to build.\n3. Current ticket descriptions — **outputs to be corrected, never authoritative\n inputs.**\n\nReview the complete sibling set once against this taxonomy, rather than a vague\n\"look for problems\" pass:\n\n- **interface drift** — the same function, endpoint, table, or field described\n differently across siblings.\n- **ownership collision** — two tickets both claiming to create the same file,\n symbol, or migration.\n- **orphan consumer** — a ticket consuming a seam no sibling produces.\n- **order violation** — a ticket assuming work a later sibling does.\n- **scope overlap** — two tickets covering the same work.\n- **vocabulary drift** — the same concept named inconsistently.\n- **NFR conflict** — a ticket contradicting `goals-and-nfrs.md`.\n\nEvery dependency edge must end up with an explicit producer, an explicit\nconsumer, an owned seam, an exact interface or artifact name, and a required\nordering.\n\n#### Canonical contract blocks\n\nRender each shared seam **once**, and insert byte-identical text into both the\nproducer's and the consumer's description. This is what makes \"described\nidentically at the edge\" checkable rather than a matter of opinion.\n\nUse stable delimiters and a stable seam identifier:\n\n```\n<!-- BEGIN CONTRACT: {seam_id} -->\n**Contract: {seam_id}**\n\n- **Producer**: {producer_ticket_key}\n- **Consumer(s)**: {consumer_ticket_keys}\n- **Interface**: {exact name and signature/shape}\n- **Ordering**: {what must exist before what}\n<!-- END CONTRACT: {seam_id} -->\n```\n\n`{seam_id}` is derived deterministically from the seam's identity (the interface\nor artifact name), so the same seam keeps the same id across runs. Everything\noutside a contract block stays ticket-specific: each ticket keeps its own\nsummary, scope, requirements, acceptance criteria, constraints, and risks.\n\n#### Fold in the exploration document\n\nThe description is the **only** channel a conductor worker can see. The\nreconciler resolves the spec and ships it in the job payload; the worker reads the\nticket, not the repo. `docs/tmp/epic-plans/` is gitignored and worker worktrees\nare cut from a pinned `origin/<base>` SHA, so exploration documents are never on\ndisk for the implementing agent.\n\nFold each sub-task's exploration document into its ticket under a deterministic\ndelimited section:\n\n```\n<!-- BEGIN EXPLORATION: {ticket_key} -->\n...\n<!-- END EXPLORATION: {ticket_key} -->\n```\n\nPreserve every implementation-relevant file, symbol, interface, constraint,\ndecision, and open risk. Resolve every mapped `TBD-N` token to its real key in\nticket prose, exploration content, drafts, and contract blocks — not only in the\nsidecar. Drafts authored before the tickets existed refer to siblings as `TBD-N`;\na worker receiving that text has no actionable dependency reference.\n\n#### The spec length bound — fail loud\n\nValidate every complete rendered description against the bound. The conductor's\npayload bound is **64,000 characters** (the reconciler no longer truncates\nsilently — overflow beyond its own bound is visibly marked). For Jira-backed\ntickets the **practical** limit binds far earlier: Jira caps a description at\n**32,767 characters**, so treat that as the real-world ceiling there.\n\nCount **Unicode code points**, not UTF-16 code units — the server's bound is a\nPython `len()`, so an astral character (an emoji, say) counts once there but\ntwice under a naive JavaScript `.length`, which would reject a description that\nactually fits.\n\n- Compact repetition and non-actionable prose **first**, without dropping\n actionable content.\n- If the required content still will not fit, **fail loud and escalate** as a\n decomposition/scope finding.\n- An attachment is **not** a workaround — the worker cannot see it.\n- Raising the server-side bound is out of scope.\n\n#### Auto-resolve vs escalate\n\n**Auto-resolve only** when the authoritative artifacts make the intended contract\nunambiguous: normalizing vocabulary, or copying a producer's already-defined\ninterface name to its consumer.\n\n**Escalate** any proposed change to scope, decomposition, ownership boundaries,\ndependencies, public contracts, migrations, NFRs, or acceptance criteria. A\ncontradiction implying a scope or decomposition change is not a wording fix;\nauto-resolving one silently is worse than the contradiction, because it launders a\nreal decision into prose nobody reviewed.\n\n#### Build `touched_files`\n\nThe file-ownership map you built to detect **ownership collision** *is*\n`touched_files`. This step is the only place with a whole-epic view, and nothing\nelse emits the field today.\n\n- Sorted, deduplicated, repository-relative POSIX paths — no leading `./`, no\n absolute paths, no `..` traversal, no temporary-worktree paths, no prose. The\n `finalize` step rejects anything else rather than dropping it. Concrete files\n only: the backend intersects these as exact strings, so a directory or glob\n entry protects nothing.\n- Every node carries the field. `[]` is valid **only** where the ticket genuinely\n touches no repository files and that is explicit.\n- **Unknown ownership is escalated, never encoded as `[]`** — an empty array\n silently disables overlap protection.\n- When two tickets legitimately touch the same file, **both** keep it. If one\n consumes an interface the other produces, add the semantic dependency\n explicitly rather than relying on overlap serialization for ordering.\n\n##### What `touched_files` can and cannot prove (BAPI-848)\n\n`touched_files` is a **pre-implementation prediction**, written before the work\nexists. Overlap serialization intersects those declared lists as exact strings,\nso it proves ordering **only for paths both nodes actually declared**. Its\ncoverage scope is `declared_files_only`, and the server now reports that scope\nalongside the counts.\n\nTwo consequences, both load-bearing:\n\n- **A passing dry run and `inserted_edges: 0` prove nothing about the\n implementation.** Zero edges means \"no two nodes declared the same path\". It\n does not mean the implementations will avoid each other. In epic BAPI-776 the\n plan reported `inserted_edges: 0` and all three tickets converged on one line\n of one undeclared file.\n- **An undeclared file is invisible to serialization**, whatever the work ends\n up touching. This is a **deliberately accepted limitation**: an implementation\n that touches a genuinely undeclared, non-discoverable file is not\n automatically serialized against its siblings. Merge-conflict detection plus\n `JOB_TYPE_REBASE` remains the reactive backstop.\n\nThe obvious \"fix\" — attaching a blanket set of hotspot paths to every node — is\n**wrong and must not be done.** It would serialize unrelated siblings and\neffectively disable the parallelism the conductor exists to provide.\n\n**Prefer removing a hotspot structurally over scheduling around it.** A test\nrunner, build manifest, or route table that enumerates its members by hand is a\nrepository-wide serialization point: every ticket that adds a member must edit\nthat one shared line, so any two such tickets conflict by construction, and a\nmember left off it silently runs nowhere. Replacing the enumeration with\ndiscovery removes the conflict class outright. Where that has been done, adding a\nmember no longer implies touching the shared file — so do not declare it as\nthough it does. Check the current state of the repository you are planning for\nrather than assuming either shape.\n\n### Phase 4 — Escalate and halt, if there are escalations\n\nAssign this invocation a local `harmonization_run_id`, and give each finding a\nstable id derived deterministically from its normalized class, its sorted\naffected ticket keys, and its seam or file identity — so the same contradiction\nkeeps the same id across runs and a resumed decision still applies.\n\nIf any finding is escalated, build **one consolidated decision page** using the\ninteraction established in `clarify-open-nfrs.md` — call `generate_decision_page`\nwith `artifact_type: \"pre_ticket_planning\"`, `ticket_key: {epic_slug}`,\n`output_subdir: \"epic-plans/{epic_slug}\"`, and\n`output_filename: \"{epic_slug}-harmonization-decisions.html\"`. Give each item its\ntaxonomy class, affected tickets, bounded conflicting evidence, options, and a\nrecommended resolution.\n\nBefore halting, atomically persist the validated manifest, the provisional report,\nand the decision-page reference. Then **halt without changing the sidecar or\nJira**. Stop and wait for the user; do not assume a resolution.\n\n**On resume**: read the manifest, re-fetch all tickets, re-evaluate the complete\nsibling set, apply the recorded decision, and continue from this phase. Do not\nrestart `plan-epic` — that risks recreating tickets or losing the key mapping. Do\nnot post a duplicate decision page or order comments.\n\n### Phase 5 — Stage the local bundle\n\nAdd the whole-epic ownership map to the input document as\n`touched_files_by_key` (real Jira key → its predicted files), then finalize:\n\n```bash\nnpx -y @bridge_gpt/mcp-server emit-conductor-bundle finalize \\\n --input <path to the input document> \\\n --json\n```\n\nThis atomically writes, in one deterministic step:\n\n1. The finalized `{docs_dir}/epic-plans/{epic_slug}/epic-plan.dag.json` — the\n validated node→key map applied across `nodes[].ticket_key`,\n `nodes[].depends_on`, and `edges[].from`/`to`, with each node's normalized\n `touched_files` attached and nothing else mutated. It rejects residual `TBD-`,\n duplicate keys, unknown references, self-dependencies, cycles, and ordinary\n edges that contradict the canonical `depends_on` graph.\n2. `{docs_dir}/epic-plans/{epic_slug}/sibling-ticket-manifest.json` — versioned\n schema, epic identity, sidecar plan version, decomposition fingerprint, the\n ordered mapping, run phase, decisions, and completed-mutation markers. It\n records both the validated source-sidecar identity and the finalized-sidecar\n identity, so a rerun can tell a legitimate finalized sidecar from a different\n decomposition, and it excludes descriptions, credentials, exploration bodies,\n and raw Jira responses.\n\nEvery write is a temp file in the destination directory followed by a rename, so\nan interrupted run cannot leave a partial recovery source. A non-zero exit halts\nthe task; relay its error verbatim.\n\nThen write the report artifacts:\n\n3. `{docs_dir}/epic-plans/{epic_slug}/harmonization-report.json` — versioned\n schema, run metadata, and bounded findings.\n4. A run-scoped archive beside it\n (`harmonization-report.{harmonization_run_id}.json`), **without replacing\n earlier archives**. The fixed path holds only the latest run, so without the\n archive one idempotent rerun would erase the only measurement of what this gate\n ever caught.\n\n- **Preserve `plan_version`.** Do not bump it: these changes finalize version 1\n *before* it is stored.\n- **Never set `base_lineage`.** It affects the plan hash but has no consumer in\n either conductor, so setting it invalidates the approved hash for no gain.\n\n### Phase 6 — The dry-run gate\n\n```bash\nnpx -y @bridge_gpt/mcp-server setup-epic \\\n --epic-key <epic_key> \\\n --plan-file <docs_dir>/epic-plans/<epic_slug>/epic-plan.dag.json \\\n --dry-run \\\n --json\n```\n\n`validateEpicPlanSidecar()` is a pure local function over the parsed sidecar —\nplan version, key uniqueness, `touched_files`, acyclicity — with no Jira\ndependency. Running it first costs nothing and removes the partial-mutation\nfailure mode entirely.\n\n- **Any non-zero exit halts.** Relay the command's failure text verbatim; it is\n already actionable.\n- **Any `touched_files` warning halts**, even on a successful exit. That warning\n means a node's ownership is undeclared, which is the exact gap this task exists\n to close: an undeclared node is never pre-serialized against an overlapping\n sibling, so it relies entirely on reactive conflict detection at the merge gate.\n Go back and declare it before proceeding.\n\n This halt is **this task's own planning-quality bar, not a server rejection.**\n Plan storage *accepts* an omitted `touched_files` as an explicit opt-out\n (BAPI-722), so nothing downstream will stop you — which is precisely why the\n standard has to be held here. A successful dry run reports\n `undeclared_nodes` and `undeclared_pairs_skipped` next to `inserted_edges`\n (BAPI-848); read those before treating a clean exit as coverage. (A *malformed* `touched_files` is different: the\n server does reject that outright with `VALIDATION`.)\n- **Never fall back** to POSTing `/jira/epic-runs/runs` directly, storing the\n plan, or approving it. A duplicate active run wedges the epic permanently.\n\nIf staging or the dry-run fails, leave Jira descriptions and attachments\nuntouched.\n\n### Phase 7 — Apply the external mutations\n\nOnly on a passing dry-run. Repeat the **Phase 1 run-state precheck immediately\nbefore the first Jira write**, and halt if it now shows approval, activation,\nblocking, plan storage, multiple runs, or an ambiguous state. On a resume,\nre-fetch all sibling tickets and re-evaluate the complete set before applying any\nrecorded decision.\n\n**Descriptions.** Compare each complete proposed description against the ticket's\ncurrent Jira description using the same normalization as\n`normalize_ticket_spec()`: collapse every run of whitespace to a single space and\ntrim, so cosmetic diffs never trigger a rewrite. A pure reflow or indentation\nedit is deliberately **not** a material change — the server treats it the same\nway, and diverging here would false-freeze tickets via `spec_stale`. Call\n`update_ticket_description` **only when the normalized descriptions differ\nmaterially**, and pass the **complete rendered description** — this is a full\nrewrite, not an append. Record each successful description mutation in the\nmanifest before moving to the next one, so a retry converges without duplicating\nwork.\n\n**Drafts and attachments.** Refresh the local draft with the reconciled complete\ndocument, then upload it with the `attachment` tool\n(`operation: \"upload\"`, `replace_existing: true`), which replaces any existing\nattachment of the same filename. Note it is delete-then-create rather than an\natomic revision, so the attachment id changes. If replacement is unavailable,\nmark the superseded local draft prominently and upload one deterministically named\nreconciled document instead. Skip the upload when an equivalent revision is\nalready present. **Do not add a Jira comment solely to refresh or supersede an\nattachment.** Record each draft/attachment mutation in the manifest so a retry\nskips completed equivalent work.\n\n**Finalize the reports.** After the required mutations converge, atomically\nrewrite the fixed and run-scoped reports with the actual mutation outcomes.\n\n### Clean reruns\n\nA rerun over an already-harmonized epic is a **clean no-op**. It must not call\n`update_ticket_description`, upload a duplicate attachment, add a comment, alter\nplan semantics, bump `plan_version`, approve anything, or create status rows. It\nstill runs the final dry-run validation, still emits the fixed report (with an\nempty `findings` array), and still preserves every prior archive. It may rewrite\nidentical local JSON whose bytes differ only by normalization.\n\n### Never do these\n\n- Never create or seed `epic_ticket_status` rows, and never add a fallback\n seeding path. Seeding is owned by `approve_epic_plan`, which seeds one\n `planned` row per plan node idempotently before flipping the run to `active`.\n- Never approve a plan, activate a run, or POST to `/jira/epic-runs/runs`.\n- Never set `base_lineage`.\n- Never rewrite a description after the run reaches `active` — that trips\n `spec_stale`.\n\n## Return\n\nReport:\n\n- The manifest path.\n- The fixed report path and the run-scoped archive path.\n- The ticket keys whose descriptions **changed**, and those left **unchanged**.\n- Attachment outcomes per ticket.\n- The dry-run result.\n- Exactly one status: **`ready_for_approval`** or **`decision_required`**.\n\nState explicitly that **approval remains a separate subsequent action** — this\ntask never approves the plan. On `ready_for_approval`, the operator runs\n`store-and-approve-epic-plan` next.\n",
704
990
  "evaluate-and-recommend.md": "Evaluate the clarifying questions and ticket critiques generated for {ticket_key} against the actual codebase, then decorate every actionable item with the resolution guidance the reviewer will need on the decision page. The result is a single combined review-and-resolution document.\n\n## Phase 0 — Grounding & Audit Setup\n\nBefore gathering any source documents, extract the codebase-grounding context produced by the preceding `materialize_fresh_base` pipeline step:\n\n- Read the `materialize_fresh_base` tool result from earlier in this session. It returns JSON `{ base_sha, base_branch, fresh_base_root }` — or, when `no_refresh_base` was set, `{ base_sha: \"local-stale\", fresh_base_root: <original repo root> }`.\n- Retain `fresh_base_root` and `base_sha` for the rest of this procedure. Every codebase read in Phase 1 / Phase 2 below is grounded against `fresh_base_root`. `fresh_base_root` is also the exact value you must pass to the pipeline's later `cleanup_fresh_base` step — it is a *runtime* value returned by the tool call, not a static recipe variable, so pass the real path string you captured here, not any placeholder text shown in the step's params.\n- If the `materialize_fresh_base` step's result contains an `error` field and `no_refresh_base` was NOT set, this is the fail-loud condition the recipe's `on_error: \"halt\"` exists for: stop here, do not fall back to grounding against your own working directory, and report the failure (name the attempted base branch and the remediation — retry, or rerun with `--no-refresh-base`).\n\n**Metadata Audit Header** — the very first content of the generated review-and-resolution document, before any other section, must be:\n\n```\n**Base SHA**: <base_sha>\n**Base Branch**: <base_branch, or \"(local, in-place)\" when base_sha is \"local-stale\">\n**Grounding Status**: <Freshly Materialized | Stale/In-Place Fallback>\n```\n\n- `Grounding Status` is **Freshly Materialized** whenever `base_sha` is a real commit SHA (the normal path).\n- `Grounding Status` is **Stale/In-Place Fallback** whenever `base_sha` is exactly `local-stale` (the `--no-refresh-base` opt-out path). In this case, immediately follow the header with a prominent, bold, high-contrast warning block, for example:\n\n > **⚠ STALE GROUNDING — `--no-refresh-base` was used.** This review evaluated the codebase as checked out locally, NOT a freshly-fetched `origin/<base>`. `file:line` citations may reflect uncommitted or unmerged local state.\n\n**Codebase grounding rule**: Ground ALL file reads and codebase searches exclusively against the `fresh_base_root` directory extracted above. Do NOT read codebase files from your default working directory or session cwd — `fresh_base_root` is the only trustworthy source of truth for `file:line` citations in this procedure.\n\n**Original-repo rule**: Ticket docs, `{docs_dir}` inputs, and ALL output paths stay in the ORIGINAL repository, never the `fresh_base_root` temp dir. This includes the ticket-fetch call below, the clarifying-questions / critique source documents, and the saved review-and-resolution output file (see the Save rule at the bottom). Do NOT redirect any of these into `fresh_base_root`.\n\n**Path hygiene rule**: Every `file:line` citation and Codebase Evidence entry in the output document MUST be repo-relative — strip the `fresh_base_root` absolute-path prefix before writing it down. A citation must never contain a temp-dir / `/tmp/...`-style absolute path (write `src/foo.ts:10`, never `/tmp/bridge-review-.../src/foo.ts:10`).\n\n1. Fetch the current ticket description using the `get_ticket` tool with ticket_number `{ticket_key}` exactly once at the top of this procedure.\n\n2. Gather the clarifying questions and critique documents from the preceding pipeline steps. The local files at `{docs_dir}/clarifying-questions/{ticket_key}-clarifying-questions.md` and `{docs_dir}/ticket-critiques/{ticket_key}-ticket-quality-critique.md` are the canonical source. After a second-opinion run, each document has this shape:\n\n - A top-level H1 (`# Ticket Analysis` for clarifier docs, `# Ticket Quality Critique` for critique docs) followed by an italic provider-attribution line of the form `_This analysis was generated by GPT|Claude|Gemini._`. The attribution names the LLM family that produced the **first round**.\n - The first-round questions / critique items, exactly as written by the first-round model.\n - **Inline second-opinion blockquotes** nested directly under each prior item the second round addressed. Each blockquote starts with `> **Second opinion (<provider>) - <stance>.**` where `<provider>` is `GPT|Claude|Gemini` and `<stance>` is `concurrence|refinement|disagreement`. The blockquote is followed by `> *Citations: <comma-separated grounding refs>*`. Items the second round did **not** comment on have no blockquote — that is the \"weak concurrence\" signal. Use the provider name in the blockquote header to attribute the comment to the second-round LLM family in your evaluation prose where helpful.\n - A **`## New in Second Opinion`** tail block listing items the second round added on top of the first round. Immediately under the H2 you will find a second italic attribution line of the form `_These additional points were raised by GPT|Claude|Gemini._` — this names the second-round LLM family. Sub-headings are agent-specific:\n - Clarifier docs: `### New Requirements Questions` and `### New Technical Questions` — numbering continues from the prior section.\n - Critique docs: `### New Requested Changes` and `### New Points to Consider` — numbering continues from the prior section.\n Each new item has its own `*Citations: ...*` line.\n - A final **`## Second Opinion Summary`** footer (1-3 sentences) capturing the second round's overall position. This always renders, even when the second round had no inline comments and no new items.\n\n **Legacy fallback shape**: in rare cases (model lacks JSON-schema support, the JSON call failed, or the response could not be parsed), the document may instead end with `\\n\\n---\\n\\n` followed by a `## Second Opinion` section containing `### Response to Prior Items` and `### Additional Points` subsections. If you detect this fallback shape, treat it equivalently: subsection responses tagged `concurrence` map to weak/strong concurrence (use the body length to disambiguate — bare one-line concurrences are weak), `refinement`/`disagreement` map to the disagree buckets, and items under `### Additional Points` map to the gap-captured bucket below.\n\n **Partial-source-doc tolerance**: if the clarifying-questions doc OR the ticket-critique doc is missing or unreadable, skip that document silently and produce items only for the surviving doc. Do not fail. If **both** documents are absent, still write the combined output file at `{docs_dir}/review/{ticket_key}-review-and-resolution.md` with the standard top-level sections (`Confirmed Improvements`, `Needs Scrutiny`, `Open Questions`, `Round Agreement Summary`) present but no emitted E-items in any section. This preserves downstream file-existence expectations for the capture-review-decisions step.\n\n3. Determine **Round Agreement** for every clarifying question and critique point using these rules:\n\n - **Both rounds agree (weak concurrence)** — the prior item has NO inline blockquote AND is not in `## New in Second Opinion`. The second round did not object to the point and did not consider it important enough to comment on. Briefly validate the answer's groundedness against the codebase. If validation surfaces concerns, demote this item to **rounds disagree** (single round only depth) and treat as Needs Scrutiny.\n - **Both rounds agree (strong concurrence)** — the prior item carries an inline `> **Second opinion (<provider>) - concurrence.** ...` blockquote. The second round explicitly reinforced the prior point. Reuse the blockquote's `*Citations:*` as starting evidence; verify briefly.\n - **Rounds disagree (refinement)** — the prior item carries an inline `> **Second opinion (<provider>) - refinement.** ...` blockquote. The second round modified or added detail. Apply full disagreement-depth analysis; reuse blockquote citations.\n - **Rounds disagree (disagreement)** — the prior item carries an inline `> **Second opinion (<provider>) - disagreement.** ...` blockquote. The second round contradicts the prior. Apply full disagreement-depth analysis; categorize the outcome based on which position the codebase supports.\n - **Gap captured** — the item lives under `## New in Second Opinion > ### New <category>` (one of: New Requirements Questions, New Technical Questions, New Requested Changes, New Points to Consider). Apply the two-axis check below. Reuse the new item's `*Citations:*` as starting evidence.\n - **Single round only** — the document has none of the above markers (no inline blockquotes, no `## New in Second Opinion` block, no `## Second Opinion Summary` footer). The pipeline ran only one round. Treat every item as a disagreement: cite 2+ codebase locations and give full analytical depth.\n\n Apply these depth and categorization rules:\n\n - **Both rounds agree (weak concurrence)**: 1 codebase citation, 1-2 sentence assessment confirming grounding. Categorize as Confirmed Improvement if grounded; demote to Needs Scrutiny if validation finds problems.\n - **Both rounds agree (strong concurrence)**: 1 codebase citation (may reuse a blockquote citation), 1-2 sentence assessment. Categorize as Confirmed Improvement.\n - **Rounds disagree (refinement or disagreement)**: 2+ codebase citations, 3-4 sentence assessment that explicitly weighs the prior-round position against the second-opinion position. Categorize based on which position the evidence supports. Always include both positions in the Assessment.\n - **Gap captured — two-axis check** (for items in `## New in Second Opinion`):\n - If both the question is grounded in the codebase/standards AND the best-guess answer is sensible → **Confirmed Improvement** with a 1-2 sentence assessment and 1 citation.\n - If the question is genuine but the best-guess answer is flawed → **Needs Scrutiny**. Cite 2+ files. Use disagreement-depth.\n - If the question itself does not hold up → **Needs Scrutiny** with evidence of what the code actually does. Disagreement-depth.\n - If neither codebase nor standards can settle the question → **Open Questions**. Disagreement-depth.\n - **Single round only**: Treat as a disagreement — cite 2+ codebase locations and give full analytical depth.\n\n For critique points (Requested Changes and Points to Consider), apply the same Round Agreement rules. The signal locations are inline `> **Second opinion (<provider>) - ...**` blockquotes nested under items in `### Requested Changes` / `### Points to Consider`, and gap-captured items under `## New in Second Opinion > ### New Requested Changes` / `### New Points to Consider`.\n\n **Depth calibration**:\n - When Round Agreement is `both rounds agree (weak concurrence)`, `both rounds agree (strong concurrence)`, or `gap captured` (passes both axes), keep Assessment to 1-2 sentences and Codebase Evidence to 1 citation — the validation step or the consensus does the heavy lifting.\n - When Round Agreement is `rounds disagree (refinement)`, `rounds disagree (disagreement)`, or `single round only`, Assessment should be 3-4 sentences and Codebase Evidence should cite 2+ files explaining the discrepancy.\n - A `gap captured` item that FAILS the two-axis check uses the disagreement depth, not the gap-captured depth.\n - A `weak concurrence` item that FAILS your validation gets demoted: change Round Agreement to `rounds disagree (single round only)`, expand Assessment to 3-4 sentences, and add a 2nd citation.\n\n **Source field conventions** — the `**Source**` string disambiguates where in the source doc the item lives so the downstream `capture-review-decisions` step can route the rewrite correctly. Use these forms:\n\n - **Weak concurrence (silent prior item)**: `Clarifying Q3 (prior round, weak concurrence)` or `Critique: Requested Change 2 (prior round, weak concurrence)`.\n - **Strong concurrence (explicit blockquote)**: `Clarifying Q9 (prior round, concurrence inline)` or `Critique: Points to Consider 1 (prior round, concurrence inline)`.\n - **Refinement (inline blockquote)**: `Clarifying Q3 (prior round, refinement inline)`.\n - **Disagreement (inline blockquote)**: `Clarifying Q5 (prior round, disagreement inline)`.\n - **Gap captured (tail-block item)**: `Clarifying Q11 (new in second opinion → New Requirements Questions)` or `Critique: Requested Change N+1 (new in second opinion → New Requested Changes)`. Always spell out the sub-section name after the arrow — capture-review-decisions uses it to find the rewrite target.\n - **Single round only**: `Clarifying Q3 (single round)`.\n\n## Phase 1 — Evaluate and classify every item\n\nNumber every item sequentially across all sections (E-1, E-2, E-3, …). When the same underlying issue is raised in BOTH the clarifying-questions doc and the critique doc, consolidate it into a SINGLE E-item rather than emitting one per source, and cite both origins in its `**Source**` field (e.g. `Clarifying Q3 + Critique: Requested Change 2`); keep the numbering sequential with no gaps. Classify every clarifying question and every critique point into exactly one of three buckets using the Round Agreement rules, codebase groundedness checks, and the `gap captured` two-axis check before producing any recommendation decoration:\n\n- **Confirmed Improvements**: Suggestions that are grounded and would genuinely improve the ticket by closing significant gaps or correcting design issues. Includes weak-concurrence items that passed validation, strong-concurrence items, and `gap captured` items that passed both axes.\n- **Needs Scrutiny**: Suggestions based on inaccurate codebase assumptions, with evidence of the actual code behavior. Includes `gap captured` items that failed either axis, weak-concurrence items demoted by validation, and the loser of any rounds-disagree pair.\n- **Open Questions**: Legitimate ambiguities that require human input to resolve.\n\nPhase 1 must complete before Phase 2 begins — do not start decorating an item with a decision tree, recommendation index, or clarity fields until classification is final.\n\n## Phase 2 — Decorate actionable items with resolution guidance\n\nPhase 2 applies **only** to items in the `Needs Scrutiny` and `Open Questions` buckets. Confirmed Improvements remain compact and undecorated (see \"Confirmed Improvements output\" below).\n\nFor every actionable (Needs Scrutiny / Open Questions) item, produce the following template using these stable labels:\n\n```\n### E-<sequential number>: <concise title>\n\n**Source**: <where this item lives in the source doc — see Source field conventions above>\n\n**Round Agreement**: <one of the six values> — <1 sentence on what the second round contributed>\n\n**Confidence**: <High|Medium|Low>\n\n**Resolution path**: <\"resolve at your desk\" or \"needs a conversation\">\n\n**Decision tree**:\n- If <condition 1>, then <action 1>. See `file:line`. <1-2 sentence rationale.>\n- If <condition 2>, then <action 2>. See `file:line`. <1-2 sentence rationale.>\n- If <condition 3>, then <action 3>. See `file:line`. <1-2 sentence rationale.>\n\n**Recommendation Index**: <0-based index of the recommended branch in the decision tree above>\n\n**Recommendation**: <which branch the evidence best supports and why, 1-2 sentences>\n\n**Original question**: <the clarifying-question or critique point as it was originally raised, sourced verbatim or near-verbatim from the original clarifying-questions / critique docs. Light rephrasing is allowed; do NOT introduce new technical content. Soft cap ~30 words.>\n\n**Option consequences**:\n- <consequence for branch 1 — describe the behavioral consequence of choosing this option, not its rationale. ~25 words.>\n- <consequence for branch 2 — same shape. ~25 words.>\n- <consequence for branch 3 — same shape. ~25 words.>\n\n**Why it matters**: <one concrete sentence on the impact this decision has on the ticket, the users, or the affected code paths. Soft cap ~40 words.>\n\n**Recommendation explanation**: <explain why the recommended branch is the best choice, tied to the codebase evidence and the consequences of each option. Soft cap ~60 words.>\n\n**Assessment**: <three-point structure>\n1. **State the original suggestion**: What did the clarifying question or critique point propose?\n2. **State the codebase evidence**: What does the actual code show about this suggestion?\n3. **State the implication**: Does the evidence confirm the suggestion, contradict it, or leave it unresolved?\n\n**Codebase Evidence**:\n- `path/to/file.ts:42` — <what this line/block demonstrates>\n- `path/to/other.ts:110-125` — <what this range demonstrates>\n\n<If no direct codebase evidence exists, state: \"No direct codebase evidence found.\">\n```\n\n**Writing quality**: Write each Assessment as if explaining to a colleague who has NOT read the original clarifying questions or critique documents. Each assessment should be self-contained and understandable without cross-referencing the source material. The three-point Assessment structure ensures every assessment tells a complete story rather than assuming the reader already knows what was suggested and why.\n\n**Decision tree rules**:\n- Each decision tree must have **2–4 branches**. Do not exceed 4 and do not produce only 1.\n- **Strict lower bound — reclassify on single-branch items**: If you can think of only one branch for a `Needs Scrutiny` or `Open Questions` item — that is, the resolution is effectively forced — you must reclassify the item as a **Confirmed Improvement** instead of emitting a single-branch decision tree. The 2-branch lower bound is a hard rule; do not work around it by stretching to a contrived second branch. If a single answer is genuinely the only path, the item belongs in Confirmed Improvements.\n- Each branch must end with a concrete, actionable step (not \"investigate further\").\n- Cite relevant code in `file:line` format where possible. If no code reference exists, omit the citation rather than fabricating one.\n- Cap each branch at 2-3 sentences total (including the action and rationale).\n- `**Recommendation Index**` must be the 0-based index of the recommended branch in the decision tree above. The first branch is index 0, the second is index 1, etc.\n- **Option consequences** must be a list parallel to the decision-tree branches: one entry per branch, in the same order. Describe the behavioral consequence of choosing that option, not its rationale.\n- **\"resolve at your desk\"**: The item can be resolved through technical investigation — reading code, running tests, or checking configuration. No stakeholder input needed.\n- **\"needs a conversation\"**: The item involves a product decision, scope question, or cross-team dependency that cannot be resolved from the codebase alone.\n\n**Confidence Tags** — assign confidence based on codebase evidence strength:\n- **High**: Cite specific `file:line` references that directly support the assessment.\n- **Medium**: Reference related code patterns or architectural conventions, but not the exact code in question.\n- **Low**: No direct codebase evidence. Assessment is based on general reasoning or domain knowledge.\n\n### Confirmed Improvements output\n\nRender each Confirmed Improvement as a single bullet in a compact list. No headings per item, no decision trees, no clarity-field decoration:\n\n- **E-<number>: <title>** — Source: <source string>; Round Agreement: <one of the six values>; Confidence: <High|Medium|Low>. <recommended action, 1 sentence.>\n\nThe compact bullet still includes `Source`, `Round Agreement`, `Confidence`, and the one-sentence recommended action so `capture-review-decisions.md` can map these items to its `clear_improvements` array.\n\n## Round Agreement Summary\n\nAfter all items are processed, produce a summary section that groups items by round agreement status:\n\n### Points of Disagreement\nFor items where the evaluation marked `rounds disagree (refinement)`, `rounds disagree (disagreement)`, or `single round only` — including `gap captured` items that failed the two-axis check and landed in Needs Scrutiny — list as bullets with the E-number, the nature of the disagreement, and a 1-sentence explanation of why this disagreement matters for the ticket (e.g., it indicates an architectural ambiguity, a scope question, or a standards gap).\n\nIf no items were marked as disagreements, write: \"All reviewed points had round consensus. No disagreement-driven risks identified.\"\n\n### Points of Agreement\nSplit this section into two sub-bullets to surface the difference between the second round explicitly reinforcing a point versus tacitly accepting it:\n\n**Strong agreement** — items where the evaluation marked `both rounds agree (strong concurrence)`. The second round took the trouble to write an explicit `concurrence` blockquote; this is a soft signal that the point is important enough that the second round wanted to underline it. List as bullets with the E-number and a half-sentence noting the shared conclusion.\n\n**Weak agreement** — items where the evaluation marked `both rounds agree (weak concurrence)`. The second round did not object and did not consider the item important enough to comment on; the local agent's brief validation found no concerns. List as bullets with the E-number and a half-sentence noting the conclusion. Lower priority for human review than strong-agreement items.\n\nIf a sub-bullet has no items, omit it (rather than writing a \"no items\" note for each — keep the section tidy).\n\n### Gaps Captured by Second Round\nFor items where the evaluation marked `gap captured` (sound second-opinion Additional Points confirmed as Confirmed Improvements): list as bullets with the E-number and a half-sentence noting the gap the second round surfaced. These items did not require a decision — they are already in Confirmed Improvements — but are surfaced here so the reviewer sees what the second-round analysis added on top of the first round.\n\nIf no gaps were captured, write: \"The second round did not surface any net-new confirmed improvements.\"\n\n## Edge Cases\n\n- If the evaluation contains zero items in Needs Scrutiny, write: \"No items flagged for scrutiny. All reviewed suggestions were either confirmed or remain open questions.\"\n- If the evaluation contains zero items in Open Questions, write: \"No open questions identified. All ambiguities were resolved through codebase analysis.\"\n- If both Needs Scrutiny and Open Questions are empty, include only the Confirmed Improvements section and add a summary: \"All suggestions from the review were confirmed as grounded improvements. No decision trees are needed.\"\n- If both source documents are absent, still write the combined file with the standard top-level sections present but no emitted E-items rather than failing.\n\n## Example of a Well-Written E-Item (Weak Concurrence — Confirmed Improvement)\n\n### E-2: Caching of analysis-type lookups\n\n**Source**: Clarifying Q4 (prior round, weak concurrence)\n\n**Round Agreement**: both rounds agree (weak concurrence) — the second round did not comment on this item; brief validation confirms the answer is grounded.\n\n**Assessment**: The prior round suggested caching `ANALYSIS_TYPES` lookups in a module-level variable to avoid repeated DB round trips. The codebase already does this at `src/python/learn_repository/__init__.py:14`, so the suggestion is grounded and the second round's silence is consistent with tacit agreement.\n\n**Codebase Evidence**:\n- `src/python/learn_repository/__init__.py:14` — module-level constant pattern is the established convention\n\n(Confirmed Improvements compact bullet form: **E-2: Caching of analysis-type lookups** — Source: Clarifying Q4 (prior round, weak concurrence); Round Agreement: both rounds agree (weak concurrence); Confidence: High. Confirm the existing module-level cache and add a short comment naming the pattern.)\n\n## Example of a Well-Written E-Item (Strong Concurrence — Confirmed Improvement)\n\n### E-4: Sequential per-type review_repository fan-out\n\n**Source**: Clarifying Technical Q2 (prior round, concurrence inline)\n\n**Round Agreement**: both rounds agree (strong concurrence) — the second round explicitly reinforced the prior recommendation, citing per-type lock release simplicity as the deciding factor.\n\n**Assessment**: The prior round recommended sequential per-type execution; the second-opinion blockquote reinforced this, noting that the per-type lock release contract becomes trivial under sequential execution. `review_repository` already uses internal `asyncio.gather` for chunk-level concurrency, so wrapping it in another concurrency layer would not buy throughput and would complicate the abort/finally cleanup contract.\n\n**Codebase Evidence**:\n- `src/python/learn_repository/review_repository.py:369-387` — review_repository internally gathers chunks with return_exceptions=True\n\n## Example of a Well-Written E-Item (Rounds Disagree — Needs Scrutiny with full clarity fields)\n\n### E-5: Authentication middleware placement for new endpoint\n\n**Source**: Clarifying Q2 (prior round, disagreement inline)\n\n**Round Agreement**: rounds disagree (disagreement) — the prior round recommended adding auth at the router level; the second-opinion blockquote argued the existing middleware stack already covers it.\n\n**Confidence**: High\n\n**Resolution path**: resolve at your desk\n\n**Decision tree**:\n- If the global middleware stack already enforces auth on `/api/*` routes, then drop the explicit `Depends(require_api_key)` from the new endpoint. See `main.py:45-52`.\n- If routers each opt in to auth via dependencies, then add `Depends(require_api_key)` to the new endpoint. See `api/routes/__init__.py:18-30`.\n- If only certain `/api/*` sub-paths need auth, then carve out a sub-router with its own dependency. See `api/routes/__init__.py:18-30`.\n\n**Recommendation Index**: 1\n\n**Recommendation**: The existing routers each opt in to auth, so the new endpoint must do the same. Adding `Depends(require_api_key)` is the smallest correct change.\n\n**Original question**: Should the new `/api/exports` endpoint declare an explicit auth dependency, or is it covered by the global middleware?\n\n**Option consequences**:\n- Endpoint becomes publicly reachable; protected data leaks via the new path.\n- Endpoint requires a valid API key, matching every other `/api/*` route.\n- Adds a parallel router; doubles the auth surface that has to be kept consistent.\n\n**Why it matters**: Authentication on `/api/exports` directly determines whether protected data leaks; the wrong default is a security regression, not a stylistic choice.\n\n**Recommendation explanation**: The codebase pattern in `api/routes/__init__.py:18-30` shows each router declaring its own `Depends(require_api_key)`. Following that convention adds two lines, keeps auth uniform across endpoints, and avoids a parallel sub-router that future maintainers would have to keep in sync.\n\n**Assessment**: The prior round suggested that the new `/api/exports` endpoint needs an explicit `Depends(require_api_key)` guard because it is not covered by the global middleware. The second opinion disagreed, claiming the middleware stack in `main.py` handles authentication for all `/api/*` routes. Codebase analysis shows that `main.py:45-52` applies rate limiting globally but authentication is applied per-router in `api/routes/__init__.py:18-30` — each router must opt in via `Depends(require_api_key)`. This supports the prior round's position: the new endpoint needs an explicit auth dependency.\n\n**Codebase Evidence**:\n- `main.py:45-52` — global middleware applies rate limiting and CORS, but not authentication\n- `api/routes/__init__.py:18-30` — each router includes its own auth dependency; there is no catch-all auth middleware\n\n## Example of a Well-Written E-Item (Gap Captured — Confirmed Improvement)\n\n### E-7: Missing Alembic migration for new role-scope column\n\n**Source**: Critique: Requested Change N+1 (new in second opinion → New Requested Changes)\n\n**Round Agreement**: gap captured — the second opinion surfaced a missing migration that the prior round did not raise, and recommended adding an Alembic revision.\n\n**Assessment**: The ticket introduces a new `role_scope` column on the `users` table but does not mention a migration. The second opinion flagged this gap and recommended adding an Alembic revision; both the gap and the recommendation are grounded, since `db/alembic/versions/` is the established location for schema changes per the project's database guide.\n\n**Codebase Evidence**:\n- `db/alembic/versions/` — all schema changes land here as autogenerated revisions\n\n## Save rule\n\nSave the combined review-and-resolution document to `{docs_dir}/review/{ticket_key}-review-and-resolution.md`. Output only the combined review-and-resolution document — no meta-commentary.\n\n## Return\n\nConfirm \"Review-and-resolution document written to `{docs_dir}/review/{ticket_key}-review-and-resolution.md`.\" and report the total count of E-items captured.\n",
705
991
  "execute-epic-research.md": "Execute the research plan and write findings.\n\n## Instructions\n\n1. Read the research plan from `{docs_dir}/epic-plans/{epic_slug}/research-plan.md`.\n\n2. Execute the plan based on the Research Mode:\n\n **If mode is `deep`**:\n - Call the `request_deep_research` MCP tool with:\n - `query`: the Deep Research Query from the plan\n - `context`: \"Bridge API is a Python/FastAPI application with PostgreSQL, LiteLLM, and Pinecone. This research supports epic planning for: {epic_description}\"\n - `wait_for_result`: true\n - `save_locally`: true\n - If deep research fails, log a warning and fall back to web searches using the Web Search Topics from the plan. Do NOT halt.\n\n **If mode is `web`**:\n - Perform web searches for each topic listed in the plan.\n - Capture relevant findings from each search.\n\n **If mode is `none`**:\n - Write a brief note: \"No external research needed. Proceeding with codebase exploration.\"\n\n3. Write all findings to `{docs_dir}/epic-plans/{epic_slug}/research-findings.md` with this structure:\n\n```markdown\n# Research Findings\n\n## Mode\n{deep | web | none}\n\n## Findings\n{Synthesized research results organized by topic. Include source references where applicable.}\n\n## Key Takeaways\n{Bullet points summarizing the most important findings that will inform the codebase exploration and epic decomposition.}\n```\n\n## Return\n\nConfirm research findings were written to `{docs_dir}/epic-plans/{epic_slug}/research-findings.md` and report the mode used (`deep`, `web`, or `none`) plus a one-line summary of the key takeaways.\n",
706
992
  "execute-plan-verification.md": "Execute the remaining plan steps for ticket {ticket_key} — faithfully — before the pull request opens.\n\nThe production phase authored the artifacts and the checkpoint pushed them to origin.\nThis phase runs everything else the plan asked for: its review steps, its\ntest-execution steps, and — when the plan authored one — its rendered-UI remediation\nloop.\n\n**This phase runs the plan as written.** It has no fixed depth and no cost-based\nnarrowing rule. If the plan says to grep for every call site of a changed signature,\ngrep for every call site. If it supplies a test command, run that command. A step is\nnot narrowed, substituted, or dropped because it is expensive or slow — that judgment\nis what removed a whole class of plan steps from execution in the first place.\n\nRunning before the pull request is deliberate. The remediation loop *changes code*.\nRunning it after the PR opened would guarantee the PR's first head carried the\nun-iterated first draft, and would pay `claude-review` plus `conductor-ci` on every\none of up to three cycles. The work is durable on origin throughout; it is simply not\nexternally visible until the PR opens a moment later.\n\n**Tool and scope boundary.** Use only the tools this recipe names, the repo's own tooling, and MCP\ncapabilities provisioned for this repository. Keep all work confined to this worktree unless\nexplicitly told to do otherwise.\n\n**Execution mode for this run: `{execution_mode}`.** Under `orchestrated`\n(`run_pipeline` / `resume_pipeline`) orchestration appends the routed phase context and\nparses the fenced result envelope you return. Under `inline` (`get_pipeline_recipe`)\nthere is no orchestrator: you obtain routing and the prior ledger with a tool call, and\nyou record your result with one. Follow the branch that matches wherever the two are\nnamed.\n\n---\n\n## Step 1 — Confirm the Checkpoint Is Durable\n\nBefore starting any long-running work:\n\n1. Run `git rev-parse --abbrev-ref HEAD` and `git rev-parse HEAD`.\n2. Confirm the checkpoint SHA recorded by `checkpoint-work.md` is present on origin —\n `git ls-remote --heads origin <branch>`. The recorded checkpoint comes from the\n routed context's `checkpoint` block when **orchestrated**, and from the `checkpoint`\n field `get_phase_context` returns in Step 2 when **inline**.\n\nIf the branch is not on origin, or HEAD and the recorded checkpoint disagree, **stop and report it**. This phase is long; entering it without recoverable work reintroduces\nthe failure the checkpoint exists to prevent.\n\n## Step 2 — Determine What You Own\n\n1. Call `get_plan` for `{ticket_key}` to retrieve the plan body.\n2. Obtain your routing and the prior ledger, by mode:\n - **orchestrated** — read the **Routed phase context** block appended to this\n instruction. `ownedSteps` lists the exact step IDs this phase must settle;\n `ledger` carries what earlier phases already recorded.\n - **inline** — call `get_phase_context` with `ticket_key` `{ticket_key}` and\n `phase` `pre_pr_verification`. It returns `ownedSteps`, the merged `ledger`,\n `terminalStepIds`, and the durable `checkpoint` Step 1 needs.\n3. Do not re-run a step the ledger already records as `executed` or `adapted` unless a\n correction since then invalidated its evidence. Settle such a step in your phase\n result anyway, with evidence naming the prior record — every owned step must be\n reported, and one you correctly skipped is settled, not missing.\n4. Name the routing source you used when you report, either the routed phase context\n or the `routingSource` value the tool returned.\n\nIf `get_phase_context` reports an error, or the routed context is absent or its\nmetadata status is not `ok`, say so plainly and execute every remaining review,\ntest-execution, and rendered-UI step in the plan. When routing is unavailable here the\nsafe failure is to run more, not less — this phase only verifies, so over-running costs\ntime, while under-running ships unverified work.\n\n## Step 3 — Execute in Plan Order\n\nWork through your owned steps in their original plan order — review steps,\nplan-authored test commands, static searches, architecture-conformance checks, runtime checks,\nand rendered-UI remediation.\n\nRun each command the plan supplies, as the plan supplies it.\n\n### Mechanical adaptation, and its boundary\n\nYou may correct a step **mechanically**, in exactly three ways:\n\n- **locator-correction** — a cited path, file, symbol, or selector has moved or is\n absent (a `docs/tmp/` citation missing from the worktree is the common case, since\n those paths are gitignored). Use the correct locator, or proceed without the\n material, and record it.\n- **repository-command-correction** — the step names a command that is not this\n repository's actual command. Use the real one.\n- **equivalent-implementation-recognized** — what the step asks for already exists in\n equivalent form.\n\n**Escalate** anything involving design, schema, public API, dependencies, or security.\nThose change what the step asks for rather than how it is addressed.\n\nA capability the environment genuinely cannot provide, and a check that genuinely\nrequires an open pull request, are handed forward to the post-PR gap-closing phase as\n`escalated` with that reason recorded. Everything else is executed here.\n\n**Never silently skip a step.** Every owned step ends as `executed`, `adapted`,\n`escalated`, or `unrun-advisory` — the last only when the plan itself declared that range advisory.\n\n## Step 4 — Rendered-UI Remediation\n\nWhen the plan authored a rendered-UI loop, run it as written:\n\n1. Render the actual implementation.\n2. Score every dimension of the plan's fixed rubric, recording the completed table —\n dimension, score, one-sentence reason, and fix attempted — for that cycle.\n3. Apply the accepted fixes.\n4. Stop as soon as the plan's threshold is met; otherwise repeat, to the plan's\n three-cycle cap.\n5. If the cap is reached with dimensions still below threshold, record the final\n scores and the unresolved defects as an **escalation**. Do not claim acceptance.\n\nThe rubric table and the accepted fixes are the evidence that the loop ran. Carry them\nin this step's record.\n\n## Step 5 — Publish Every Material Change Immediately\n\nAfter each material code change:\n\n1. Stage the specific files, commit, and **push immediately**. The commit and its push\n are one consecutive sequence, never separated by another check. An unpushed\n correction is invisible to the pull request, to CI, and to the reconciler.\n2. Run `git rev-parse HEAD` and record the new pushed SHA.\n\nThen continue with the next verification action. Do not batch several cycles of\nchanges behind a single push — the point of pushing immediately is that the next\nlong-running action cannot strand them.\n\n## Step 6 — Report Honestly\n\nRecord the exact command run, its observed result, and on failure the relevant detail:\nfailing test names, error output, diagnostic lines. Report all of it, including\nfailures you did not fix.\n\n**Do not issue a gate verdict.** Do not write that CI passed, that the gate is met, or\nthat the review is approved. Those states are decided by the `ci` and `code_review`\ngates and observed by the reconciler, never asserted by this phase. Worker\nself-verification has reported green while the full suite was red; that is why the\ngates decide and this phase only observes.\n\n## Return\n\nReport the branch, the latest pushed SHA, every command run with its observed outcome,\nevery correction commit pushed, and every unresolved finding.\n\nThen record the machine-readable phase result, by mode.\n\n### orchestrated\n\nEnd your result with the envelope in a fenced block tagged `bapi-phase-result`.\nOrchestration validates it against the steps you own and persists it as durable state;\nan owned step you do not report will be rejected.\n\n```bapi-phase-result\n{\"version\":1,\"phase\":\"pre_pr_verification\",\"lastCommitSha\":\"<sha>\",\"records\":[{\"stepId\":12,\"provenanceClass\":\"final_plan_review\",\"disposition\":\"executed\",\"evidence\":\"grepped all call sites of changed signature; 4 found, all updated\"}]}\n```\n\n### inline\n\nCall the `record_phase_result` tool with `ticket_key` `{ticket_key}` and\n`phase_result` set to that same envelope object. The tool validates it against the\nsteps you own and persists it to the durable ledger; an owned step you do not report\nis rejected there. **Do not also emit a fenced `bapi-phase-result` block** — nothing\nparses one on this path.\n\nThe tool call is this phase's final action, **not the end of your turn.** When it\nreturns successfully, continue immediately with the next recipe step —\n`commit-and-push.md`, then the pull request. Stopping here leaves verified work with no\npull request, which is how an earlier run stranded four pushed correction commits.\n\nIf the call fails, fix what it reports and call it again.\n",
@@ -711,26 +997,30 @@ export const INSTRUCTIONS = {
711
997
  "frame-goals-and-nfrs.md": "Frame the business goals, desired end-state, and non-functional requirements (NFRs) for this work before any functional decomposition or drafting. When the goals and the desired end-state of the system are clear, the functional requirements become much easier to design accurately. This step is documentary: it records the framing and classifies what is unclear. It does NOT pause and does NOT generate a decision page (interactive surfaces handle that separately).\n\n## Inputs\n\n- The idea or epic description for this run, plus any prior planning artifacts the earlier steps wrote into this run's working directory under `{docs_dir}` (for example: research findings, codebase exploration, resolved uncertainties, duplicate assessment). Read whichever of these exist; proceed without the ones that do not.\n\n## Instructions\n\n1. From the inputs, derive and state plainly:\n - **Business goal** — the business value this work delivers and why it matters.\n - **Desired end-state** — the concrete state the system should reach once this work is done.\n - **System behavior** — how the system must behave to complete its task (the quality attributes in prose, not a feature list).\n\n2. Identify the non-functional requirements. Consider every one of these canonical NFR categories and include the ones that genuinely apply (omit categories that do not):\n - security/privacy\n - performance/latency\n - reliability/failure-modes\n - observability/auditability\n - accessibility/UX\n - data-integrity/migration\n - compatibility\n - operability/config\n - compliance/SOC2\n - rollout/reversibility\n\n For each NFR you include, write three things: the `requirement`, its `implication` (what this requirement changes about the implementation), and a `status`. **An NFR with no concrete implication is boilerplate — drop it rather than record it.**\n\n3. Classify each NFR's `status` with this rubric:\n - `confirmed` — only if it is explicitly stated in the idea/description/standards or is directly observable in the codebase.\n - `assumed` — only if it is a low-risk, conventional, and reversible default.\n - `open` — if it touches architecture, the data model, security, user-visible behavior, migration, or irreversible Jira creation and is not settled. Be willing to mark things `open`: surfacing an unclear NFR is the point of this step.\n\n4. If this work is an epic (it will be decomposed into multiple sub-tasks or child tickets), draft a provisional **recommended implementation order**. For each slice, record a short title, its hard prerequisites (`depends_on` — what must land first), any soft sequencing preferences (`recommended_after` — not hard blockers), and a one-line rationale. Keep hard prerequisites separate from soft sequencing. Do not create Jira dependency links — the order is delivered into the epic downstream.\n\n5. Write the framing to a file named `goals-and-nfrs.md` in this run's working directory — the **same directory the earlier exploration/research steps in this pipeline wrote to** under `{docs_dir}`. Getting this path right matters: downstream steps read `goals-and-nfrs.md` from that exact directory and silently degrade (they see no framing) if it lands elsewhere. The directory differs by pipeline:\n - **plan-epic**: the epic plan directory, `docs/epic-plans/<epic-slug>/` (alongside `codebase-exploration.md` and `epic-plan.md`).\n - **idea-to-ticket**: the run directory, `docs/idea-to-ticket/<slug>-<run-id>/` (alongside `research-pack.md` and `resolved-uncertainties.md`).\n\n Use this structure (no markdown tables, no `- [ ]` checkboxes — BAPI-320 hygiene):\n\n```markdown\n# Goals & Non-Functional Requirements\n\n## Business Goal\n{business goal}\n\n## Desired End-State\n{desired end-state}\n\n## System Behavior\n{how the system must behave to complete its task}\n\n## Non-Functional Requirements\n- **{nfr category}** ({confirmed, assumed, or open}): {the requirement}. Implication: {what it changes about the implementation}.\n- ...\n\n## Recommended Implementation Order\n(Epics only; omit this section for a single task or spike.)\n1. {slice title} — depends on: {hard prerequisites or \"none\"}; recommended after: {soft preferences or \"none\"}. Rationale: {one line}.\n2. ...\n```\n\n## Return\n\nConfirm `goals-and-nfrs.md` was written, report the counts of `confirmed` / `assumed` / `open` NFRs, and state whether a recommended implementation order was produced (epics) or skipped (single task/spike).\n",
712
998
  "gather-and-attach-materials.md": "Post-create materials-completeness step. Gather the phase-eligible local materials a freshly-created ticket references and attach them via `attachment` (operations: `\"list\"`, `\"upload\"`, `\"delete\"`), while recording everything that is record-only. This is the POST-CREATE half of the upload-time materials-completeness pass (BAPI-423, phase-tagged since BAPI-643); the PRE-CREATE half — inventorying and writing the `## Materials & Access` section into the draft — already ran in the `jira-ticket-writer` agent.\n\n## Inputs\n\n- `{ticket_number}` — the real Jira key of the already-created ticket (e.g. `BAPI-423`). Attachment is a POST-CREATE step; never attempt to attach before the key exists.\n- `{draft_file_path}` — path to the draft markdown that carries the trailing `## Materials & Access` section.\n- `{auto_approve_external}` — the unattended-vs-interactive signal (named for consistency with `upload-and-track.md`). **Polarity is counter-intuitive: `\"true\"` means UNATTENDED, which is the MORE restrictive mode here** — skip all prompts AND keep external/auth-gated materials record-only (never auto-attach them). It does NOT grant permission to attach external materials. Any other value (including `\"false\"`, missing, or empty) means an interactive invocation that MAY prompt for external/auth-gated materials. Invocations from `write-ticket` and `full-automation` are always unattended (`\"true\"`) for this step.\n\n## Instructions\n\n> **Orchestrator-directed step.** This agent task is authorized to call `attachment` (operations: `list`, `upload`, `delete`) and `update_ticket_description` as directed below.\n\n1. **Read the record.** Read `{draft_file_path}` and parse its trailing `## Materials & Access` section. Collect the inventoried items grouped under *Planning Assets*, *Downloadable Assets*, *Planning & Downloadable Assets* (each phase-eligible; present only when a material of that phase exists), *External/Auth-Gated Links*, and *Binary/Image Materials (Record-Only)*. If there is no `## Materials & Access` section, there is nothing to gather — return a no-op success.\n\n2. **Canonical phase-token mapping.** Every phase-eligible bullet maps to exactly one filename token:\n - *Planning Assets* → `planasset`\n - *Downloadable Assets* → `implasset`\n - *Planning & Downloadable Assets* → `bothasset`\n\n *External/Auth-Gated Links* and *Binary/Image Materials (Record-Only)* are **never** phase-tagged and are behaviorally unchanged by this step — they stay record-only, exactly as before.\n\n3. **Source classification (scheme-based, no network probe).** Honor the classification already recorded in the draft:\n - **Planning Assets / Downloadable Assets / Planning & Downloadable Assets** — a local filesystem path that is **NOT tracked in version control** is a **low-risk** material eligible for auto-attach — proceed to step 5. The pre-create inventory already excluded version-controlled files (source code and in-repo docs are already available in the repository and are never attached — they are cited inline as *Relevant code*). As a safety net, this step must **never attach a file that is available in version control**: if any phase-tagged item is a code file or otherwise clearly version-controlled, skip it and treat it as record-only. A bullet under *Planning & Downloadable Assets* recorded via an existing `attachment_id` (not a local file — e.g. a design comp already attached on this or another ticket) is **not** re-uploaded by this step; leave it recorded with its `attachment_id`/path so a later implementation agent can download it into its worktree via the Jira attachment download capability.\n - **External/Auth-Gated Links** (every `http(s)` URI, even if explicitly linked) are **record-only** on unattended paths. If `{auto_approve_external}` is `\"true\"` (or the invocation is from `write-ticket` / `full-automation`), leave them record-only and never auto-attach. (Mind the polarity: `auto_approve_external = \"true\"` means we are in unattended mode, so external materials must stay record-only — `\"true\"` is NOT permission to attach them.) Only an explicitly interactive invocation (`auto_approve_external` is any non-`\"true\"` value) may prompt the user to confirm before attaching.\n - **Binary/Image Materials (Record-Only)** — ordinary/unrelated binaries (arbitrary screenshots, PDFs, ZIPs, and other binaries not design-relevant) stay **record-only** in this step; never attempt to upload them.\n\n4. **Deduplicate and detect stale phase reclassification.** Call the `attachment` MCP tool with `operation` set to `\"list\"` and `ticket_number` set to `{ticket_number}` BEFORE uploading anything, so a resumed or re-run invocation does not re-attach a material that is already present.\n - Compute each candidate's deterministic filename first (step 6), then compare the listed attachments by **`{ticket_number}/{hash}`** (the ticket prefix plus the 8-hex-char hash segment) against the phase-tagged filenames already on the ticket, using the exact grammar `{ticket}-{planasset|implasset|bothasset}-{hash}{ext}` — this comparison spans all three phase tokens for the same source.\n - If a listed attachment shares the same `{ticket_number}/{hash}` as a candidate but carries a **different phase token**, treat it as a **stale classification for the same source** — the material was reclassified to a different phase since it was last gathered.\n - If a listed attachment's filename **exactly matches** the candidate's newly computed filename (same ticket, same phase token, same hash, same extension), it is already attached — skip re-uploading it.\n - Legacy `{ticket_number}-material-{hash}{ext}` attachments (pre-BAPI-643) are a distinct, valid, untouched naming form — never treat them as stale phase-tagged attachments, and never delete or supersede them here.\n\n5. **Supersede a stale phase-tagged attachment before uploading the reclassified filename.** When step 4 identifies a stale same-source attachment under a different phase token:\n - Call the `attachment` MCP tool with `operation` set to `\"delete\"`, `ticket_number` set to `{ticket_number}`, and `file_name` set to the stale attachment's exact filename. This is a genuine removal (not `replace_existing`, which only matches an upload's *own* filename) — the stale filename differs from the new filename whenever the phase token changed, so `replace_existing` alone cannot supersede it.\n - **If the delete call fails** (or reports `deleted: false` for a reason other than \"already absent\" — e.g. a transient Jira error), **skip the replacement upload for that material entirely** this run, preserving the one-active-attachment invariant rather than leaving two phase-tagged copies of the same source live at once. Record the failure (see step 9) and continue with the next material; never halt the run.\n - **If the delete succeeds** (or the stale attachment was already absent), proceed to upload the newly classified filename in step 7.\n\n6. **Gather and size-tier each phase-eligible local material; compute deterministic filenames.**\n - For each phase-eligible local **text** material:\n - Read the local file from disk.\n - If the content exceeds **200,000 characters**, SKIP the upload and RECORD it (note the path and that it was skipped for size) — do not attach it.\n - If the content is **<= 200,000 characters**, upload it RAW via `attachment` (operation: `\"upload\"`). Do NOT summarize locally: the backend already summarizes attached text at plan time, so the size tiers are backend behavior this step defers to. The agent performs NO local summarization.\n - **Extension rule by phase**: a **planning-only** (`planasset`) text material normalizes to `.md` regardless of its original extension — the planner only needs prose content, not the original format. A **downloadable or both-phase** (`implasset`/`bothasset`) text material instead **preserves its lowercased original source extension** (e.g. `.json`, `.csv`, `.py`, `.yml`/`.yaml`) — the implementing agent needs the file back in its original, directly usable format. There is no blanket \"all text becomes `.md`\" rule; the extension is phase-dependent.\n - Use a deterministic, sanitized filename of the form `{ticket_number}-{phase-token}-{hash}{ext}` (using the `{ticket_number}` input from the Inputs section and the phase token from step 2), where `{hash}` is the first 8 lowercase hex characters of the SHA-256 digest of the sanitized absolute source path, and `{ext}` follows the extension rule above.\n - For each eligible local design/UI comp image (or other phase-tagged image, extension mapping to an allowlisted MIME type — `image/png`, `image/jpeg`, `image/webp`, `image/gif`), eligible for the allowlist-guarded binary upload path (distinct from the text upload path above):\n - Use a deterministic filename of the form `{ticket_number}-{phase-token}-{hash}{ext}`, where `{hash}` is computed the same way (first 8 lowercase hex characters of the SHA-256 digest of the sanitized absolute source path) and `{ext}` is the lowercased allowlisted source extension (`.png`, `.jpg`, `.jpeg`, `.webp`, `.gif`) — the existing image-extension behavior is unchanged by phase.\n - Pin the hash algorithm exactly (SHA-256, first 8 lowercase hex chars, of the sanitized absolute path) — do NOT substitute another hash — so the same source always maps to the same `{ticket}/{hash}` identity and the dedup/stale-detection in step 4 works across separate sessions, re-runs, and phase reclassification. Compute filenames before the step 4 dedup/stale comparison is applied. Keep the sanitized source provenance inside the text attachment body (not applicable to binary comp uploads), not only in the filename.\n\n7. **`attachment` upload parameter discipline (Zod).**\n - For text materials, call `attachment` with `operation: \"upload\"`, `ticket_number`, the deterministic phase-tagged attachment filename, and the text `content`.\n - For design comp image uploads, call `attachment` with `operation: \"upload\"`, `ticket_number`, `file_path` (the local source path), and `file_name` set to the deterministic phase-tagged comp filename from step 6 — pass `file_path` rather than reading and UTF-8-encoding the bytes yourself, so `resolveUploadAttachment()` performs binary detection, the MIME allowlist check, and base64 encoding. Never UTF-8-encode image bytes locally.\n - In both cases, OMIT the optional parameters `link_type` and `replace_existing` entirely when they are unused — do NOT pass `null` or empty strings for them. The Zod schemas reject `null`/empty values, so an unused optional parameter must be omitted rather than nulled.\n\n8. **Persist destination provenance after successful uploads (one batched update).** For every material successfully uploaded in this run, record a stable, sanitized mapping from its generated Jira filename to the bullet's original repository-relative destination path (as recorded in the draft's `## Materials & Access` section). Write these mappings as bulleted lines under a dedicated trailing `### Asset Destinations` subsection of `## Materials & Access`, one line per mapping, in the exact form:\n ```markdown\n ### Asset Destinations\n\n - `{jira-filename}` → `{original/repo/relative/destination.ext}`\n ```\n This is the canonical, machine-parsed contract consumed by `extract_asset_destination_paths()` (`api/agent_flows/code_writer/library/ticket_asset_lib.py`) — do not vary the arrow character (`→`), the backtick wrapping, or the bullet form. Append new mappings to this subsection (creating it if absent) rather than replacing prior mappings from earlier gather runs. Apply **all** of this run's successful-upload provenance updates in **one single batched call** to `update_ticket_description` — never one Jira description update per asset. If the provenance update call fails, **retain the original bullet unchanged** in the ticket description and record a non-blocking warning that the destination mapping could not be persisted; this does not fail the run, and the filename token itself is still enough for plan generation to classify the asset even without a fully actionable destination.\n\n9. **Redact secrets everywhere.** Before writing any URL or access note ANYWHERE — the Jira `## Materials & Access` record, any warning or final-report output, and any local intermediate file — sanitize and redact embedded credentials, SAS tokens, API keys, and basic-auth secrets using a high-visibility placeholder such as `[REDACTED_TOKEN]`. Mirror the backend `_redact_forge_fields()` / `_sanitize_jira_error_message()` patterns. A location/access note must never expose a plaintext secret.\n\n10. **Warn, never halt (error handling).** This step must NEVER halt, prompt-to-fail, or fail the overarching command because a material could not be gathered, attached, or superseded. Follow the warn-not-halt convention:\n - If an `attachment` upload call fails (or a file disappeared between inventory and upload), warn gracefully and continue with the next material.\n - If a stale-attachment `delete` call fails, skip only that material's replacement upload (step 5) and continue with the rest — never halt the run.\n - This includes design comp image uploads: an unsupported/disallowed MIME type, an oversize image (`> 10 MB`), a missing local file, a malformed upload payload, or a Jira upload failure must all be warned and skipped, never halting the run.\n - On such a post-create attach/delete failure, call `update_ticket_description` to record the failure in the issue's `## Materials & Access` record (the material became unavailable, or its stale copy could not be superseded, only after the issue existed). `update_ticket_description` is an existing MCP tool, not a backend change.\n - Everything knowable PRE-CREATE was already written into the description at create time, so `update_ticket_description` is reserved for these rarer post-create attach/delete/provenance failures. This complements the existing `partial_success` recording convention in `upload-and-track.md`.\n - Apply the step 9 redaction to every warning and recorded note.\n\n## Return\n\nConfirm the outcome, reporting each category **by its user-facing phase name and original path** (not by the internal filename token, which is implementation metadata): which Planning Assets, Downloadable Assets, and Planning & Downloadable Assets were attached (noting their deterministic Jira filenames for cross-reference only), which stale phase-tagged attachments were superseded (and which supersession attempts failed and were skipped), which materials were skipped/recorded as record-only (over-size text, external/auth-gated links, ordinary/unrelated binaries, or non-local design references), whether the destination-provenance batched update succeeded, any attach/delete/provenance failures recorded via `update_ticket_description`, and that no failure halted the run.\n",
713
999
  "get-prd.md": "# get_prd\n\nRetrieve an already-generated **Product Requirements Document (PRD)** for a Jira\nticket.\n\nThis tool only **fetches** an existing PRD — it does **not** start or trigger\ngeneration. If no PRD exists yet (or you need a fresh one), call `request_prd`\nfirst; it starts the async generation and `get_prd` retrieves the result once\nprocessing completes.\n\nThe PRD is product/stakeholder-facing: problem framing, goals, non-goals, target\nusers, success metrics, product requirements, scope, and risks. Present the\nreturned markdown verbatim without summarizing.\n\n## Parameters\n\n| Parameter | Type | Default | Description |\n| --- | --- | --- | --- |\n| `ticket_number` | string | — | Jira ticket key in `PROJECT-NUMBER` format (e.g. `BAPI-123`). |\n| `save_locally` | boolean | `true` | Save the retrieved PRD to a local file. Set to `false` to skip saving. |\n\nLocal saves go to `BAPI_DOCS_DIR/prd/{ticket}-prd-plan.md`.\n\n## Return\n\n- The full PRD as markdown text when one exists.\n- A `404` / not-found response when no PRD is ready yet — that means generation\n has not run, not that the tool failed. Call `request_prd` to generate one.\n",
714
- "learn-architecture.md": "## Objective\n\nExplore the codebase to identify architectural principles, directory conventions, design patterns, and data flow, then draft `architecture_instructions` for the project config.\n\n## Instructions\n\n### Phase 1 — Principles Research\n\nResearch the codebase to identify architectural principles and conventions. For each area below, examine at least 5 representative files. Cite file paths for every pattern. Include code examples (5-15 lines) showing correct usage. Where relevant, include a WRONG example showing the common mistake.\n\nFor each pattern, classify its evidence level:\n- `ENFORCED` consistently followed across the codebase, violations would be bugs\n- `CONVENTION` commonly observed, occasional deviations exist\n- `ASPIRATIONAL` intended direction, not yet consistently applied\n\nResearch areas:\n1. **Architectural coding patterns**: Search `api/routes/` and `api/library/` for separation of concerns, layer boundaries, function-vs-class decisions. Read files matching `*_lib.py`, `*_utils.py`, `*_helpers.py` to document module naming suffix conventions.\n2. **Design patterns**: Search for factory functions, strategy patterns, middleware chains, registry patterns, and dependency injection in `api/` and `src/python/`. Cite concrete usage with file path and function name.\n3. **Dependency management**: Read `requirements.in`, `requirements-dev.in`, and `package.json` files to document how dependencies are declared and organized.\n4. **Error handling architecture**: Search for `log_exception_to_sentry` and `HTTPException` usage patterns across `api/routes/` to document the system-wide error propagation strategy.\n5. **Configuration management**: Search for `os.environ` and `get_config_field` usage to document the two-tier system (env vars vs. database config).\n6. **Tech stack detection**: Read `requirements.in`, `package.json`, and `main.py` to identify primary languages, frameworks, and key libraries.\n7. **Security architecture**: Read `api/routes/setup/auth.py` and search for `require_api_key`, `require_api_session`, and `verify_repo_access` to document authentication and authorization design.\n8. **Agent prompting conventions**: Read files in `src/python/llms/agents/` to document prompt construction, section headers, dynamic content delimiters, and role-based personas.\n\nScope exclusion: Do NOT document testing patterns. Skip the `tests/` directory entirely.\n\nWrite findings to `{docs_dir}/tmp/architecture-principles.md`.\n\n### Phase 2 Structure & Data Flow Research\n\n1. Call the `regenerate_directory_map` MCP tool to get a fresh directory map.\n2. Read the principles document from Phase 1.\n3. Research and document:\n - **Directory conventions**: For each major directory, document purpose, file naming, internal structure, and an example file.\n - **Module boundaries and import patterns**: Which directories are distinct modules and how they interact. Document import restrictions.\n - **Data flow patterns**: Trace 2-3 complete request paths (synchronous, async background task, agent orchestration).\n - **Integration patterns**: How external services (Jira, GitHub/Bitbucket, LLMs, Pinecone, PostgreSQL) are integrated.\n - **Background task patterns**: The async task lifecycle with `asyncio.create_task`, semaphores, and error reporting.\n\nWrite findings to `{docs_dir}/tmp/architecture-structure.md`.\n\n### Phase 3 Draft\n\n1. Read both research documents.\n2. Combine into a single `architecture_instructions` draft with these required sections:\n - **1. Core Principles** Each principle with evidence level and explanation.\n - **2. Layered Architecture** Layer separation, dependency rule, agent vs orchestration logic.\n - **3. Directory Conventions** Purpose, naming, structure for each major directory.\n - **4. Data Flow Patterns** Complete request path traces with file paths.\n - **5. Technical Standards** Coding style, async patterns, database, schema, LLM integration, config, dependencies.\n - **6. Error Handling & Monitoring** Error propagation strategy, Sentry integration, Langfuse tracing.\n - **7. Security & Authentication** Auth architecture, session model, permission model.\n - **8. Agent Prompting Conventions** Prompt construction, section headers, content delimiters.\n - **9. Integration Points** External service clients and their calling patterns.\n - **10. AI Code Generation Guidelines** Anti-patterns, duplication avoidance, pattern compliance checklist.\n\n3. Write the draft to `{docs_dir}/standards/architecture_instructions.md`.\n\n## Length Budget\n\nThe platform ceiling for a configuration field is **40,000 characters**, and the server enforces it\nat upload time: a draft over the limit is rejected outright and ``architecture_instructions`` is simply not learned. Bound\nthe draft here, at write time, rather than discovering the limit through a failed upload.\n\n1. When the draft is complete, measure its length in **characters** not tokens, not words, not\n bytes.\n2. If it exceeds 40,000 characters, condense it: remove redundancy, collapse repetitive passages, and\n shorten or drop verbose code examples.\n3. Keep every required section listed above. Never meet the budget by deleting a required section,\n and never truncate the draft mid-sentence.\n4. Re-measure after condensing and repeat until the draft is at most 40,000 characters.\n\n## Return\n\nReturn a brief summary of what was learned about the project's architecture (core principles, directory conventions, data flow), citing the key files inspected, and confirm the draft was written to `{docs_dir}/standards/architecture_instructions.md`.\n\nAlso report the draft's final length in characters, the applicable maximum (40,000 characters),\nwhether the draft was condensed to meet that maximum (`condensed`: true/false), and when it was\ncondensed the reason it needed condensing.\n",
715
- "learn-backend-correctness.md": "## Objective\n\nExplore the codebase to identify correctness standards for backend code, then draft the corresponding correctness standards document.\n\n## Target Type\n\n- **Type**: `backend_correctness`\n- **Field name**: `backend_correctness_standards`\n- **Scope**: Server-side code: Python, Ruby, Go, Java, C#, Node.js server code, API routes, business logic.\n\n## Instructions\n\n### Phase 1 — Explore Correctness Patterns\n\nFocus on implementation correctness: how to write code that is correct, idiomatic, and robust within this project's conventions.\n\n1. **File Type Detection**: Search by filename pattern for files matching `**/*.py` in `api/` and `src/python/`. If very few or no files exist, note this and draft minimal instructions.\n\n2. **Convention Analysis**: Read 3-5 representative files in `api/routes/` and `api/library/` to identify:\n - Structure patterns (imports, exports, class structure, function ordering)\n - Naming conventions (variables, functions, classes, files)\n - Framework conventions and idioms\n - Best practices followed\n - Issues and inconsistencies\n\n Also read files to document:\n - Error handling implementation (try/except ordering, Sentry calls) with CORRECT/WRONG examples\n - Authentication implementation (auth check sequence) with code examples\n - Database call patterns (`postgres_helpers` (bool, result) tuple handling) with CORRECT/WRONG examples\n - Input validation patterns (Pydantic models, naming conventions)\n - HTTP client patterns (error handling, JiraError sanitization)\n - Async implementation patterns (`asyncio.to_thread()` for blocking code)\n\n### Phase 2 — Draft\n\nDraft correctness standards as clear, actionable instructions for an AI code generation agent. Cover:\n- Code structure and organization requirements\n- Naming conventions to follow\n- Framework-specific patterns and idioms\n- Security requirements relevant to this code type\n- Performance considerations\n- Common mistakes to avoid\n- Guards against common AI weaknesses: duplicative code, verbose implementations, security vulnerabilities\n\nAlso include:\n- Route handler boilerplate (auth -> validation -> business logic -> error handling)\n- Database interaction patterns with CORRECT/WRONG examples\n- Exception handling pattern (specific first, HTTPException re-raise, generic with Sentry)\n- Sentry reporting patterns and common mistakes\n- Input sanitization rules (JiraError headers, raw exception messages)\n\nWrite the draft to `{docs_dir}/standards/backend_correctness_standards.md`.\n\n## Length Budget\n\nThe platform ceiling for a configuration field is **40,000 characters**, and the server enforces it\nat upload time: a draft over the limit is rejected outright and ``backend_correctness_standards`` is simply not learned. Bound\nthe draft here, at write time, rather than discovering the limit through a failed upload.\n\n1. When the draft is complete, measure its length in **characters** — not tokens, not words, not\n bytes.\n2. If it exceeds 40,000 characters, condense it: remove redundancy, collapse repetitive passages, and\n shorten or drop verbose code examples.\n3. Keep every required section listed above. Never meet the budget by deleting a required section,\n and never truncate the draft mid-sentence.\n4. Re-measure after condensing and repeat until the draft is at most 40,000 characters.\n\n## Return\n\nReturn a brief summary of what was learned about backend correctness conventions (structure, naming, error handling, auth, DB patterns), citing the key files inspected, and confirm the draft was written to `{docs_dir}/standards/backend_correctness_standards.md`.\n\nAlso report the draft's final length in characters, the applicable maximum (40,000 characters),\nwhether the draft was condensed to meet that maximum (`condensed`: true/false), and — when it was\ncondensed — the reason it needed condensing.\n",
1000
+ "greenfield-apply-and-report.md": "Save the project setup in one authoritative call, then hand `/install-bridge` the fresh manifest it\nresumes its reporting from. This is the only step in this pipeline that writes the install manifest.\n\n## Step 1 — Gather what the earlier steps settled\n\nCollect, from the results already produced in this pipeline:\n\n- the **snapshot token** from this pipeline's single `get_install_manifest` step;\n- the interview task's `setup_path`, candidate manifest values, VCS decision, and repository facts;\n- the synthesis task's `final_path`, approved `project_description`, and drafted fields;\n- the outcome of the ten `config_field` upload steps.\n\nUse the snapshot token **exactly as that step returned it**. Do not re-read the manifest to obtain a\nfresher one before applying: a new read invalidates the token this apply is about to use.\n\n## Step 2 Build the apply payload\n\nInclude a field only when it was genuinely resolved. An omitted field stays unset, which is always a\nsafe outcome; a guessed field is a wrong value someone has to find and correct later.\n\n- **`working_in`** include only when it was resolved through the authoritative configuration, clear\n local evidence, or the interview. Never infer it from an ambiguous template.\n- **`project_description`** include **only** in the form `{ \"value\": \"...\", \"confirmed\": true }`, and\n only when the decision and standards-approval path explicitly confirmed that text. If it was never\n confirmed, omit it entirely.\n- **Unit and end-to-end test stacks** include each only when the interview gave a definite answer.\n Never invent a framework, and never fill one in from the other.\n- **`version_control_system` and `base_branch`** include only when the collected VCS decision and the\n local repository facts make the values valid. A project that deferred version control, or that has no\n repository yet, gets neither field.\n- **`ticket_backend_mode`** include only when the interview legitimately asked for it because Jira\n was not configured. When Jira is configured, this field is not this pipeline's business.\n- **`greenfield_setup_path`** always include it, set to exactly `interview` or `defaults` according\n to the **final approved outcome**: `interview` when the standards were approved, `defaults` when the\n human chose defaults, skipped the interview, or the revision loops ran out. This is the field that\n marks the project greenfield-complete, so it must describe what actually happened.\n\n## Step 3 Apply exactly once\n\nCall `apply_install_manifest` **exactly once**, with the snapshot token from Step 1 and the fields from\nStep 2.\n\nOn the `defaults` path, the server seeds this project's catalog defaults as part of this call and\nreturns the per-field outcomes in `greenfield_defaults_seeding`. Report those outcomes as the server\nreturned them. `noop_already_current`, `swapped`, and `skipped_user_content` are all successful results\n— an idempotent re-apply and a project whose content someone already edited are both correct.\n\nThe apply is partial-tolerant: a field that fails validation lands in the `rejected` bucket while the\nvalid fields still commit. Report a rejected field; do not retry the whole call because of one.\n\n## Step 4Read the manifest back\n\nAfter the apply succeeds, call `get_install_manifest` **once**. This read-after-write response reflects\nwhat was just written, and it is what `/install-bridge` needs: its Stage 7 capability report and its\nStage 8 next step both come from this response and from nothing else. This read does not need a\nsnapshot token.\n\n## Step 5 Failures\n\nIf the apply fails, or the read-after-write fails, report it through this pipeline's ordinary failure\nshape, naming which of the two failed and what the server said.\n\n**Do not retry the apply with a new snapshot token.** A retry that re-reads the manifest for a fresh\ntoken would apply a second time against state the first attempt may already have changed. Reporting a\nclean failure lets the human re-run `/install-bridge`, which is safe.\n\n## Constraints\n\n- **Never call `parse_repository`**, and never mention or invoke `/learn-repository` in any output. A\n brand-new project has nothing to index and nothing to learn from; the server decides the next step and\n returns it in `next_step`.\n- **Never request, echo, log, document, or return a credential** or any secret-like value.\n\n## Return\n\nReturn the complete read-after-write manifest from Step 4 to the calling `/install-bridge` flow it is\nthe authoritative source for that command's Stage 7 report and Stage 8 next step, and the command\nrequires it before it resumes.\n\nAlongside it, report:\n\n- `greenfield_setup_path` as applied (`interview` or `defaults`);\n- the apply buckets the server returned (applied, skipped, conflict, needs_confirmation, rejected,\n deferred);\n- `greenfield_defaults_seeding` verbatim when the defaults path ran, omitted otherwise;\n- which standards fields were written and which uploads were skipped;\n- whether the deferred version-control guide was written, and its path.\n",
1001
+ "greenfield-interview.md": "Settle what this new project is, what it must not do, and how its code will be reviewed — before any\ncode exists. This step gathers a bounded framing from the human, ratifies whatever it could not settle\non its own, and decides how version control will work. It is the only step in this pipeline that\ninterviews the human about the project itself.\n\nNothing downstream may run until this step ends in an explicit decision commit.\n\n## Operating rules (apply to the whole task)\n\n1. **Never ask for a secret, and never echo one.** No API key, token, password, connection string, or\n `.env` value is an input here. If the human volunteers one, do not repeat it back, do not write it\n to any file, and do not carry it into the decision page, the framing document, the council brief, or\n this task's return value. Redact it to `[redacted]` at the moment you notice it.\n2. **Ask about the project, not about the repository's code.** This project is new; there is nothing to\n read. Local evidence is worth a glance (a `package.json`, a framework template) but it never\n overrides an answer the human gives.\n3. **Keep the surface calm.** One question at a time, a compact progress marker, and no diagnostic\n transcript. The human is being onboarded, not debugged.\n4. **A headless session never waits.** Wherever this file says to ask, a session that cannot obtain an\n interactive response takes the stated non-interactive branch immediately and continues.\n\n## Step 1 — Offer the interview\n\nLead with this exact sentence, verbatim:\n\n`Skipping uses generic defaults; the agents will write noticeably weaker code until standards exist.`\n\nThen offer exactly two choices:\n\n- **Interview** — answer up to eight short questions so the standards match this project. This is the\n higher-quality path, and say so.\n- **Use defaults** — skip the questions and start from generic defaults.\n\nPresent defaults as a legitimate choice, not a failure or a warning. Someone who wants to write code\nright now is making a reasonable trade, and the sentence above already tells them what it costs.\n\nRecord the answer as the **setup path**: `interview` or `defaults`.\n\n**In a headless session, select `defaults` immediately** and continue — do not wait, do not prompt, and\ndo not treat the absent human as an error.\n\nWhen the setup path is `defaults`, skip Steps 2 through 6 entirely: no questions, no framing document,\nno decision page. Go straight to Step 7 (version control), which runs on both paths, and then return.\n\n## Step 2 — Interview (at most eight questions)\n\nAsk **at most eight** questions, one at a time, covering these areas in this order. Merge or drop any\nthat a previous answer already settled — eight is a ceiling, not a quota.\n\n1. **Purpose and users** — what this project is for and who uses it.\n2. **Non-goals** — what it deliberately will not do.\n3. **Language and framework commitments** — what is already decided about the stack.\n4. **Architecture shape** — the rough structure (service, CLI, library, app, monolith, split).\n5. **Testing expectations** — what \"tested enough\" means here.\n6. **Quality bars** — what \"good\" means: performance, accessibility, security posture, review rigor.\n7. **Delivery constraints** — deadlines, platforms, environments, or team constraints that shape the\n code.\n8. **Must-never-happen** — the failures that would be unacceptable in this project.\n\nShow progress compactly — for example `Question 3 of 8` — and nothing else. Do not print the running\ntranscript, an evidence dump, or a per-answer confirmation block.\n\n## Step 3 — Collect the configuration facts the setup needs\n\nThese are configuration values, not framing. Fold them into the same eight-question budget wherever\nthey fit naturally (the stack question and the language/framework question are usually the same\nquestion), and never ask for one whose answer you already have.\n\n- **`working_in`** — ask **only** when local evidence and existing configuration do not already resolve\n it. Offer exactly these values, and never invent, narrow, or reword the list:\n `Salesforce Commerce Cloud`, `Salesforce Core`, `Scayle`, `Shopify`, `Python`,\n `JavaScript/TypeScript`, `Go`, `Java/Kotlin`, `Ruby`, `.NET`, `Other`.\n- **Unit test stack and end-to-end test stack** — collect each only when it applies. Do not guess an\n ambiguous framework and do not offer to pick one later: an unset stack is a fine outcome, a wrong one\n is not.\n- **`ticket_backend_mode`** — ask **only** when the authoritative configuration shows Jira is not\n configured for this project. When Jira is configured, do not raise the subject at all.\n\nCarry every collected value forward as a **candidate manifest value**. This task writes none of them —\nthe final apply task owns every write.\n\n## Step 4 — Frame the answers into goals and NFRs\n\nWrite the framing to `{docs_dir}/greenfield/{repo_name}/goals-and-nfrs.md`.\n\nUse the vocabulary and structure defined in `frame-goals-and-nfrs.md`: a business goal, a desired\nend-state, the system behavior, and a classified NFR list where each NFR carries a `requirement`, its\n`implication`, and a `status` of `confirmed`, `assumed`, or `open`. Apply that file's rubric for the\nstatus classification, and its rule that an NFR with no concrete implication is boilerplate and should\nbe dropped rather than recorded. Omit the recommended-implementation-order section — this is a project\nframing, not an epic decomposition.\n\nThe interview answers are the input in place of an epic description. Where an answer settles something\noutright, mark it `confirmed`. Where you filled a conventional, low-risk, reversible gap yourself, mark\nit `assumed`. Where the answer touches architecture, the data model, security, user-visible behavior,\nor something otherwise expensive to reverse and is not settled, mark it `open` — that is what the next\nstep exists to resolve.\n\n## Step 5 — Put the open NFRs on a decision page\n\nCall `generate_decision_page` with the routing fields at the root and the content nested under\n`content`:\n\n- `artifact_type`: `pre_ticket_planning`\n- `ticket_key`: `greenfield-{repo_name}`\n- `output_subdir`: `greenfield/{repo_name}`\n- `labels`: onboarding-flavored overrides — for example a `title` naming the project setup and a\n `section_heading` such as \"Open questions about this project\".\n- `content.system_goals` (read-only): `business_goal`, `desired_end_state`, `system_behavior`, and the\n full classified `nfrs` list from Step 4.\n- `content.actionable_items`: **one card per `open` NFR**, built exactly as `clarify-open-nfrs.md`\n specifies — a short stable `id`, the `question` the open NFR poses, 2–4 concrete `options`, one\n `option_consequences` line per option, `why_it_matters`, `recommendation_explanation`, and a 0-based\n `recommendation_index`. Do not add \"None of these\" or \"Ask about this\" options yourself; the renderer\n appends both, and they are how the human disagrees or asks rather than commits.\n\nOrder the surface so the **project framing and the unresolved questions come first** and the supporting\nevidence sits behind them. Expose exactly **one** clear commit action — the page's own submission. Do\nnot offer a second competing way to finish.\n\nIf there are no `open` NFRs at all, still require an explicit commit: present the framing in chat and\nask for it there, exactly as Step 6's fallback does. Ratification is the gate, not the page.\n\n## Step 6 — Wait for the commit, and resolve everything it left open\n\nDirect the human to the returned `file_path`, and **stop and wait**.\n\nTreat a paste as a commit only when it is a JSON object carrying all three top-level fields:\n`ticket_key` (string), `decisions` (object), and `general_comment` (string).\n\nThen settle every card:\n\n- **Committed** — an explicit option choice. Record it.\n- **Ask** — the human wants to discuss it. Discuss until they give an explicit decision, then record\n that decision. Every `ask` must become an explicit recorded decision.\n- **Disagree** — the \"none of these\" answer, meaning no offered option is right. Capture the reason\n they gave, in the card comment or in chat, and settle the question with them explicitly. A disagree\n that is never resolved is not a commit.\n\nAn explicit conversational change to a decision after the paste is an in-flight override and takes\nprecedence over the submitted JSON.\n\nThen fold the results back into `{docs_dir}/greenfield/{repo_name}/goals-and-nfrs.md`: move each\nresolved NFR's `status` from `open` to `confirmed` (or `assumed` when the human chose a provisional\ndefault), record the chosen resolution in its `requirement` and `implication`, and weave\n`general_comment` in as overarching guidance.\n\n**If `generate_decision_page` fails**, do not stall and do not skip the gate: present the same\ncontent — the framing, then each open question with its options and consequences — directly in chat,\nand require the same explicit commit there. The surface may degrade; the ratification may not.\n\n**Nothing downstream may run before this commit.** Do not call `request_council`, do not draft any\nstandards field, and do not begin synthesis until a valid explicit decision commit exists. The visible\norder is `Decision committed` → `Standards review` → `One approval`, and it is not merely cosmetic:\nstandards drafted before the commit would encode assumptions the human never agreed to.\n\n## Step 7 — Settle version control (both paths)\n\nAsk whether this project will use GitHub.\n\nTreat absent Git metadata — no repository, no commits, no remote — as neutral context, not as an error,\na warning, or an answer. A brand-new project legitimately has none of it.\n\n**If yes:** retain `version_control_system` = `github` as a candidate manifest value, and print the\ncommand the human runs to connect it, as wrapped monospace technical text on its own line:\n\n`npx -y @bridge_gpt/mcp-server@latest connect-github --repo {repo_name}`\n\nDo not run it yourself — connecting the repository is the human's own explicit next action.\n\n**If no (or deferred):** write `{docs_dir}/greenfield/{repo_name}/connect-version-control.md`\nidempotently — write the guide when it is absent, refresh it in place when it already exists, and never\nappend a second copy. The guide explains, in order:\n\n1. Creating the repository when they are ready.\n2. Installing the Bridge GitHub App on it.\n3. Running the `connect-github` command above.\n4. What connecting unlocks: repository indexing, pull-request creation, CI monitoring and follow-up,\n and the council modes that need an indexed repository (the `general` council works without it).\n\nThen say the guide was written and where, in one line. Do not treat a deferred VCS as a problem — it is\na normal state for a project that has not started yet.\n\nIn a headless session, do not wait for this answer either: skip the question, write the deferred-VCS\nguide, and leave `version_control_system` unset.\n\n## Return\n\nReturn a secret-free structured payload for the rest of the pipeline. It must carry:\n\n- `setup_path` — exactly `interview` or `defaults`.\n- `decision_committed` — whether an explicit commit was received (always `false` on the `defaults`\n path, which asks no questions to commit to).\n- `framing` — the committed business goal, desired end-state, system behavior, and the settled NFR list\n with each one's final `status`, plus the path to `goals-and-nfrs.md`. Omit on the `defaults` path.\n- `candidate_manifest_values` — the configuration values collected here for the final apply task:\n `working_in`, the unit and end-to-end test stacks, and `ticket_backend_mode` when it was legitimately\n asked for. Include only values that were actually resolved.\n- `vcs` — the decision (`github`, or deferred), whether the connect command was printed, and whether\n `connect-version-control.md` was written and at what path.\n- `repository_facts` — whether a local repository, any commits, and a remote exist. Report absence as\n absence, never as an error.\n\nNever include a credential, a token, or any secret-like value in this payload, and never include the\nraw interview transcript.\n",
1002
+ "greenfield-synthesize-standards.md": "Turn the ratified project framing into one coherent set of project standards, and take exactly one\napproval on the whole set. These standards are what the agents will follow until this project has real\ncode to learn from.\n\n## Step 0 — Decide whether this task runs at all\n\nRead the previous task's result.\n\n- **`setup_path` is `defaults`** — return immediately. Do not call `request_council`, do not draft a\n single standards field, and do not ask for approval. The defaults path exists precisely to skip this\n work, and the server seeds catalog defaults when the final apply records it.\n- **`setup_path` is `interview` but `decision_committed` is not true** — refuse to execute. Report that\n the interview did not end in a ratified decision commit and return without drafting anything.\n Standards synthesized from an uncommitted framing would encode assumptions the human never agreed\n to, which is the one failure this pipeline's ordering exists to prevent.\n- **`setup_path` is `interview` and the decision was committed** — continue.\n\n## Step 1 — Determine which fields are unset\n\nRead the ten `config_field` get results from earlier in this pipeline. A field is **unset** — and\ntherefore in scope — when its `value` is null, absent, or whitespace only, **or** when\n`is_catalog_default` is true (Bridge-seeded catalog content is not this project's standard).\n\nEvery other field is already populated. Exclude it: do not draft it, do not summarize it, and do not\ninclude it in the approval screen. Decide this from the fetch results, not from whether a draft file\nhappens to exist on disk from an earlier run, and not from the upload steps' `only_if_null` guard —\nthat guard is the server's last line of defense, not this task's decision procedure.\n\n## Step 2 — Ask the council once\n\nCall `request_council` exactly once, with:\n\n- `mode`: `\"general\"` — hardcoded. **Never** `technical` and never `discovery`: both expect an indexed\n repository, and this project has no code to index. Never pass a different mode, and never retry with\n one.\n- `wait_for_result`: `true`.\n- `task_description`: the brief described below.\n\nBuild the brief from the ratified framing (the business goal, desired end-state, system behavior, and\nthe settled NFR list), the definitions of the ten standards fields listed in Step 3, and each field's\ncharacter cap. Ask the council for a **translation brief** — how this project's goals and constraints\nshould shape each standard, what to emphasize, what to leave out. Do **not** ask it to write the final\nstandards text; that is Step 3's job and it must read as one coherent voice.\n\nRedact any secret-like value out of the brief before sending it.\n\n**A council failure or timeout is not a failure of this task.** Continue the synthesis without it and\nmention the missing advisory input once, as a plain informational line — not a warning, not an error,\nand not a reason to pause. The standards still get written.\n\n## Step 3 — Synthesize one coherent set of standards\n\nDraft **one** set of standards covering every unset field from Step 1 plus a concise\n`project_description`. Write them as a single coherent whole — the review standard should reflect the\nsame architecture the architecture standard describes, and the testing standards should agree with each\nother. Do not generate ten unrelated field-by-field documents.\n\nThe ten fields, and where each draft goes:\n\n| Field | Draft path | Max characters |\n| --- | --- | --- |\n| `architecture_instructions` | `{docs_dir}/greenfield/{repo_name}/standards/architecture_instructions.md` | 6000 |\n| `review_instructions` | `{docs_dir}/greenfield/{repo_name}/standards/review_instructions.md` | 6000 |\n| `documentation_instructions` | `{docs_dir}/greenfield/{repo_name}/standards/documentation_instructions.md` | 1500 |\n| `unit_testing_instructions` | `{docs_dir}/greenfield/{repo_name}/standards/unit_testing_instructions.md` | 6000 |\n| `e2e_testing_instructions` | `{docs_dir}/greenfield/{repo_name}/standards/e2e_testing_instructions.md` | 6000 |\n| `frontend_correctness_standards` | `{docs_dir}/greenfield/{repo_name}/standards/frontend_correctness_standards.md` | 6000 |\n| `backend_correctness_standards` | `{docs_dir}/greenfield/{repo_name}/standards/backend_correctness_standards.md` | 6000 |\n| `template_correctness_standards` | `{docs_dir}/greenfield/{repo_name}/standards/template_correctness_standards.md` | 6000 |\n| `style_correctness_standards` | `{docs_dir}/greenfield/{repo_name}/standards/style_correctness_standards.md` | 6000 |\n| `design_principles` | `{docs_dir}/greenfield/{repo_name}/standards/design_principles.md` | 6000 |\n\nRules for every draft:\n\n1. **Write only the unset fields.** A populated field keeps its existing content untouched.\n2. **Begin every draft with this exact marker line, as its first line:**\n `> Greenfield standard: chosen before implementation code existed; revisit once the repository has real code.`\n3. **Respect the character cap** in the table — 6,000 characters per field, and 1,500 for\n `documentation_instructions`. Measure in characters. Condense rather than truncate: drop\n repetition and long examples, never a required section and never mid-sentence.\n4. **Derive each field's section skeleton from `learn-repository-fan-out.md`**, whose field matrix names\n what each standard must cover. Adapt the depth to a project with no code: state the intended\n convention rather than citing files that do not exist, and never invent a file path, command, or\n pattern. Never mention the `/learn-repository` command to the human — this project has nothing to\n learn from yet, and pointing them at it is a dead end.\n5. **Redact secrets** from every draft, every summary, and everything returned.\n\nWrite each draft to its path in the table. A field you decide not to draft must leave **no file** at\nits path, so the declarative upload step that follows simply skips it.\n\n## Step 4 — One review screen, one approval\n\nPresent **one** screen containing:\n\n- the proposed `project_description`, in full — it is short, and it is what the human is really\n confirming; and\n- **one short summary per drafted field** — a line or two naming what that standard commits the project\n to.\n\nKeep the full drafts secondary: expandable, or offered on request, wherever the interaction surface\nsupports it. The screen the human reads is the summary, not ten documents.\n\nThen ask **exactly one** approval question, with two outcomes: `yes` or `no`.\n\n- **`yes`** — the drafts stand as written. Continue.\n- **`no`** — ask exactly one follow-up: `targeted revisions (which?) or use defaults?`\n - **Targeted revisions** — revise only the named fields, then present the same one-screen review and\n the same single approval question again. Allow **at most two** revision loops.\n - **Use defaults**, or a second unapproved round after those two loops — the final outcome becomes\n `defaults`. Discard the drafts: delete every draft file this task wrote so nothing unapproved can\n be uploaded, and record the final path as `defaults`. Unapproved and abandoned drafts are not\n standards, and must never reach a `config_field` write.\n\nMaintain the visible sequence `Decision committed` → `Standards review` → `One approval`. Never imply\nthat synthesis began before the commit, because it did not.\n\n## Return\n\nReturn a secret-free structured payload carrying:\n\n- `final_path` — `interview` when the standards were approved, `defaults` when the human chose defaults\n or the revision loops ran out.\n- `project_description` — the approved description, or absent when the final path is `defaults`.\n- `drafted_fields` — the fields with an approved draft on disk, each with its path; empty when the\n final path is `defaults`.\n- `field_summaries` — the one-line summary shown for each drafted field.\n- `skipped_fields` — the fields excluded because they were already populated.\n- `council_available` — whether the council result was used, or the synthesis proceeded without it.\n- `revision_loops` — how many targeted revision rounds ran (0, 1, or 2).\n\nNever include a credential, a token, or any secret-like value in this payload.\n",
1003
+ "learn-architecture.md": "## Objective\n\nExplore the codebase to identify architectural principles, directory conventions, design patterns, and data flow, then draft `architecture_instructions` for the project config.\n\n## Instructions\n\n### Phase 1 — Principles Research\n\nResearch the codebase to identify architectural principles and conventions. For each area below, examine at least 5 representative files. Cite file paths for every pattern. Include code examples (5-15 lines) showing correct usage. Where relevant, include a WRONG example showing the common mistake.\n\nIf there is nothing repository-specific to cite, say that no repository evidence was found and write a short conservative standard; do not invent files, commands, or conventions.\n\nFor each pattern, classify its evidence level:\n- `ENFORCED` — consistently followed across the codebase, violations would be bugs\n- `CONVENTION` — commonly observed, occasional deviations exist\n- `ASPIRATIONAL` — intended direction, not yet consistently applied\n\nResearch areas:\n1. **Architectural coding patterns**: Search `api/routes/` and `api/library/` for separation of concerns, layer boundaries, function-vs-class decisions. Read files matching `*_lib.py`, `*_utils.py`, `*_helpers.py` to document module naming suffix conventions.\n2. **Design patterns**: Search for factory functions, strategy patterns, middleware chains, registry patterns, and dependency injection in `api/` and `src/python/`. Cite concrete usage with file path and function name.\n3. **Dependency management**: Read `requirements.in`, `requirements-dev.in`, and `package.json` files to document how dependencies are declared and organized.\n4. **Error handling architecture**: Search for `log_exception_to_sentry` and `HTTPException` usage patterns across `api/routes/` to document the system-wide error propagation strategy.\n5. **Configuration management**: Search for `os.environ` and `get_config_field_value` usage to document the two-tier system (env vars vs. database config).\n6. **Tech stack detection**: Read `requirements.in`, `package.json`, and `main.py` to identify primary languages, frameworks, and key libraries.\n7. **Security architecture**: Read `api/routes/setup/auth.py` and search for `require_api_key`, `require_api_session`, and `verify_repo_access` to document authentication and authorization design.\n8. **Agent prompting conventions**: Read files in `src/python/llms/agents/` to document prompt construction, section headers, dynamic content delimiters, and role-based personas.\n\nScope exclusion: Do NOT document testing patterns. Skip the `tests/` directory entirely.\n\nWrite findings to `{docs_dir}/tmp/architecture-principles.md`.\n\n### Phase 2 — Structure & Data Flow Research\n\n1. Call the `regenerate_directory_map` MCP tool to get a fresh directory map.\n2. Read the principles document from Phase 1.\n3. Research and document:\n - **Directory conventions**: For each major directory, document purpose, file naming, internal structure, and an example file.\n - **Module boundaries and import patterns**: Which directories are distinct modules and how they interact. Document import restrictions.\n - **Data flow patterns**: Trace 2-3 complete request paths (synchronous, async background task, agent orchestration).\n - **Integration patterns**: How external services (Jira, GitHub/Bitbucket, LLMs, Pinecone, PostgreSQL) are integrated.\n - **Background task patterns**: The async task lifecycle with `asyncio.create_task`, semaphores, and error reporting.\n\nWrite findings to `{docs_dir}/tmp/architecture-structure.md`.\n\n### Phase 3 — Draft\n\n1. Read both research documents.\n2. Combine into a single `architecture_instructions` draft with these required sections:\n - **1. Core Principles** — Each principle with evidence level and explanation.\n - **2. Layered Architecture** — Layer separation, dependency rule, agent vs orchestration logic.\n - **3. Directory Conventions** — Purpose, naming, structure for each major directory.\n - **4. Data Flow Patterns** — Complete request path traces with file paths.\n - **5. Technical Standards** — Coding style, async patterns, database, schema, LLM integration, config, dependencies.\n - **6. Error Handling & Monitoring** — Error propagation strategy, Sentry integration, Langfuse tracing.\n - **7. Security & Authentication** — Auth architecture, session model, permission model.\n - **8. Agent Prompting Conventions** — Prompt construction, section headers, content delimiters.\n - **9. Integration Points** — External service clients and their calling patterns.\n - **10. AI Code Generation Guidelines** — Anti-patterns, duplication avoidance, pattern compliance checklist.\n\n3. Write the draft to `{docs_dir}/standards/architecture_instructions.md`.\n\n## Length Budget\n\nThe platform ceiling for a configuration field is **40,000 characters**, and the server enforces it\nat upload time: a draft over the limit is rejected outright and ``architecture_instructions`` is simply not learned. Bound\nthe draft here, at write time, rather than discovering the limit through a failed upload.\n\n1. When the draft is complete, measure its length in **characters** — not tokens, not words, not\n bytes.\n2. If it exceeds 40,000 characters, condense it: remove redundancy, collapse repetitive passages, and\n shorten or drop verbose code examples.\n3. Keep every required section listed above. Never meet the budget by deleting a required section,\n and never truncate the draft mid-sentence.\n4. Re-measure after condensing and repeat until the draft is at most 40,000 characters.\n\n## Return\n\nReturn a brief summary of what was learned about the project's architecture (core principles, directory conventions, data flow), citing the key files inspected, and confirm the draft was written to `{docs_dir}/standards/architecture_instructions.md`.\n\nAlso report the draft's final length in characters, the applicable maximum (40,000 characters),\nwhether the draft was condensed to meet that maximum (`condensed`: true/false), and — when it was\ncondensed — the reason it needed condensing.\n",
1004
+ "learn-backend-correctness.md": "## Objective\n\nExplore the codebase to identify correctness standards for backend code, then draft the corresponding correctness standards document.\n\n## Target Type\n\n- **Type**: `backend_correctness`\n- **Field name**: `backend_correctness_standards`\n- **Scope**: Server-side code: Python, Ruby, Go, Java, C#, Node.js server code, API routes, business logic.\n\n## Instructions\n\n### Phase 1 — Explore Correctness Patterns\n\nFocus on implementation correctness: how to write code that is correct, idiomatic, and robust within this project's conventions.\n\n1. **File Type Detection**: Search by filename pattern for files matching `**/*.py` in `api/` and `src/python/`. If very few or no files exist, note this and draft minimal instructions.\n\n2. **Convention Analysis**: Read 3-5 representative files in `api/routes/` and `api/library/` to identify:\n - Structure patterns (imports, exports, class structure, function ordering)\n - Naming conventions (variables, functions, classes, files)\n - Framework conventions and idioms\n - Best practices followed\n - Issues and inconsistencies\n\n Also read files to document:\n - Error handling implementation (try/except ordering, Sentry calls) with CORRECT/WRONG examples\n - Authentication implementation (auth check sequence) with code examples\n - Database call patterns (`postgres_helpers` (bool, result) tuple handling) with CORRECT/WRONG examples\n - Input validation patterns (Pydantic models, naming conventions)\n - HTTP client patterns (error handling, JiraError sanitization)\n - Async implementation patterns (`asyncio.to_thread()` for blocking code)\n\nIf there is nothing repository-specific to cite, say that no repository evidence was found and write a short conservative standard; do not invent files, commands, or conventions.\n\n### Phase 2 — Draft\n\nDraft correctness standards as clear, actionable instructions for an AI code generation agent. Cover:\n- Code structure and organization requirements\n- Naming conventions to follow\n- Framework-specific patterns and idioms\n- Security requirements relevant to this code type\n- Performance considerations\n- Common mistakes to avoid\n- Guards against common AI weaknesses: duplicative code, verbose implementations, security vulnerabilities\n\nAlso include:\n- Route handler boilerplate (auth -> validation -> business logic -> error handling)\n- Database interaction patterns with CORRECT/WRONG examples\n- Exception handling pattern (specific first, HTTPException re-raise, generic with Sentry)\n- Sentry reporting patterns and common mistakes\n- Input sanitization rules (JiraError headers, raw exception messages)\n\nWrite the draft to `{docs_dir}/standards/backend_correctness_standards.md`.\n\n## Length Budget\n\nThe platform ceiling for a configuration field is **40,000 characters**, and the server enforces it\nat upload time: a draft over the limit is rejected outright and ``backend_correctness_standards`` is simply not learned. Bound\nthe draft here, at write time, rather than discovering the limit through a failed upload.\n\n1. When the draft is complete, measure its length in **characters** — not tokens, not words, not\n bytes.\n2. If it exceeds 40,000 characters, condense it: remove redundancy, collapse repetitive passages, and\n shorten or drop verbose code examples.\n3. Keep every required section listed above. Never meet the budget by deleting a required section,\n and never truncate the draft mid-sentence.\n4. Re-measure after condensing and repeat until the draft is at most 40,000 characters.\n\n## Return\n\nReturn a brief summary of what was learned about backend correctness conventions (structure, naming, error handling, auth, DB patterns), citing the key files inspected, and confirm the draft was written to `{docs_dir}/standards/backend_correctness_standards.md`.\n\nAlso report the draft's final length in characters, the applicable maximum (40,000 characters),\nwhether the draft was condensed to meet that maximum (`condensed`: true/false), and — when it was\ncondensed — the reason it needed condensing.\n",
716
1005
  "learn-design-principles.md": "## Objective\n\nExplore the codebase to identify frontend design principles, then draft a structured design principles document.\n\n## Target Type\n\n- **Type**: `design_principles`\n- **Field name**: `design_principles`\n- **Scope**: Visual identity, design tokens, component inventory, layout patterns, composition rules, interaction patterns, and anti-patterns.\n\n## Instructions\n\n### Phase 1 — Explore Design Patterns\n\nSearch the codebase by filename pattern, search file contents by text pattern, and read relevant files to explore the codebase for design patterns. **Exclude `node_modules/`, `dist/`, `build/`, `.next/`, and `__pycache__/` from filename-pattern searches** to avoid token bloat.\n\nCall the `get_project_standards` MCP tool to check if `working_in` and `version` context is available. If available, use them to prioritize relevant file types. If unavailable or the call fails, read dependency files (`package.json`, `tailwind.config.js`, `postcss.config.js`) to infer the framework and styling approach.\n\n1. **Design Token Detection**: Search for CSS custom properties, SCSS/LESS variables, theme configs, Tailwind config, and design token definitions. Document naming conventions, token hierarchy, and value scales (spacing, colors, typography).\n\n2. **Component Inventory**: Search by filename pattern for component files (JSX/TSX/Vue/Svelte/ISML/template files). Read 5-10 representative components to identify composition patterns, prop interfaces, naming conventions, and component categories.\n\n3. **Style Architecture**: Find and analyze stylesheets (CSS/SCSS/LESS/styled-components/Tailwind). Document methodology (BEM, CSS Modules, utility-first), responsive breakpoints, and media query patterns.\n\n4. **Layout Patterns**: Identify grid systems, page templates, container components, and responsive layout strategies.\n\n5. **Interaction Patterns**: Search for animations, transitions, hover states, loading states, and error states.\n\n6. **Visual Consistency Audit**: Compare patterns across files. Note inconsistencies in spacing, color usage, component structure, or naming.\n\n### Phase 2 — Draft\n\nSynthesize findings into a structured document with exactly these 7 sections:\n\n1. **Visual Identity** — Colors, typography, spacing scales, iconography, visual tone\n2. **Design Token Reference** — Token naming conventions, hierarchy, value definitions\n3. **Component Inventory** — What components exist, their responsibilities, naming patterns\n4. **Page Layout Patterns** — Grid systems, page templates, responsive strategies, container patterns\n5. **Composition Rules** — How components combine, nesting patterns, slot/children conventions\n6. **Interaction Patterns** — Animations, transitions, states, hover/focus/active behaviors\n7. **Anti-patterns** — Inconsistencies found, patterns to avoid, deprecated approaches\n\nWrite the draft to `{docs_dir}/standards/design_principles.md`.\n\n## Length Budget\n\nThe platform ceiling for a configuration field is **40,000 characters**, and the server enforces it\nat upload time: a draft over the limit is rejected outright and ``design_principles`` is simply not learned. Bound\nthe draft here, at write time, rather than discovering the limit through a failed upload.\n\n1. When the draft is complete, measure its length in **characters** — not tokens, not words, not\n bytes.\n2. If it exceeds 40,000 characters, condense it: remove redundancy, collapse repetitive passages, and\n shorten or drop verbose code examples.\n3. Keep every required section listed above. Never meet the budget by deleting a required section,\n and never truncate the draft mid-sentence.\n4. Re-measure after condensing and repeat until the draft is at most 40,000 characters.\n\n## Return\n\nReturn a brief summary of what was learned about the project's design principles (visual identity, tokens, components, layout, interactions), citing the key files inspected, and confirm the draft was written to `{docs_dir}/standards/design_principles.md`.\n\nAlso report the draft's final length in characters, the applicable maximum (40,000 characters),\nwhether the draft was condensed to meet that maximum (`condensed`: true/false), and — when it was\ncondensed — the reason it needed condensing.\n",
717
- "learn-documentation-instructions.md": "## Objective\n\nExplore the codebase to identify implementation documentation patterns — the markdown records that document what was built, why, and when — then draft `documentation_instructions` for the project config.\n\n## Instructions\n\n### Phase 1 — Explore Implementation Record Patterns\n\nFocus on how the project records what was built, why, and when. These records serve as persistent project memory. Code-level documentation (docstrings, inline comments) is handled by correctness standards, not here.\n\n1. **Implementation Record Discovery**: Search for:\n - Ticket-numbered documents matching `BAPI-*.md` or `PROJ-*.md` in `docs/` and subdirectories\n - Feature/migration documents in `docs/`, `documentation/`, or similar directories\n - Architecture Decision Records (ADRs) in `adr/`, `decisions/`, or similar\n - Changelogs (`CHANGELOG.md`, release notes)\n\n Count how many records exist and identify the naming convention.\n\n2. **Record Structure Analysis**: Read 3-5 representative implementation records (mix of early and recent). Document:\n - Sections present (Summary, Architecture, Database Changes, API Reference, etc.)\n - Level of detail provided\n - Types of information captured (motivation, design decisions, schema changes, file paths, API contracts)\n - How code examples and diagrams are used\n\n3. **Documentation Location and Organization**: Read the directory structure of `docs/` to identify where records are stored, the file naming convention, whether there is a table of contents or index, and whether subdirectories serve different purposes.\n\n### Phase 2 — Draft\n\nDraft `documentation_instructions` as **exactly one concise prose paragraph** that an AI agent will follow when writing implementation documentation after completing a feature. The drafted value is inlined verbatim into a generated plan step, so it has hard formatting constraints:\n\n- The output MUST be **one prose paragraph under 1,500 characters**.\n- The output MUST avoid **markdown headings, bullets, numbered lists, and intentional blank lines**. Write flowing prose (semicolon-separated clauses are fine), not a document outline or multi-section manual.\n- The paragraph MUST cover, in prose: the discovered **file naming convention** (or a sensible default), the **file location** where implementation records live, and the **key content to include** (what changed and why, important files and design decisions, any API/configuration/database impacts, and brief usage or validation examples).\n- The paragraph SHOULD include **skip guidance**: skip implementation documentation for trivial, test-only, or docs-only changes where appropriate.\n\nKeep the scope to implementation records only; code-level documentation (docstrings, inline comments) belongs in correctness standards.\n\nWrite the draft to `{docs_dir}/standards/documentation_instructions.md`.\n\n## Length Budget\n\nThe general platform ceiling for a configuration field is **40,000 characters**, but\n`documentation_instructions` has a stricter **effective limit of 1,500 characters** enforced by the\nserver for this field specifically, because the value is inlined verbatim into a generated plan step.\nThe uploaded draft must satisfy the 1,500-character limit — the 40,000-character ceiling is not the\nconstraint that applies here.\n\n1. When the draft is complete, measure its length in **characters** — not tokens, not words, not\n bytes.\n2. If it exceeds 1,500 characters, condense it: remove redundancy, tighten the prose, and drop\n examples before anything else.\n3. Keep all four required topics (naming convention, location, key content, skip guidance) and the\n single-paragraph form. Never meet the budget by dropping a required topic, and never truncate the\n paragraph mid-sentence.\n4. Re-measure after condensing and repeat until the draft is at most 1,500 characters.\n\n## Return\n\nReturn a brief summary of what was learned about the project's implementation-record conventions (naming, location, required sections), citing the key files inspected, and confirm the draft was written to `{docs_dir}/standards/documentation_instructions.md`.\n\nAlso report the draft's final length in characters, the applicable maximum (1,500 characters),\nwhether the draft was condensed to meet that maximum (`condensed`: true/false), and — when it was\ncondensed — the reason it needed condensing.\n",
718
- "learn-e2e-testing.md": "## Objective\n\nDetect whether an E2E testing framework exists in the codebase, document how to run and write E2E tests, then draft `e2e_testing_instructions` for the project config.\n\n## Instructions\n\n### Phase 1 — Detect E2E Framework\n\nSearch for config files and indicators:\n- **Playwright**: Search for `playwright.config.ts`, `playwright.config.js`, `@playwright/test` in `package.json`\n- **Cypress**: Search for `cypress.json`, `cypress.config.*`, `cypress/` directory\n- **Selenium/WebDriver**: Search for `selenium` in `requirements.in` or `package.json`\n- **Puppeteer**: Search for `puppeteer` in `package.json`\n- **TestCafe**: Search for `.testcaferc.json`\n\nAlso read `package.json` for E2E-related scripts and search for test directories containing E2E tests.\n\nIf NO E2E testing framework is detected, write \"No E2E testing framework detected in this repository.\" to `{docs_dir}/standards/e2e_testing_instructions.md` and stop.\n\n### Phase 2 — Explore E2E Testing Conventions\n\n1. **Test Execution**: Read the E2E config file and `package.json` scripts to determine exact commands (all tests, single file, headed/headless), prerequisites (server running, database seeded), and environment requirements.\n\n2. **Test Patterns**: Read 2-3 representative E2E test files in `tests/playwright/` to identify structure (page objects, fixtures, helpers), login/auth flows, test data setup/teardown, async waiting strategies, and selector patterns.\n\n3. **Common Pitfalls**: Search for hard-coded waits (`setTimeout`, `page.waitForTimeout`), test isolation issues, and browser state management patterns across E2E test files.\n\n### Phase 3 — Draft\n\nDraft `e2e_testing_instructions` as clear, actionable instructions for an AI agent writing E2E tests. Cover:\n- How to run tests (exact commands, prerequisites)\n- Test structure and organization\n- Authentication and setup patterns\n- How to wait for async operations (never hard-coded sleeps)\n- Common pitfalls with browser automation\n- Guards against common AI weaknesses: flaky tests, brittle selectors, hard-coded waits\n\nWrite the draft to `{docs_dir}/standards/e2e_testing_instructions.md`.\n\n## Length Budget\n\nThe platform ceiling for a configuration field is **40,000 characters**, and the server enforces it\nat upload time: a draft over the limit is rejected outright and ``e2e_testing_instructions`` is simply not learned. Bound\nthe draft here, at write time, rather than discovering the limit through a failed upload.\n\n1. When the draft is complete, measure its length in **characters** — not tokens, not words, not\n bytes.\n2. If it exceeds 40,000 characters, condense it: remove redundancy, collapse repetitive passages, and\n shorten or drop verbose code examples.\n3. Keep every required section listed above. Never meet the budget by deleting a required section,\n and never truncate the draft mid-sentence.\n4. Re-measure after condensing and repeat until the draft is at most 40,000 characters.\n\n## Return\n\nReturn a brief summary of what was learned about the project's E2E testing setup (framework detected, run commands, test patterns) — or state that no framework was detected — citing the key files inspected, and confirm the draft was written to `{docs_dir}/standards/e2e_testing_instructions.md`.\n\nAlso report the draft's final length in characters, the applicable maximum (40,000 characters),\nwhether the draft was condensed to meet that maximum (`condensed`: true/false), and — when it was\ncondensed — the reason it needed condensing.\n",
1006
+ "learn-documentation-instructions.md": "## Objective\n\nExplore the codebase to identify implementation documentation patterns — the markdown records that document what was built, why, and when — then draft `documentation_instructions` for the project config.\n\n## Instructions\n\n### Phase 1 — Explore Implementation Record Patterns\n\nFocus on how the project records what was built, why, and when. These records serve as persistent project memory. Code-level documentation (docstrings, inline comments) is handled by correctness standards, not here.\n\n1. **Implementation Record Discovery**: Search for:\n - Ticket-numbered documents matching `BAPI-*.md` or `PROJ-*.md` in `docs/` and subdirectories\n - Feature/migration documents in `docs/`, `documentation/`, or similar directories\n - Architecture Decision Records (ADRs) in `adr/`, `decisions/`, or similar\n - Changelogs (`CHANGELOG.md`, release notes)\n\n Count how many records exist and identify the naming convention.\n\n2. **Record Structure Analysis**: Read 3-5 representative implementation records (mix of early and recent). Document:\n - Sections present (Summary, Architecture, Database Changes, API Reference, etc.)\n - Level of detail provided\n - Types of information captured (motivation, design decisions, schema changes, file paths, API contracts)\n - How code examples and diagrams are used\n\n3. **Documentation Location and Organization**: Read the directory structure of `docs/` to identify where records are stored, the file naming convention, whether there is a table of contents or index, and whether subdirectories serve different purposes.\n\n### Phase 2 — Draft\n\nDraft `documentation_instructions` as **exactly one concise prose paragraph** that an AI agent will follow when writing implementation documentation after completing a feature. The drafted value is inlined verbatim into a generated plan step, so it has hard formatting constraints:\n\n- The output MUST be **one prose paragraph under 1,500 characters**.\n- The output MUST avoid **markdown headings, bullets, numbered lists, and intentional blank lines**. Write flowing prose (semicolon-separated clauses are fine), not a document outline or multi-section manual.\n- The paragraph MUST cover, in prose: the discovered **file naming convention** (or a sensible default), the **file location** where implementation records live, and the **key content to include** (what changed and why, important files and design decisions, any API/configuration/database impacts, and brief usage or validation examples).\n- The paragraph SHOULD include **skip guidance**: skip implementation documentation for trivial, test-only, or docs-only changes where appropriate.\n\nKeep the scope to implementation records only; code-level documentation (docstrings, inline comments) belongs in correctness standards.\n\nIf there is nothing repository-specific to cite, say that no repository evidence was found and write a short conservative standard; do not invent files, commands, or conventions.\n\nWrite the draft to `{docs_dir}/standards/documentation_instructions.md`.\n\n## Length Budget\n\nThe general platform ceiling for a configuration field is **40,000 characters**, but\n`documentation_instructions` has a stricter **effective limit of 1,500 characters** enforced by the\nserver for this field specifically, because the value is inlined verbatim into a generated plan step.\nThe uploaded draft must satisfy the 1,500-character limit — the 40,000-character ceiling is not the\nconstraint that applies here.\n\n1. When the draft is complete, measure its length in **characters** — not tokens, not words, not\n bytes.\n2. If it exceeds 1,500 characters, condense it: remove redundancy, tighten the prose, and drop\n examples before anything else.\n3. Keep all four required topics (naming convention, location, key content, skip guidance) and the\n single-paragraph form. Never meet the budget by dropping a required topic, and never truncate the\n paragraph mid-sentence.\n4. Re-measure after condensing and repeat until the draft is at most 1,500 characters.\n\n## Return\n\nReturn a brief summary of what was learned about the project's implementation-record conventions (naming, location, required sections), citing the key files inspected, and confirm the draft was written to `{docs_dir}/standards/documentation_instructions.md`.\n\nAlso report the draft's final length in characters, the applicable maximum (1,500 characters),\nwhether the draft was condensed to meet that maximum (`condensed`: true/false), and — when it was\ncondensed — the reason it needed condensing.\n",
1007
+ "learn-e2e-testing.md": "## Objective\n\nDetect whether an E2E testing framework exists in the codebase, document how to run and write E2E tests, then draft `e2e_testing_instructions` for the project config.\n\n## Instructions\n\n### Phase 1 — Detect E2E Framework\n\nSearch for config files and indicators:\n- **Playwright**: Search for `playwright.config.ts`, `playwright.config.js`, `@playwright/test` in `package.json`\n- **Cypress**: Search for `cypress.json`, `cypress.config.*`, `cypress/` directory\n- **Selenium/WebDriver**: Search for `selenium` in `requirements.in` or `package.json`\n- **Puppeteer**: Search for `puppeteer` in `package.json`\n- **TestCafe**: Search for `.testcaferc.json`\n\nAlso read `package.json` for E2E-related scripts and search for test directories containing E2E tests.\n\nIf NO E2E testing framework is detected, write \"No E2E testing framework detected in this repository.\" to `{docs_dir}/standards/e2e_testing_instructions.md` and stop.\n\nIf there is nothing repository-specific to cite, say that no repository evidence was found and write a short conservative standard; do not invent files, commands, or conventions.\n\n### Phase 2 — Explore E2E Testing Conventions\n\n1. **Test Execution**: Read the E2E config file and `package.json` scripts to determine exact commands (all tests, single file, headed/headless), prerequisites (server running, database seeded), and environment requirements.\n\n2. **Test Patterns**: Read 2-3 representative E2E test files in `tests/playwright/` to identify structure (page objects, fixtures, helpers), login/auth flows, test data setup/teardown, async waiting strategies, and selector patterns.\n\n3. **Common Pitfalls**: Search for hard-coded waits (`setTimeout`, `page.waitForTimeout`), test isolation issues, and browser state management patterns across E2E test files.\n\n### Phase 3 — Draft\n\nDraft `e2e_testing_instructions` as clear, actionable instructions for an AI agent writing E2E tests. Cover:\n- How to run tests (exact commands, prerequisites)\n- Test structure and organization\n- Authentication and setup patterns\n- How to wait for async operations (never hard-coded sleeps)\n- Common pitfalls with browser automation\n- Guards against common AI weaknesses: flaky tests, brittle selectors, hard-coded waits\n\nWrite the draft to `{docs_dir}/standards/e2e_testing_instructions.md`.\n\n## Length Budget\n\nThe platform ceiling for a configuration field is **40,000 characters**, and the server enforces it\nat upload time: a draft over the limit is rejected outright and ``e2e_testing_instructions`` is simply not learned. Bound\nthe draft here, at write time, rather than discovering the limit through a failed upload.\n\n1. When the draft is complete, measure its length in **characters** — not tokens, not words, not\n bytes.\n2. If it exceeds 40,000 characters, condense it: remove redundancy, collapse repetitive passages, and\n shorten or drop verbose code examples.\n3. Keep every required section listed above. Never meet the budget by deleting a required section,\n and never truncate the draft mid-sentence.\n4. Re-measure after condensing and repeat until the draft is at most 40,000 characters.\n\n## Return\n\nReturn a brief summary of what was learned about the project's E2E testing setup (framework detected, run commands, test patterns) — or state that no framework was detected — citing the key files inspected, and confirm the draft was written to `{docs_dir}/standards/e2e_testing_instructions.md`.\n\nAlso report the draft's final length in characters, the applicable maximum (40,000 characters),\nwhether the draft was condensed to meet that maximum (`condensed`: true/false), and — when it was\ncondensed — the reason it needed condensing.\n",
719
1008
  "learn-frontend-correctness.md": "## Objective\n\nExplore the codebase to identify correctness standards for frontend code, then draft the corresponding correctness standards document.\n\n## Target Type\n\n- **Type**: `frontend_correctness`\n- **Field name**: `frontend_correctness_standards`\n- **Scope**: JS, TS, JSX, TSX files: React/Vue/Angular/Svelte components, client-side logic, state management.\n\n## Instructions\n\n### Phase 1 — Explore Correctness Patterns\n\nFocus on implementation correctness: how to write code that is correct, idiomatic, and robust within this project's conventions.\n\n1. **File Type Detection**: Search by filename pattern for files matching `**/*.js`, `**/*.ts`, `**/*.jsx`, `**/*.tsx` (excluding `node_modules/` and `build/`). If very few or no files exist, note this and draft minimal instructions.\n\n2. **Convention Analysis**: Read 3-5 representative frontend files to identify:\n - Structure patterns (imports, exports, class structure, function ordering)\n - Naming conventions (variables, functions, classes, files)\n - Framework conventions and idioms\n - Best practices followed\n - Issues and inconsistencies\n\n### Phase 2 — Draft\n\nDraft correctness standards as clear, actionable instructions for an AI code generation agent. Cover:\n- Code structure and organization requirements\n- Naming conventions to follow\n- Framework-specific patterns and idioms\n- Security requirements relevant to this code type\n- Performance considerations\n- Common mistakes to avoid\n- Guards against common AI weaknesses: duplicative code, verbose implementations, security vulnerabilities\n\nWrite the draft to `{docs_dir}/standards/frontend_correctness_standards.md`.\n\n## Length Budget\n\nThe platform ceiling for a configuration field is **40,000 characters**, and the server enforces it\nat upload time: a draft over the limit is rejected outright and ``frontend_correctness_standards`` is simply not learned. Bound\nthe draft here, at write time, rather than discovering the limit through a failed upload.\n\n1. When the draft is complete, measure its length in **characters** — not tokens, not words, not\n bytes.\n2. If it exceeds 40,000 characters, condense it: remove redundancy, collapse repetitive passages, and\n shorten or drop verbose code examples.\n3. Keep every required section listed above. Never meet the budget by deleting a required section,\n and never truncate the draft mid-sentence.\n4. Re-measure after condensing and repeat until the draft is at most 40,000 characters.\n\n## Return\n\nReturn a brief summary of what was learned about frontend correctness conventions (structure, naming, framework idioms), citing the key files inspected, and confirm the draft was written to `{docs_dir}/standards/frontend_correctness_standards.md`.\n\nAlso report the draft's final length in characters, the applicable maximum (40,000 characters),\nwhether the draft was condensed to meet that maximum (`condensed`: true/false), and — when it was\ncondensed — the reason it needed condensing.\n",
720
1009
  "learn-repository-confirmations.md": "## Objective\n\nApply the configuration fields that the server requires a human to confirm — in **one** batched round\nat the very end of the run, after everything that could be applied unattended already has been.\n\nFields carrying `requires_confirmation: true` can never be written on derivation alone: the\nrequirement is a server-side registry property, not a client-side courtesy. This task is where the\nhuman is asked, exactly once, with the full run behind them.\n\n## Operating rules\n\n1. **One question, not one per field.** Every candidate is presented together in a single round.\n2. **Only explicitly approved values are applied.** Silence is not approval.\n3. **Never stall.** If no human response can be obtained, omit the unconfirmed fields, report them,\n and finish successfully.\n4. **This step never invalidates the run.** The learned fields were already uploaded by earlier\n steps. A decline, a rejection, or a conflict here is a reportable outcome for that one field — it\n never undoes or discards anything already applied.\n\n## Step 1 — Read the manifest once\n\n1. Call the `get_install_manifest` MCP tool **exactly once**, with `save_locally: false` — this is a\n confirmation round, not an install artifact worth writing to disk.\n2. Keep the returned `snapshot_token` **verbatim**. The apply call in Step 4 must present that same\n token. Do not re-read the manifest before applying; a fresh read invalidates the token you are\n about to use.\n3. Select candidates from the manifest's field groups using the manifest's own metadata:\n - the field's `requires_confirmation` is `true`, **and**\n - the field's `is_set` is `false`.\n\n A confirmation-required field that is already set is **not** a candidate: record it as\n `skipped_existing` and leave it alone. Do not maintain your own list of which fields require\n confirmation — the manifest is the authority, so a field the server adds later is picked up here\n with no change to this instruction.\n\n## Step 2 — Derive a proposed value for each candidate\n\n### `selected_mcp_slugs`\n\nMCP validation manuals are supplied to the final plan reviewer. Propose them **only** from clear\nrepository markers, and record the evidence you used for each:\n\n| Evidence in the repository | Proposed slug |\n| --- | --- |\n| SFCC cartridges, or Salesforce Commerce Cloud markers | `b2c-commerce-developer` |\n| A Playwright configuration file | `playwright-mcp` |\n| PWA Kit markers | `pwa-kit-mcp` |\n\nRules:\n\n- Every proposed slug must come from the manifest's supported values for the field. Never propose a\n slug that is not in the catalog — the server rejects unknown slugs outright.\n- Every proposed slug needs concrete evidence, named in the question. Cite the file or marker.\n- **No clear marker means the field is `not_applicable`** — report it as such and omit it from the\n question. Do not guess, and do not propose a manual on weak evidence: a wrong manual degrades the\n reviewer's behavior.\n\n### `project_description`\n\nIf `project_description` is still unset after install, derive a concise candidate description from the\nlocal codebase, following the field's manifest `guidance`. If it is already set, it is\n`skipped_existing`.\n\n### Any other confirmation-required candidate\n\nFor a candidate this instruction does not name explicitly, follow the field's manifest `guidance` to\nderive a value, and apply the same rule: no clear evidence means omit it rather than guess.\n\n## Step 3 — Ask once\n\nPresent **all** candidates in a **single** batched question. Separate per-field confirmation rounds\nare prohibited — the entire point of this step is that the human is interrupted exactly once.\n\nFor each candidate include:\n\n- the **field name**,\n- the **proposed value**,\n- the **evidence** behind the proposal, and\n- the **impact** of setting it — what changes about Bridge's behavior once the value is applied.\n\nLet the human approve or decline each field individually within that one round.\n\n## Step 4 — Apply the approved fields (one call)\n\n1. Make **at most one** `apply_install_manifest` call, passing the exact `snapshot_token` from Step 1.\n2. Submit every approved field in the `fields` map as `{ \"value\": <approved value>, \"confirmed\": true }`.\n The `confirmed: true` metadata is what satisfies the server's confirmation requirement.\n3. **Omit** declined fields and unanswered fields entirely — do not send them with `confirmed: false`,\n and do not send a placeholder value.\n4. If no field was approved, make no apply call at all.\n5. The apply is partial-tolerant. A field returned in the `rejected` bucket, or in the `conflict`\n bucket because it changed since the manifest was read, is reported as that field's outcome — the\n other approved fields still commit, and the learned fields uploaded earlier are untouched. Do not\n retry the whole apply because one field failed.\n\n## Step 5 — Headless safety\n\nIf interaction is unavailable, or no response can be obtained (a non-interactive or headless\nsession):\n\n- Do **not** wait indefinitely and do **not** abandon the run.\n- Omit **every** unconfirmed field from the apply.\n- Report each candidate as `pending human input`, using that exact phrase.\n- Finish successfully. Everything else the run applied stays applied — an unconfirmed field never\n blocks a confirmed or already-uploaded one.\n\n## Return\n\nReturn a single JSON object listing each field name in its outcome bucket:\n\n```json\n{\n \"approved\": [],\n \"applied\": [],\n \"declined\": [],\n \"pending_human_input\": [],\n \"not_applicable\": [],\n \"skipped_existing\": [],\n \"conflict\": [],\n \"rejected\": []\n}\n```\n\n- `approved` — the human approved the proposed value this round.\n- `applied` — the server committed the value.\n- `declined` — the human explicitly rejected the proposal.\n- `pending_human_input` — presented but unanswered, or never presentable (headless).\n- `not_applicable` — no clear evidence supported a proposal, so none was made.\n- `skipped_existing` — already set; never re-proposed.\n- `conflict` — changed since the manifest snapshot was read.\n- `rejected` — failed server-side validation.\n",
721
- "learn-repository-fan-out.md": "## Objective\n\nResearch all ten learned configuration fields for this repository **in parallel**, one isolated\nsubagent per field, and leave a bounded, upload-ready draft on disk for each field that needs one.\n\nThis step performs research only. It never uploads: the ten declarative `config_field` update steps\nthat follow this task own every write.\n\n## Operating rules (apply to the whole task)\n\n1. **Local research only.** Every field is learned by reading *this checkout* on local disk. Do not\n delegate research to the Bridge API backend or any server-side index — the backend has a vector\n index of the code, not the working tree in front of you.\n2. **One isolated subagent per missing field.** Use the host coding agent's own subagent/task\n mechanism. Each subagent gets exactly one field and does not see the others' work.\n3. **Launch before you await.** Start every missing-field subagent first, then collect results. Never\n spawn a subagent, wait for it to finish, and only then spawn the next one — a sequential\n spawn-and-wait loop defeats the entire purpose of this step.\n4. **Subagents never upload and never talk to the human.** A subagent's only outputs are its draft\n file and its structured result. All human interaction is deferred to the final confirmation step.\n5. **A failed field is not a failed run.** Isolate each failure to its own field, let the other\n subagents finish, and return a successful aggregate result naming the gap.\n\n## Step 1 — Classify each field from the ten preceding fetch results\n\nThe ten `config_field` steps immediately before this task already read the current value of every\nlearned field. Use **those results** — do not re-read the fields and do not infer state from whether\na draft file happens to exist on disk from an earlier run.\n\nFor each field, read the fetch response's `value` and `is_catalog_default` properties. Those are the\nauthoritative signals:\n\n- `value` is `null`, absent, or contains only whitespace → the field is **missing** (needs research).\n- `is_catalog_default` is `true` → the field is **missing** (needs research). The stored content is an\n unmodified Bridge-seeded catalog default, not learned knowledge; the server's guarded write permits\n replacing it while still protecting user-edited or previously learned content.\n- `value` holds any other content → the field is **populated** (already learned).\n\nIf a fetch step warned and returned no usable response at all, treat that field as **missing**.\n\nRecord every populated field as `skipped_existing` and do not spawn a subagent for it. This is what\nmakes a rerun after a partial failure cheap: only the fields that are still empty are researched\nagain.\n\n## Step 2 — Prepare the artifact targets (before any subagent starts)\n\nThe upload steps that follow this task read a fixed file path per field. Stale content at one of\nthose paths would be uploaded as if it were this run's work, so:\n\n1. For every **missing** field, delete any existing file at its draft path. A field whose subagent\n later fails must leave *no* file behind, so the declarative upload has nothing to send.\n2. For every **skipped_existing** field, write the value you just fetched to that field's draft path,\n so the unchanged upload step has a readable file. The upload carries `only_if_null: true`, so the\n server skips the write for learned or user-edited content — and if the value is a still-default\n catalog seed, re-sending the same bytes is a harmless self-copy under the widened\n null-or-default guard.\n\n## Step 3 — Fan out\n\nLaunch one subagent for every missing field, all of them, before awaiting any result. Then await all\nof them and collect each result.\n\nGive each subagent the prompt block for its field from the matrix below. Every prompt block already\ncarries the field name, the draft path, the research responsibilities, the evidence expectations, and\nthe character budget. Pass each block through verbatim, substituting `{docs_dir}` with the resolved\ndocs directory.\n\n### Shared subagent contract\n\nInclude this contract in every subagent prompt, in addition to the field-specific block:\n\n\"\"\"\nYou are researching exactly one configuration field for this repository. Read the local checkout on\ndisk — the actual files, not a summary of them. Cite concrete file paths as evidence for every claim\nyou make; a pattern you cannot point at a file for does not belong in the draft.\n\nWrite your finished draft to the draft path given below, and nothing else. Do not call any\nconfiguration-update tool, do not upload your draft anywhere, and do not ask the human any question:\nanother step owns writing and another step owns asking.\n\n**Length budget — this is a hard limit, measured in characters.**\n\n- Your draft must be at most the maximum character count stated in your field block below, measured\n in **characters** (not tokens, not words, not bytes).\n- When your draft is complete, measure its length in characters.\n- If it exceeds the budget, condense it: remove redundancy, collapse repetitive passages, and shorten\n or drop verbose code examples. Keep every required section — never meet the budget by deleting a\n required section, and never truncate mid-sentence.\n- Re-measure after condensing, and repeat until the draft is within budget.\n- The budget is enforced by the server at upload time, so a draft over the limit is rejected outright\n and the field is simply not learned. Condensing is how the field gets learned at all.\n\nReturn a JSON object:\n`{\"field_name\": \"...\", \"draft_path\": \"...\", \"character_count\": <int>, \"max_character_count\": <int>,\n\"condensed\": <bool>, \"condensation_reason\": \"<why you condensed, or empty>\"}`\n\"\"\"\n\n### Field matrix\n\nTen fields. Each row names the field, its draft path, its character budget, and the research\nresponsibilities to expand into that subagent's prompt block. The responsibilities mirror the\nmatching standalone `learn-*.md` instruction, which remains the fuller reference for that field.\n\n| Field | Draft path | Max characters | Mirrors |\n| --- | --- | --- | --- |\n| `architecture_instructions` | `{docs_dir}/standards/architecture_instructions.md` | 40000 | `learn-architecture.md` |\n| `review_instructions` | `{docs_dir}/standards/review_instructions.md` | 40000 | `learn-review-instructions.md` |\n| `documentation_instructions` | `{docs_dir}/standards/documentation_instructions.md` | 1500 | `learn-documentation-instructions.md` |\n| `unit_testing_instructions` | `{docs_dir}/standards/unit_testing_instructions.md` | 40000 | `learn-unit-testing.md` |\n| `e2e_testing_instructions` | `{docs_dir}/standards/e2e_testing_instructions.md` | 40000 | `learn-e2e-testing.md` |\n| `frontend_correctness_standards` | `{docs_dir}/standards/frontend_correctness_standards.md` | 40000 | `learn-frontend-correctness.md` |\n| `backend_correctness_standards` | `{docs_dir}/standards/backend_correctness_standards.md` | 40000 | `learn-backend-correctness.md` |\n| `template_correctness_standards` | `{docs_dir}/standards/template_correctness_standards.md` | 40000 | `learn-template-correctness.md` |\n| `style_correctness_standards` | `{docs_dir}/standards/style_correctness_standards.md` | 40000 | `learn-style-correctness.md` |\n| `design_principles` | `{docs_dir}/standards/design_principles.md` | 40000 | `learn-design-principles.md` |\n\n#### `architecture_instructions` — max 40000 characters\n\n\"\"\"\nDraft `architecture_instructions` to `{docs_dir}/standards/architecture_instructions.md`, at most\n40000 characters.\n\nExplore the codebase for architectural principles, directory conventions, design patterns, and data\nflow. Examine at least 5 representative files per area and cite file paths for every pattern.\nClassify each pattern's evidence level as `ENFORCED` (violations would be bugs), `CONVENTION`\n(commonly observed, deviations exist), or `ASPIRATIONAL` (intended, not yet consistent).\n\nCover: architectural coding patterns and layer boundaries; design patterns (factories, strategies,\nmiddleware, registries, dependency injection); dependency management; error-handling architecture;\nconfiguration management; tech-stack detection; security/auth architecture; and agent prompting\nconventions. Trace 2-3 complete request paths end to end. Do NOT document testing patterns — skip the\ntests directory entirely.\n\nRequired sections: 1. Core Principles; 2. Layered Architecture; 3. Directory Conventions; 4. Data\nFlow Patterns; 5. Technical Standards; 6. Error Handling & Monitoring; 7. Security & Authentication;\n8. Agent Prompting Conventions; 9. Integration Points; 10. AI Code Generation Guidelines.\n\"\"\"\n\n#### `review_instructions` — max 40000 characters\n\n\"\"\"\nDraft `review_instructions` to `{docs_dir}/standards/review_instructions.md`, at most 40000\ncharacters.\n\nFollow the responsibilities in `learn-review-instructions.md`: research what this repository's code\nreviewers actually enforce, and turn it into instructions the AI code reviewer can apply. Cite file\npaths as evidence for every rule, and preserve that instruction's required sections.\n\"\"\"\n\n#### `documentation_instructions` — max 1500 characters\n\n\"\"\"\nDraft `documentation_instructions` to `{docs_dir}/standards/documentation_instructions.md`, at most\n**1500 characters**.\n\nThis field's limit is far stricter than the platform's general 40000-character ceiling, because the\nserver enforces 1500 characters for this field specifically and the value is inlined verbatim into a\ngenerated plan step.\n\nProduce **a single concise prose paragraph**: no markdown headings, no bullets, no numbered lists, no\nintentional blank lines. The paragraph must cover the implementation-document file naming convention,\nwhere documentation lives, the key content to capture, and when to skip documentation.\n\nBecause 1500 characters is tight, expect to condense. Cut redundancy and examples first; keep all\nfour required topics.\n\"\"\"\n\n#### `unit_testing_instructions` — max 40000 characters\n\n\"\"\"\nDraft `unit_testing_instructions` to `{docs_dir}/standards/unit_testing_instructions.md`, at most\n40000 characters.\n\nFollow the responsibilities in `learn-unit-testing.md`: research this repository's unit-testing\nconventions — framework, layout, fixtures, mocking boundaries, and assertion style — and cite file\npaths for every convention. Preserve that instruction's required sections.\n\"\"\"\n\n#### `e2e_testing_instructions` — max 40000 characters\n\n\"\"\"\nDraft `e2e_testing_instructions` to `{docs_dir}/standards/e2e_testing_instructions.md`, at most 40000\ncharacters.\n\nFollow the responsibilities in `learn-e2e-testing.md`: research this repository's end-to-end testing\nconventions — runner, prerequisites, page/selector patterns, and how tests are executed — and cite\nfile paths for every convention. Preserve that instruction's required sections.\n\"\"\"\n\n#### `frontend_correctness_standards` — max 40000 characters\n\n\"\"\"\nDraft `frontend_correctness_standards` to\n`{docs_dir}/standards/frontend_correctness_standards.md`, at most 40000 characters.\n\nFollow the responsibilities in `learn-frontend-correctness.md`: research the correctness rules that\napply to this repository's frontend code and cite file paths as evidence. Preserve that\ninstruction's required sections. If the repository has no frontend code, say so explicitly rather\nthan inventing standards.\n\"\"\"\n\n#### `backend_correctness_standards` — max 40000 characters\n\n\"\"\"\nDraft `backend_correctness_standards` to `{docs_dir}/standards/backend_correctness_standards.md`, at\nmost 40000 characters.\n\nFollow the responsibilities in `learn-backend-correctness.md`: research the correctness rules that\napply to this repository's backend code and cite file paths as evidence. Preserve that instruction's\nrequired sections.\n\"\"\"\n\n#### `template_correctness_standards` — max 40000 characters\n\n\"\"\"\nDraft `template_correctness_standards` to `{docs_dir}/standards/template_correctness_standards.md`,\nat most 40000 characters.\n\nFollow the responsibilities in `learn-template-correctness.md`: research the correctness rules that\napply to this repository's template files and cite file paths as evidence. Preserve that\ninstruction's required sections. If the repository has no templates, say so explicitly rather than\ninventing standards.\n\"\"\"\n\n#### `style_correctness_standards` — max 40000 characters\n\n\"\"\"\nDraft `style_correctness_standards` to `{docs_dir}/standards/style_correctness_standards.md`, at most\n40000 characters.\n\nFollow the responsibilities in `learn-style-correctness.md`: research the style and formatting rules\nthat apply to this repository's styling files and cite file paths as evidence. Preserve that\ninstruction's required sections. If the repository has no styling files, say so explicitly rather\nthan inventing standards.\n\"\"\"\n\n#### `design_principles` — max 40000 characters\n\n\"\"\"\nDraft `design_principles` to `{docs_dir}/standards/design_principles.md`, at most 40000 characters.\n\nFollow the responsibilities in `learn-design-principles.md`: research this repository's design\ntokens, component inventory, layout patterns, and composition rules, and cite file paths as evidence.\nPreserve that instruction's required sections. If the repository has no user interface, say so\nexplicitly rather than inventing principles.\n\"\"\"\n\n## Step 4 — Verify each result before marking it upload-ready\n\nFor every subagent that returned successfully:\n\n1. Confirm the draft file exists at the field's draft path.\n2. Measure the file's length in characters **yourself**. Do not trust the subagent's reported count —\n an oversized draft handed to the declarative upload is rejected by the server, and the field is\n silently not learned.\n3. If the file is within the field's budget, mark the field `drafted`.\n4. If the file is still over budget, condense it yourself to fit — preserving its required sections —\n then re-measure. If you cannot bring it within budget, delete the draft file and record the field\n as `failed` with the reason, so the upload sends nothing rather than something the server rejects.\n\nFor every subagent that failed, errored, or returned nothing usable: leave the draft path absent,\nrecord the field as `failed` with a short sanitized reason (no credentials, tokens, URLs, or raw\nstack traces), and continue. Do not abandon the other fields and do not fail this task.\n\n## Return\n\nReturn a single JSON object. This task succeeds even when some fields failed — the summary is how a\ngap gets reported, not an exception.\n\n```json\n{\n \"fields\": [\n {\n \"field_name\": \"architecture_instructions\",\n \"status\": \"drafted\",\n \"draft_path\": \"{docs_dir}/standards/architecture_instructions.md\",\n \"character_count\": 18240,\n \"max_character_count\": 40000,\n \"condensed\": false,\n \"condensation_reason\": \"\",\n \"failure_reason\": \"\"\n }\n ],\n \"drafted\": [\"architecture_instructions\"],\n \"skipped_existing\": [],\n \"condensed\": [],\n \"failed\": []\n}\n```\n\nRules for the return value:\n\n- `status` is exactly one of `drafted`, `skipped_existing`, or `failed`.\n- Every one of the ten fields appears exactly once in `fields`.\n- `condensed` is `true` only when the draft was shortened to meet the budget; `condensation_reason`\n explains why whenever `condensed` is `true`.\n- `failure_reason` is populated only for `failed` fields and is sanitized.\n- The four aggregate arrays list the field names in each outcome, so the command's closing summary\n can report them without re-deriving them from prose.\n",
722
- "learn-review-instructions.md": "## Objective\n\nExplore the codebase to identify self-verification patterns, downstream impact analysis techniques, and local validation tooling, then draft `review_instructions` for the project config.\n\n## Instructions\n\n### Phase 1 — Explore Self-Verification Patterns\n\nFocus on how an AI agent working in a code editor (with capabilities to search file contents by text pattern, search by filename pattern, read files, and call MCP tools) can verify its own code changes before requesting human review. Do NOT document test runners or CI/CD — focus on static analysis by reading code and searching for patterns.\n\n1. **Code Correctness Patterns**: Read 3-5 representative modules in `api/routes/` and `api/library/` to identify:\n - Function signature conventions (return types, parameter patterns)\n - Import conventions and layer boundaries (deprecated modules, import restrictions)\n - Return value handling (structured results, tuple unpacking)\n - Auth pattern compliance (required decorators, dependency injections, call order)\n - Naming conventions (files, functions, classes, variables)\n - Error handling patterns (try/except structure, ordering, logging)\n\n2. **Downstream Impact Analysis**: For each technique, demonstrate with a concrete codebase example:\n - Caller discovery (text-pattern search for finding all callers of utility functions)\n - Import graph analysis (finding all files importing from a module)\n - Route registration verification (checking new routes are properly included)\n - Database schema impact (finding queries referencing a given table/column)\n - Model/schema usage (verifying model changes don't break dependents)\n\n3. **Local Validation Tooling**: Discover available MCP tools and validation capabilities:\n - Database MCP tools (schema verification, query validation)\n - Project API MCP tools (config verification, health checks)\n - Hooks and guards (pre-commit hooks, pre-tool hooks)\n - Safety model (read-only vs. mutating operations)\n - Runtime smoke verification capability: Document which tools the executor can use to _run_ code safely (test runners, dbhub MCP, local dev servers, fixture loaders) and whether mutations are permitted against local/ephemeral state. The per-repo `allow_mutating_smoke_ops` flag (on `config_code_repositories`) controls whether the final reviewer is allowed to plan mutating verification steps.\n\n4. **Correctness Standards Integration**: Read files in `{docs_dir}/standards/` matching `*_correctness_standards.md`. Extract key verification checkpoints that can be statically verified.\n\n### Phase 2 — Draft\n\nDraft `review_instructions` with these required sections:\n1. **Self-Verification Checklist** — Concise, scannable checklist with concrete actions and tools.\n2. **Local Code Verification** — Detailed static analysis instructions (function calls, imports, auth, error handling, naming).\n3. **Downstream Effect Analysis** — Finding callers, checking signature compatibility, import tracking, schema impact, route registration.\n4. **Validation Using Local Tooling** — Database validation, project API validation, hooks and guards.\n5. **Correctness Standards Reference** — Distilled checkpoints from loaded standards, or placeholder paths.\n6. **Common AI Agent Mistakes** — Verification-framed guards against duplication, unnecessary abstraction, data leaks, edge cases.\n\nWrite the draft to `{docs_dir}/standards/review_instructions.md`.\n\n## Length Budget\n\nThe platform ceiling for a configuration field is **40,000 characters**, and the server enforces it\nat upload time: a draft over the limit is rejected outright and ``review_instructions`` is simply not learned. Bound\nthe draft here, at write time, rather than discovering the limit through a failed upload.\n\n1. When the draft is complete, measure its length in **characters** — not tokens, not words, not\n bytes.\n2. If it exceeds 40,000 characters, condense it: remove redundancy, collapse repetitive passages, and\n shorten or drop verbose code examples.\n3. Keep every required section listed above. Never meet the budget by deleting a required section,\n and never truncate the draft mid-sentence.\n4. Re-measure after condensing and repeat until the draft is at most 40,000 characters.\n\n## Return\n\nReturn a brief summary of what was learned about the project's self-review and downstream-impact analysis patterns (verification checkpoints, local validation tooling), citing the key files inspected, and confirm the draft was written to `{docs_dir}/standards/review_instructions.md`.\n\nAlso report the draft's final length in characters, the applicable maximum (40,000 characters),\nwhether the draft was condensed to meet that maximum (`condensed`: true/false), and — when it was\ncondensed — the reason it needed condensing.\n",
1010
+ "learn-repository-fan-out.md": "## Objective\n\nResearch all ten learned configuration fields for this repository **in parallel**, one isolated\nsubagent per field, and leave a bounded, upload-ready draft on disk for each field that needs one.\n\nThis step performs research only. It never uploads: the ten declarative `config_field` update steps\nthat follow this task own every write.\n\n## Operating rules (apply to the whole task)\n\n1. **Local research only.** Every field is learned by reading *this checkout* on local disk. Do not\n delegate research to the Bridge API backend or any server-side index — the backend has a vector\n index of the code, not the working tree in front of you.\n2. **One isolated subagent per missing field.** Use the host coding agent's own subagent/task\n mechanism. Each subagent gets exactly one field and does not see the others' work.\n3. **Launch before you await.** Start every missing-field subagent first, then collect results. Never\n spawn a subagent, wait for it to finish, and only then spawn the next one — a sequential\n spawn-and-wait loop defeats the entire purpose of this step.\n4. **Subagents never upload and never talk to the human.** A subagent's only outputs are its draft\n file and its structured result. All human interaction is deferred to the final confirmation step.\n5. **A failed field is not a failed run.** Isolate each failure to its own field, let the other\n subagents finish, and return a successful aggregate result naming the gap.\n\n## Step 1 — Classify each field from the ten preceding fetch results\n\nThe ten `config_field` steps immediately before this task already read the current value of every\nlearned field. Use **those results** — do not re-read the fields and do not infer state from whether\na draft file happens to exist on disk from an earlier run.\n\nFor each field, read the fetch response's `value` and `is_catalog_default` properties. Those are the\nauthoritative signals:\n\n- `value` is `null`, absent, or contains only whitespace → the field is **missing** (needs research).\n- `is_catalog_default` is `true` → the field is **missing** (needs research). The stored content is an\n unmodified Bridge-seeded catalog default, not learned knowledge; the server's guarded write permits\n replacing it while still protecting user-edited or previously learned content.\n- `value` holds any other content → the field is **populated** (already learned).\n\nIf a fetch step warned and returned no usable response at all, treat that field as **missing**.\n\nRecord every populated field as `skipped_existing` and do not spawn a subagent for it. This is what\nmakes a rerun after a partial failure cheap: only the fields that are still empty are researched\nagain.\n\n## Step 2 — Prepare the artifact targets (before any subagent starts)\n\nThe upload steps that follow this task read a fixed file path per field. Stale content at one of\nthose paths would be uploaded as if it were this run's work, so:\n\n1. For every **missing** field, delete any existing file at its draft path. A field whose subagent\n later fails must leave *no* file behind, so the declarative upload has nothing to send.\n2. For every **skipped_existing** field, write the value you just fetched to that field's draft path,\n so the unchanged upload step has a readable file. The upload carries `only_if_null: true`, so the\n server skips the write for learned or user-edited content — and if the value is a still-default\n catalog seed, re-sending the same bytes is a harmless self-copy under the widened\n null-or-default guard.\n\n## Step 3 — Fan out\n\nLaunch one subagent for every missing field, all of them, before awaiting any result. Then await all\nof them and collect each result.\n\nGive each subagent the prompt block for its field from the matrix below. Every prompt block already\ncarries the field name, the draft path, the research responsibilities, the evidence expectations, and\nthe character budget. Pass each block through verbatim, substituting `{docs_dir}` with the resolved\ndocs directory.\n\n### Shared subagent contract\n\nInclude this contract in every subagent prompt, in addition to the field-specific block:\n\n\"\"\"\nYou are researching exactly one configuration field for this repository. Read the local checkout on\ndisk — the actual files, not a summary of them. Cite concrete file paths as evidence for every claim\nyou make; a pattern you cannot point at a file for does not belong in the draft.\n\nWrite your finished draft to the draft path given below, and nothing else. Do not call any\nconfiguration-update tool, do not upload your draft anywhere, and do not ask the human any question:\nanother step owns writing and another step owns asking.\n\n**Length budget — this is a hard limit, measured in characters.**\n\n- Your draft must be at most the maximum character count stated in your field block below, measured\n in **characters** (not tokens, not words, not bytes).\n- When your draft is complete, measure its length in characters.\n- If it exceeds the budget, condense it: remove redundancy, collapse repetitive passages, and shorten\n or drop verbose code examples. Keep every required section — never meet the budget by deleting a\n required section, and never truncate mid-sentence.\n- Re-measure after condensing, and repeat until the draft is within budget.\n- The budget is enforced by the server at upload time, so a draft over the limit is rejected outright\n and the field is simply not learned. Condensing is how the field gets learned at all.\n\nReturn a JSON object:\n`{\"field_name\": \"...\", \"draft_path\": \"...\", \"character_count\": <int>, \"max_character_count\": <int>,\n\"condensed\": <bool>, \"condensation_reason\": \"<why you condensed, or empty>\"}`\n\"\"\"\n\n### Field matrix\n\nTen fields. Each row names the field, its draft path, its character budget, and the research\nresponsibilities to expand into that subagent's prompt block. The responsibilities mirror the\nmatching standalone `learn-*.md` instruction, which remains the fuller reference for that field.\n\n| Field | Draft path | Max characters | Mirrors |\n| --- | --- | --- | --- |\n| `architecture_instructions` | `{docs_dir}/standards/architecture_instructions.md` | 40000 | `learn-architecture.md` |\n| `review_instructions` | `{docs_dir}/standards/review_instructions.md` | 40000 | `learn-review-instructions.md` |\n| `documentation_instructions` | `{docs_dir}/standards/documentation_instructions.md` | 1500 | `learn-documentation-instructions.md` |\n| `unit_testing_instructions` | `{docs_dir}/standards/unit_testing_instructions.md` | 40000 | `learn-unit-testing.md` |\n| `e2e_testing_instructions` | `{docs_dir}/standards/e2e_testing_instructions.md` | 40000 | `learn-e2e-testing.md` |\n| `frontend_correctness_standards` | `{docs_dir}/standards/frontend_correctness_standards.md` | 40000 | `learn-frontend-correctness.md` |\n| `backend_correctness_standards` | `{docs_dir}/standards/backend_correctness_standards.md` | 40000 | `learn-backend-correctness.md` |\n| `template_correctness_standards` | `{docs_dir}/standards/template_correctness_standards.md` | 40000 | `learn-template-correctness.md` |\n| `style_correctness_standards` | `{docs_dir}/standards/style_correctness_standards.md` | 40000 | `learn-style-correctness.md` |\n| `design_principles` | `{docs_dir}/standards/design_principles.md` | 40000 | `learn-design-principles.md` |\n\n#### `architecture_instructions` — max 40000 characters\n\n\"\"\"\nDraft `architecture_instructions` to `{docs_dir}/standards/architecture_instructions.md`, at most\n40000 characters.\n\nExplore the codebase for architectural principles, directory conventions, design patterns, and data\nflow. Examine at least 5 representative files per area and cite file paths for every pattern.\nClassify each pattern's evidence level as `ENFORCED` (violations would be bugs), `CONVENTION`\n(commonly observed, deviations exist), or `ASPIRATIONAL` (intended, not yet consistent).\n\nCover: architectural coding patterns and layer boundaries; design patterns (factories, strategies,\nmiddleware, registries, dependency injection); dependency management; error-handling architecture;\nconfiguration management; tech-stack detection; security/auth architecture; and agent prompting\nconventions. Trace 2-3 complete request paths end to end. Do NOT document testing patterns — skip the\ntests directory entirely.\n\nRequired sections: 1. Core Principles; 2. Layered Architecture; 3. Directory Conventions; 4. Data\nFlow Patterns; 5. Technical Standards; 6. Error Handling & Monitoring; 7. Security & Authentication;\n8. Agent Prompting Conventions; 9. Integration Points; 10. AI Code Generation Guidelines.\n\nIf there is nothing repository-specific to cite, say that no repository evidence was found and\nwrite a short conservative standard; do not invent files, commands, or conventions.\n\"\"\"\n\n#### `review_instructions` — max 40000 characters\n\n\"\"\"\nDraft `review_instructions` to `{docs_dir}/standards/review_instructions.md`, at most 40000\ncharacters.\n\nFollow the responsibilities in `learn-review-instructions.md`: research what this repository's code\nreviewers actually enforce, and turn it into instructions the AI code reviewer can apply. Cite file\npaths as evidence for every rule, and preserve that instruction's required sections.\n\nIf there is nothing repository-specific to cite, say that no repository evidence was found and\nwrite a short conservative standard; do not invent files, commands, or conventions.\n\"\"\"\n\n#### `documentation_instructions` — max 1500 characters\n\n\"\"\"\nDraft `documentation_instructions` to `{docs_dir}/standards/documentation_instructions.md`, at most\n**1500 characters**.\n\nThis field's limit is far stricter than the platform's general 40000-character ceiling, because the\nserver enforces 1500 characters for this field specifically and the value is inlined verbatim into a\ngenerated plan step.\n\nProduce **a single concise prose paragraph**: no markdown headings, no bullets, no numbered lists, no\nintentional blank lines. The paragraph must cover the implementation-document file naming convention,\nwhere documentation lives, the key content to capture, and when to skip documentation.\n\nBecause 1500 characters is tight, expect to condense. Cut redundancy and examples first; keep all\nfour required topics.\n\nIf there is nothing repository-specific to cite, say that no repository evidence was found and\nwrite a short conservative standard; do not invent files, commands, or conventions.\n\"\"\"\n\n#### `unit_testing_instructions` — max 40000 characters\n\n\"\"\"\nDraft `unit_testing_instructions` to `{docs_dir}/standards/unit_testing_instructions.md`, at most\n40000 characters.\n\nFollow the responsibilities in `learn-unit-testing.md`: research this repository's unit-testing\nconventions — framework, layout, fixtures, mocking boundaries, and assertion style — and cite file\npaths for every convention. Preserve that instruction's required sections.\n\nIf there is nothing repository-specific to cite, say that no repository evidence was found and\nwrite a short conservative standard; do not invent files, commands, or conventions.\n\"\"\"\n\n#### `e2e_testing_instructions` — max 40000 characters\n\n\"\"\"\nDraft `e2e_testing_instructions` to `{docs_dir}/standards/e2e_testing_instructions.md`, at most 40000\ncharacters.\n\nFollow the responsibilities in `learn-e2e-testing.md`: research this repository's end-to-end testing\nconventions — runner, prerequisites, page/selector patterns, and how tests are executed — and cite\nfile paths for every convention. Preserve that instruction's required sections.\n\nIf there is nothing repository-specific to cite, say that no repository evidence was found and\nwrite a short conservative standard; do not invent files, commands, or conventions.\n\"\"\"\n\n#### `frontend_correctness_standards` — max 40000 characters\n\n\"\"\"\nDraft `frontend_correctness_standards` to\n`{docs_dir}/standards/frontend_correctness_standards.md`, at most 40000 characters.\n\nFollow the responsibilities in `learn-frontend-correctness.md`: research the correctness rules that\napply to this repository's frontend code and cite file paths as evidence. Preserve that\ninstruction's required sections. If the repository has no frontend code, say so explicitly rather\nthan inventing standards.\n\"\"\"\n\n#### `backend_correctness_standards` — max 40000 characters\n\n\"\"\"\nDraft `backend_correctness_standards` to `{docs_dir}/standards/backend_correctness_standards.md`, at\nmost 40000 characters.\n\nFollow the responsibilities in `learn-backend-correctness.md`: research the correctness rules that\napply to this repository's backend code and cite file paths as evidence. Preserve that instruction's\nrequired sections.\n\nIf there is nothing repository-specific to cite, say that no repository evidence was found and\nwrite a short conservative standard; do not invent files, commands, or conventions.\n\"\"\"\n\n#### `template_correctness_standards` — max 40000 characters\n\n\"\"\"\nDraft `template_correctness_standards` to `{docs_dir}/standards/template_correctness_standards.md`,\nat most 40000 characters.\n\nFollow the responsibilities in `learn-template-correctness.md`: research the correctness rules that\napply to this repository's template files and cite file paths as evidence. Preserve that\ninstruction's required sections. If the repository has no templates, say so explicitly rather than\ninventing standards.\n\"\"\"\n\n#### `style_correctness_standards` — max 40000 characters\n\n\"\"\"\nDraft `style_correctness_standards` to `{docs_dir}/standards/style_correctness_standards.md`, at most\n40000 characters.\n\nFollow the responsibilities in `learn-style-correctness.md`: research the style and formatting rules\nthat apply to this repository's styling files and cite file paths as evidence. Preserve that\ninstruction's required sections. If the repository has no styling files, say so explicitly rather\nthan inventing standards.\n\"\"\"\n\n#### `design_principles` — max 40000 characters\n\n\"\"\"\nDraft `design_principles` to `{docs_dir}/standards/design_principles.md`, at most 40000 characters.\n\nFollow the responsibilities in `learn-design-principles.md`: research this repository's design\ntokens, component inventory, layout patterns, and composition rules, and cite file paths as evidence.\nPreserve that instruction's required sections. If the repository has no user interface, say so\nexplicitly rather than inventing principles.\n\"\"\"\n\n## Step 4 — Verify each result before marking it upload-ready\n\nFor every subagent that returned successfully:\n\n1. Confirm the draft file exists at the field's draft path.\n2. Measure the file's length in characters **yourself**. Do not trust the subagent's reported count —\n an oversized draft handed to the declarative upload is rejected by the server, and the field is\n silently not learned.\n3. If the file is within the field's budget, mark the field `drafted`.\n4. If the file is still over budget, condense it yourself to fit — preserving its required sections —\n then re-measure. If you cannot bring it within budget, delete the draft file and record the field\n as `failed` with the reason, so the upload sends nothing rather than something the server rejects.\n\nFor every subagent that failed, errored, or returned nothing usable: leave the draft path absent,\nrecord the field as `failed` with a short sanitized reason (no credentials, tokens, URLs, or raw\nstack traces), and continue. Do not abandon the other fields and do not fail this task.\n\n## Return\n\nReturn a single JSON object. This task succeeds even when some fields failed — the summary is how a\ngap gets reported, not an exception.\n\n```json\n{\n \"fields\": [\n {\n \"field_name\": \"architecture_instructions\",\n \"status\": \"drafted\",\n \"draft_path\": \"{docs_dir}/standards/architecture_instructions.md\",\n \"character_count\": 18240,\n \"max_character_count\": 40000,\n \"condensed\": false,\n \"condensation_reason\": \"\",\n \"failure_reason\": \"\"\n }\n ],\n \"drafted\": [\"architecture_instructions\"],\n \"skipped_existing\": [],\n \"condensed\": [],\n \"failed\": []\n}\n```\n\nRules for the return value:\n\n- `status` is exactly one of `drafted`, `skipped_existing`, or `failed`.\n- Every one of the ten fields appears exactly once in `fields`.\n- `condensed` is `true` only when the draft was shortened to meet the budget; `condensation_reason`\n explains why whenever `condensed` is `true`.\n- `failure_reason` is populated only for `failed` fields and is sanitized.\n- The four aggregate arrays list the field names in each outcome, so the command's closing summary\n can report them without re-deriving them from prose.\n",
1011
+ "learn-review-instructions.md": "## Objective\n\nExplore the codebase to identify self-verification patterns, downstream impact analysis techniques, and local validation tooling, then draft `review_instructions` for the project config.\n\n## Instructions\n\n### Phase 1 — Explore Self-Verification Patterns\n\nFocus on how an AI agent working in a code editor (with capabilities to search file contents by text pattern, search by filename pattern, read files, and call MCP tools) can verify its own code changes before requesting human review. Do NOT document test runners or CI/CD — focus on static analysis by reading code and searching for patterns.\n\nIf there is nothing repository-specific to cite, say that no repository evidence was found and write a short conservative standard; do not invent files, commands, or conventions.\n\n1. **Code Correctness Patterns**: Read 3-5 representative modules in `api/routes/` and `api/library/` to identify:\n - Function signature conventions (return types, parameter patterns)\n - Import conventions and layer boundaries (deprecated modules, import restrictions)\n - Return value handling (structured results, tuple unpacking)\n - Auth pattern compliance (required decorators, dependency injections, call order)\n - Naming conventions (files, functions, classes, variables)\n - Error handling patterns (try/except structure, ordering, logging)\n\n2. **Downstream Impact Analysis**: For each technique, demonstrate with a concrete codebase example:\n - Caller discovery (text-pattern search for finding all callers of utility functions)\n - Import graph analysis (finding all files importing from a module)\n - Route registration verification (checking new routes are properly included)\n - Database schema impact (finding queries referencing a given table/column)\n - Model/schema usage (verifying model changes don't break dependents)\n\n3. **Local Validation Tooling**: Discover available MCP tools and validation capabilities:\n - Database MCP tools (schema verification, query validation)\n - Project API MCP tools (config verification, health checks)\n - Hooks and guards (pre-commit hooks, pre-tool hooks)\n - Safety model (read-only vs. mutating operations)\n - Runtime smoke verification capability: Document which tools the executor can use to _run_ code safely (test runners, dbhub MCP, local dev servers, fixture loaders) and whether mutations are permitted against local/ephemeral state. The per-repo `allow_mutating_smoke_ops` flag (on `config_code_repositories`) controls whether the final reviewer is allowed to plan mutating verification steps.\n\n4. **Correctness Standards Integration**: Read files in `{docs_dir}/standards/` matching `*_correctness_standards.md`. Extract key verification checkpoints that can be statically verified.\n\n### Phase 2 — Draft\n\nDraft `review_instructions` with these required sections:\n1. **Self-Verification Checklist** — Concise, scannable checklist with concrete actions and tools.\n2. **Local Code Verification** — Detailed static analysis instructions (function calls, imports, auth, error handling, naming).\n3. **Downstream Effect Analysis** — Finding callers, checking signature compatibility, import tracking, schema impact, route registration.\n4. **Validation Using Local Tooling** — Database validation, project API validation, hooks and guards.\n5. **Correctness Standards Reference** — Distilled checkpoints from loaded standards, or placeholder paths.\n6. **Common AI Agent Mistakes** — Verification-framed guards against duplication, unnecessary abstraction, data leaks, edge cases.\n\nWrite the draft to `{docs_dir}/standards/review_instructions.md`.\n\n## Length Budget\n\nThe platform ceiling for a configuration field is **40,000 characters**, and the server enforces it\nat upload time: a draft over the limit is rejected outright and ``review_instructions`` is simply not learned. Bound\nthe draft here, at write time, rather than discovering the limit through a failed upload.\n\n1. When the draft is complete, measure its length in **characters** — not tokens, not words, not\n bytes.\n2. If it exceeds 40,000 characters, condense it: remove redundancy, collapse repetitive passages, and\n shorten or drop verbose code examples.\n3. Keep every required section listed above. Never meet the budget by deleting a required section,\n and never truncate the draft mid-sentence.\n4. Re-measure after condensing and repeat until the draft is at most 40,000 characters.\n\n## Return\n\nReturn a brief summary of what was learned about the project's self-review and downstream-impact analysis patterns (verification checkpoints, local validation tooling), citing the key files inspected, and confirm the draft was written to `{docs_dir}/standards/review_instructions.md`.\n\nAlso report the draft's final length in characters, the applicable maximum (40,000 characters),\nwhether the draft was condensed to meet that maximum (`condensed`: true/false), and — when it was\ncondensed — the reason it needed condensing.\n",
723
1012
  "learn-style-correctness.md": "## Objective\n\nExplore the codebase to identify correctness standards for style files, then draft the corresponding correctness standards document.\n\n## Target Type\n\n- **Type**: `style_correctness`\n- **Field name**: `style_correctness_standards`\n- **Scope**: Style files: CSS, SCSS, SASS, LESS, Styled Components, Tailwind configs.\n\n## Instructions\n\n### Phase 1 — Explore Correctness Patterns\n\nFocus on implementation correctness: how to write code that is correct, idiomatic, and robust within this project's conventions.\n\n1. **File Type Detection**: Search by filename pattern for files matching `**/*.css`, `**/*.scss`, `**/*.sass`, `**/*.less` (excluding `node_modules/`). If very few or no files exist, note this and draft minimal instructions.\n\n2. **Convention Analysis**: Read 3-5 representative style files to identify:\n - Structure patterns (imports, exports, class structure, function ordering)\n - Naming conventions (variables, functions, classes, files)\n - Framework conventions and idioms\n - Best practices followed\n - Issues and inconsistencies\n\n### Phase 2 — Draft\n\nDraft correctness standards as clear, actionable instructions for an AI code generation agent. Cover:\n- Code structure and organization requirements\n- Naming conventions to follow\n- Framework-specific patterns and idioms\n- Security requirements relevant to this code type\n- Performance considerations\n- Common mistakes to avoid\n- Guards against common AI weaknesses: duplicative code, verbose implementations, security vulnerabilities\n\nWrite the draft to `{docs_dir}/standards/style_correctness_standards.md`.\n\n## Length Budget\n\nThe platform ceiling for a configuration field is **40,000 characters**, and the server enforces it\nat upload time: a draft over the limit is rejected outright and ``style_correctness_standards`` is simply not learned. Bound\nthe draft here, at write time, rather than discovering the limit through a failed upload.\n\n1. When the draft is complete, measure its length in **characters** — not tokens, not words, not\n bytes.\n2. If it exceeds 40,000 characters, condense it: remove redundancy, collapse repetitive passages, and\n shorten or drop verbose code examples.\n3. Keep every required section listed above. Never meet the budget by deleting a required section,\n and never truncate the draft mid-sentence.\n4. Re-measure after condensing and repeat until the draft is at most 40,000 characters.\n\n## Return\n\nReturn a brief summary of what was learned about style-file correctness conventions (structure, naming, methodology), citing the key files inspected, and confirm the draft was written to `{docs_dir}/standards/style_correctness_standards.md`.\n\nAlso report the draft's final length in characters, the applicable maximum (40,000 characters),\nwhether the draft was condensed to meet that maximum (`condensed`: true/false), and — when it was\ncondensed — the reason it needed condensing.\n",
724
1013
  "learn-template-correctness.md": "## Objective\n\nExplore the codebase to identify correctness standards for template files, then draft the corresponding correctness standards document.\n\n## Target Type\n\n- **Type**: `template_correctness`\n- **Field name**: `template_correctness_standards`\n- **Scope**: Template files: HTML, Jinja2, Handlebars, EJS, ERB, Blade, Pug, Twig.\n\n## Instructions\n\n### Phase 1 — Explore Correctness Patterns\n\nFocus on implementation correctness: how to write code that is correct, idiomatic, and robust within this project's conventions.\n\n1. **File Type Detection**: Search by filename pattern for files matching `**/*.html`, `**/*.jinja2`, `**/*.j2` in `templates/` and similar directories (excluding `node_modules/`). If very few or no files exist, note this and draft minimal instructions.\n\n2. **Convention Analysis**: Read 3-5 representative template files to identify:\n - Structure patterns (imports, exports, class structure, function ordering)\n - Naming conventions (variables, functions, classes, files)\n - Framework conventions and idioms\n - Best practices followed\n - Issues and inconsistencies\n\n### Phase 2 — Draft\n\nDraft correctness standards as clear, actionable instructions for an AI code generation agent. Cover:\n- Code structure and organization requirements\n- Naming conventions to follow\n- Framework-specific patterns and idioms\n- Security requirements relevant to this code type\n- Performance considerations\n- Common mistakes to avoid\n- Guards against common AI weaknesses: duplicative code, verbose implementations, security vulnerabilities\n\nWrite the draft to `{docs_dir}/standards/template_correctness_standards.md`.\n\n## Length Budget\n\nThe platform ceiling for a configuration field is **40,000 characters**, and the server enforces it\nat upload time: a draft over the limit is rejected outright and ``template_correctness_standards`` is simply not learned. Bound\nthe draft here, at write time, rather than discovering the limit through a failed upload.\n\n1. When the draft is complete, measure its length in **characters** — not tokens, not words, not\n bytes.\n2. If it exceeds 40,000 characters, condense it: remove redundancy, collapse repetitive passages, and\n shorten or drop verbose code examples.\n3. Keep every required section listed above. Never meet the budget by deleting a required section,\n and never truncate the draft mid-sentence.\n4. Re-measure after condensing and repeat until the draft is at most 40,000 characters.\n\n## Return\n\nReturn a brief summary of what was learned about template-file correctness conventions (structure, naming, framework idioms), citing the key files inspected, and confirm the draft was written to `{docs_dir}/standards/template_correctness_standards.md`.\n\nAlso report the draft's final length in characters, the applicable maximum (40,000 characters),\nwhether the draft was condensed to meet that maximum (`condensed`: true/false), and — when it was\ncondensed — the reason it needed condensing.\n",
725
- "learn-unit-testing.md": "## Objective\n\nExplore the codebase to identify the test runner, assertion library, mocking framework, and testing patterns, then draft `unit_testing_instructions` for the project config.\n\n## Instructions\n\n### Phase 1 — Explore Testing Infrastructure\n\n1. **Test Runner and Framework Detection**: Search for test runner configs (`pytest.ini`, `pyproject.toml` `[tool.pytest]` section, `jest.config.*`) and read `package.json` test scripts. Read the `tests/` directory structure.\n\n2. **Testing Patterns**: Read 3-5 representative test files in `tests/pytest/` to identify:\n - Assertion library and style (`assert`, `expect`, custom matchers)\n - Mocking framework (`unittest.mock`, `jest.mock`, `sinon`, etc.)\n - Fixture patterns (setup/teardown)\n - Test organization (by module, feature, layer)\n - Exemplary tests vs. weak tests\n\n3. **How to Run Tests**: Read `pyproject.toml`, `package.json`, and `Makefile` (if present) to determine exact commands for: full suite, single file, by name pattern, with verbose output.\n\n4. **Mocking vs. Fidelity**: Read test helper files in `tests/pytest/helpers/` to document how external APIs are mocked, whether integration tests exist alongside unit tests, and patterns for avoiding third-party calls in tests.\n\n### Phase 2 — Draft\n\nDraft `unit_testing_instructions` as clear, actionable instructions for an AI agent writing unit tests. Cover:\n- How to run tests (exact commands)\n- Which test framework and assertion library to use\n- How to mock external dependencies without calling third parties\n- How to structure test files and test functions\n- What constitutes a thorough test (not just happy path)\n- How to avoid shallow tests that pass but don't verify meaningful behavior\n- Guards against common AI weaknesses: tests that mock the thing being tested, trivially passing assertions, overly complex setup\n\nWrite the draft to `{docs_dir}/standards/unit_testing_instructions.md`.\n\n## Length Budget\n\nThe platform ceiling for a configuration field is **40,000 characters**, and the server enforces it\nat upload time: a draft over the limit is rejected outright and ``unit_testing_instructions`` is simply not learned. Bound\nthe draft here, at write time, rather than discovering the limit through a failed upload.\n\n1. When the draft is complete, measure its length in **characters** — not tokens, not words, not\n bytes.\n2. If it exceeds 40,000 characters, condense it: remove redundancy, collapse repetitive passages, and\n shorten or drop verbose code examples.\n3. Keep every required section listed above. Never meet the budget by deleting a required section,\n and never truncate the draft mid-sentence.\n4. Re-measure after condensing and repeat until the draft is at most 40,000 characters.\n\n## Return\n\nReturn a brief summary of what was learned about the project's unit testing setup (test runner, assertion library, mocking framework, run commands), citing the key files inspected, and confirm the draft was written to `{docs_dir}/standards/unit_testing_instructions.md`.\n\nAlso report the draft's final length in characters, the applicable maximum (40,000 characters),\nwhether the draft was condensed to meet that maximum (`condensed`: true/false), and — when it was\ncondensed — the reason it needed condensing.\n",
726
- "monitor-ci-checks.md": "Monitor CI checks for the most recent commit. The behavior is dispatched on the repo-specific `ci_followup_config` JSON value: `poll_only`, `fix_and_iterate`, or `custom`. Read this entire file once before doing anything, then follow only the matching branch.\n\n> **Warning**: Keep this file behaviorally in sync with `commands/src/check-ci.md` (and its scaffolded copies) to prevent drift (BAPI-462).\n\n**Required-check source**: Both the `poll_only` (Step 5) and `fix_and_iterate` (Step 6) branches gate progression on the *required* check subset, not the aggregate `all_passed` flag. Each check returned by `resolve_ci_checks`/`poll_ci_checks` carries a `required` field (from GitHub Branch Protection, or an LLM classification fallback) — treat `required: false` as non-required (e.g. `pip-audit`) and a missing field or `required: true` as required. This is the tool-provided proxy for the Conductor done-gate's authoritative required-checks set (`mcp_server/src/conductor/done-gate.ts`); do not re-derive required/non-required status in prose.\n\n## Entry state and ownership boundary\n\n**Re-resolve the git context before anything else.** Run `git branch --show-current`\nand `git rev-parse HEAD` when this step begins. In the implement pipeline the pull\nrequest is opened *before* the bounded post-finalization verification phase, and\nthat phase may have pushed a correction on top of the commit the PR was originally\nopened at. The head you monitor must be the branch's current pushed head, not a SHA\ncarried over from PR creation.\n\n**Ownership is split, and the split matters.** The bounded verification phase\n(`verify-plan.md`) owns findings produced by its own local, touched-area commands.\nThis step owns everything subsequently reported by the authoritative `ci` and\n`code_review` gates — failing required checks and requested review changes. Do not\nre-adjudicate the other phase's findings, and do not assume a finding it reported\nhas been fixed unless a pushed commit shows it.\n\n**Do not substitute a broad local run for the authoritative checks.** Running the\nfull local suite here does not establish that CI passed; it duplicates the work the\n`conductor-ci` gate already performs on the pull request, and it is exactly the\nbudget sink this protocol was reordered to avoid. Use the structured CI failure\ndetail from `poll_ci_checks` to target a fix, and scope any local reproduction to\nthe failing area.\n\n**Keep every correction on the existing pull request branch**, and commit *and push*\neach correction before polling resumes. Polling always restarts against the new\npushed head — an unpushed correction is invisible to CI, to review, and to the\nreconciler.\n\nThroughout, report the check and review states you **observed**. Observing that a\ncheck is green is not the same as issuing a verdict: the done-gate evaluation is\nserver-side and the reconciler decides. This step never emits a control signal of\nits own.\n\n## Step 3 — Parse `ci_followup_config`\n\nLook at the response from the immediately preceding `config_field` call (the pipeline step that ran right before this one). The response envelope's `value` field is itself a JSON string and must be parsed again with `JSON.parse` (i.e., the `value` is double-encoded — the outer envelope is JSON, and the inner `value` is a JSON-encoded string of the actual config object).\n\nIf ANY of the following hold, log a warning and use the defaults `{\"strategy\":\"poll_only\",\"max_iterations\":1,\"max_minutes\":10}`:\n\n- The `config_field` response is missing or unavailable (e.g., the step warned-and-continued).\n- The response `value` is `null`.\n- Parsing `value` with `JSON.parse` fails (the persisted text is not valid JSON).\n- The parsed result is not a JSON object.\n- One or more of the required keys (`strategy`, `max_iterations`, `max_minutes`, `instructions`) is missing.\n- `strategy` is not one of `poll_only`, `fix_and_iterate`, or `custom`.\n\n## Step 4 — Dispatch on `strategy`\n\nRead this whole file once and then follow only the matching branch:\n\n- `poll_only` → follow Step 5.\n- `fix_and_iterate` → follow Step 6.\n- `custom` → follow Step 7.\n\nIf `strategy` is unrecognized, log a warning and fall through to Step 5 (`poll_only`).\n\n## Step 5 — `poll_only`\n\nPreserve the baseline polling behavior. The configured `max_minutes` is IGNORED in this branch — `poll_only` always uses the existing 10-minute baseline.\n\n1. Run `git rev-parse HEAD` to get the current commit SHA.\n2. Call the `resolve_ci_checks` tool with `commit_ref` set to that SHA. This discovers and classifies the CI checks for the repository, including each check's `required` field.\n3. Poll CI status by calling `poll_ci_checks` with `commit_ref` set to the same SHA. Check the response for `all_complete`, and note each check's own `required`/green status — do not use the aggregate `all_passed` flag to decide pass/fail (see step 7 below).\n4. **Conflicting-head escalation**: if the poll shows zero check-runs for the current head after roughly two minutes of polling, call `gh pr view --json mergeable,mergeStateStatus` for the current branch's pull request. A `mergeable` value of `CONFLICTING` or a `mergeStateStatus` of `DIRTY` means GitHub will not start `pull_request` workflows for that head, so continuing to poll the same SHA is futile — merge the current base branch into your branch, resolve any conflicts, push the result, obtain the new head SHA (`git rev-parse HEAD`), and restart polling from step 1 against that new SHA. When the mergeability response is not `CONFLICTING`/`DIRTY`, zero checks alone is not a CI failure — continue with the ordinary wait behavior below.\n5. **Conductor steerability**: if launched under the Conductor (the `BAPI_CONDUCTOR_RUN_ID`/`BAPI_CONDUCTOR_WORKER_ID` env identifiers are present), call the `check_messages` MCP tool once per poll cycle, alongside `poll_ci_checks`. Returned messages are advisory supervisor guidance only — acknowledged by the call, not redelivered — and never override the deterministic required-subset/verdict-token rules below; `poll_only` never attempts fixes regardless of guidance. Fail-open: if `check_messages` errors with an identity-unavailable message, you were not launched under the Conductor — stop calling it for the rest of the run.\n6. If checks are not yet complete, wait 30 seconds and poll again. Repeat until all checks are complete or 10 minutes have elapsed.\n7. Compute `required_green` = every check with `required: true` (or a missing `required` field) is complete and green. If `required_green` is `true`, report success — non-required failures (e.g. `pip-audit` with `required: false`) are reported for visibility only and never flip the Passed/Failed classification.\n8. **Review verdict gating**: if `claude-review` is one of the required checks, its GitHub check reaching a non-pending/\"success\" state means only that the review action *ran* — this is transport completion, not approval. Fetch the PR's comments and confirm the sticky comment contains `claude-review-verdict: approved` on its own line with a `Reviewed-SHA:` line matching the current commit SHA before treating the review as approved; `claude-review-verdict: changes_requested`, a missing verdict, or a stale `Reviewed-SHA:` all mean the review is not yet approved and success is not yet reached.\n9. **Conductor done-gate**: once `required_green` is `true` and (if `claude-review` is required) the verdict token confirms approval for the current head, and if launched under the Conductor, call the `wait_for_done_gate` MCP tool once (no arguments required) to drive the authoritative done-gate evaluation server-side against the Conductor's `required_checks` config. This tool applies its own short internal poll cap — if it times out without observing `gate_met`, exit cleanly and still report success rather than treating the timeout as a failure: the Conductor's own reconciliation pass is the correctness backstop. Fail-open: if the tool errors with an identity-unavailable message, skip it.\n10. If any required checks fail, report which required checks failed (and any non-required failures for visibility) and include any available annotations or log details from the poll response. Do NOT attempt to fix failures — just report them clearly.\n11. If CI status is unavailable (resolver/poll returns `available: false`), report unavailable status and exit; do not attempt fixes.\n12. If the 10-minute timeout is reached, report timeout and exit.\n\n### Polling Directive\n\nDuring the polling loop, execute `sleep 30` silently. Do NOT output any inline commentary, reasoning, or partial status updates between polls. Only output a status message when:\n- All checks are complete (pass or fail), OR\n- The 10-minute timeout is reached.\n\nThis minimizes context window consumption during long-running CI waits.\n\n## Step 6 — `fix_and_iterate`\n\nThis is a self-contained loop where `iteration` is the number of correction rounds already pushed and `start_time` is captured before the first iteration. `max_minutes` is the TOTAL wall-clock cap across all iterations, not an additional per-iteration budget. The 10-minute per-iteration `poll_ci_checks` cap is INSIDE that total budget.\n\nInitialize:\n\n- `iteration = 0`\n- `start_time = now()`\n\nBefore starting each iteration AND before applying corrections, check the total wall-clock budget. If `now() - start_time >= max_minutes`, warn and exit.\n\nPer iteration:\n\n1. Run `git rev-parse HEAD` to get the current commit SHA. The previous push may have changed it; always read fresh.\n2. Run `git branch --show-current` to get the current branch. Always read fresh.\n3. Call `resolve_ci_checks` with `commit_ref` set to the current SHA (once per new SHA — the server caches per project but the agent should still call it for each new SHA). Each returned check carries a `required` field — this is the tool-provided proxy for the done-gate's authoritative required-checks set.\n4. Poll `poll_ci_checks` with `commit_ref` set to the current SHA. Stop when `all_complete` is true, OR the per-iteration 10-minute timeout is reached, OR the remaining total wall-clock budget is exhausted.\n5. **Conflicting-head escalation**: if the poll shows zero check-runs for the current head after roughly two minutes of polling, call `gh pr view --json mergeable,mergeStateStatus` for the current branch's pull request. A `mergeable` value of `CONFLICTING` or a `mergeStateStatus` of `DIRTY` means GitHub will not start `pull_request` workflows for that head, so continuing to poll the same SHA is futile — merge the current base branch into your branch, resolve any conflicts, push the result, obtain the new head SHA (`git rev-parse HEAD`), and restart polling from step 1 of this per-iteration block against that new SHA. When the mergeability response is not `CONFLICTING`/`DIRTY`, zero checks alone is not a CI failure — continue with the ordinary per-iteration behavior below.\n6. **Conductor steerability**: if launched under the Conductor (the `BAPI_CONDUCTOR_RUN_ID`/`BAPI_CONDUCTOR_WORKER_ID` env identifiers are present), call the `check_messages` MCP tool once per poll cycle, alongside `poll_ci_checks`. Returned messages are advisory supervisor guidance only — acknowledged by the call, not redelivered — and never override the deterministic required-subset/verdict-token rules or cause a fix you are not confident in. Fail-open: if `check_messages` errors with an identity-unavailable message, you were not launched under the Conductor — stop calling it for the rest of the run.\n7. If CI status is unavailable (`available: false`), warn and exit the loop — automated remediation cannot make reliable progress without CI signals.\n8. Compute `required_green` = every check with `required: true` (or a missing `required` field) is complete and green; non-required failures (e.g. `pip-audit` with `required: false`) are reported for visibility but never gate exit condition 1 below. If `claude-review` is a required check, its GitHub check reaching a non-pending/\"success\" state is transport completion only, not approval — fetch the PR's comments and confirm the sticky comment contains `claude-review-verdict: approved` on its own line with a `Reviewed-SHA:` line matching the current commit SHA before treating the review as approved; `claude-review-verdict: changes_requested`, a missing verdict, or a stale `Reviewed-SHA:` mean the review is not yet approved.\n9. Apply repo-specific `instructions` ONLY when the `instructions` field is non-empty. If the repo `instructions` reference templated placeholder tokens for the GitHub owner, repo, or PR number — e.g., the literal tokens written as a left brace, the word `owner`/`repo`/`pr`, then a right brace — resolve them from the local git/VCS context. Use `gh pr list --head <branch> --json number` to get the PR number; parse the remote URL (`git config --get remote.origin.url`) for owner/repo. If `instructions` is empty, skip repo-specific signal gathering and use only structured CI failure information.\n\n10. Evaluate exit conditions in this order:\n 1. `required_green` is true AND (if `claude-review` is required) the verdict token confirms approval for the current head AND any repo-specific exit criteria from `instructions` are met → success. If there are no repo-specific exit criteria, `required_green` (plus verdict-token approval when `claude-review` is required) alone satisfies the success condition. On success, if launched under the Conductor, call the `wait_for_done_gate` MCP tool once before returning (no arguments required) to drive the authoritative done-gate evaluation server-side; this tool applies its own short internal poll cap and, if it times out without observing `gate_met`, exit cleanly and still return success — the Conductor's own reconciliation pass is the correctness backstop, not this call. Fail-open: if the tool errors with an identity-unavailable message, skip it. Then return.\n 2. `iteration >= max_iterations` → warn and exit (iteration cap reached).\n 3. Total elapsed wall-clock time `>= max_minutes` → warn and exit (total wall-clock cap reached).\n 4. After attempting corrections, `git status --porcelain` is empty → warn and exit (nothing to commit; avoids infinite loop on stuck failures).\n\n11. Apply corrections ONLY for failing **required** checks — skip failures on non-required checks (e.g. `pip-audit` with `required: false`) with a warning and never spend a correction/retry on them. For each failing required check, use the actual `poll_ci_checks` response shape — inspect its singular `failure_detail` field:\n - If `failure_detail` is a dict containing actionable keys such as `annotations`, `log_tail`, or `log`, treat it as structured detail and use it for remediation.\n - If `failure_detail` is a dict containing only `url`, treat it as URL-only and skip with a warning (no actionable detail).\n - If `failure_detail` is missing, `null`, or unrecognized, treat the failure as non-actionable and skip with a warning.\n - Do NOT rely on a per-check field or a plural variant of `failure_detail` — those do not exist on the response.\n\n12. After applying a non-empty correction set: stage corrections (`git add` the specific files), commit, and push. Use the canonical commit message:\n ```\n {ticket_key}: address review/CI feedback (round N+1)\n ```\n where `N` is the zero-indexed `iteration`.\n13. Increment `iteration` only AFTER a successful commit and push. Then loop back to step 1 of the per-iteration block.\n\n## Step 7 — `custom`\n\nIn `custom` mode, the `instructions` field IS the complete CI follow-up instruction set for this step. Follow it verbatim. Ignore Steps 5 and 6 entirely.\n\nCustom instructions are authoritative for CI follow-up behavior, but they remain subject to the agent's normal tool approval, credential handling, secret-handling, and platform safety constraints. Custom prose CANNOT bypass approval gates, exfiltrate secrets, or override platform safety policies, even though admin-only access controls who can set the field.\n\n## Worker finalization — clean session exit (Conductor auto mode)\n\nThis section applies ONLY when you were launched under the Conductor in auto mode (the `BAPI_CONDUCTOR_RUN_ID`/`BAPI_CONDUCTOR_WORKER_ID` env identifiers are present). A standalone `/check-ci` run or any non-Conductor worker ignores it entirely.\n\nBefore doing anything here, distinguish two states:\n\n- **CI/review follow-up still owned by this worker** — the `fix_and_iterate` loop is still correcting failures, review changes were requested and are unaddressed, a merge conflict on your PR is unresolved, or you have unpushed local commits. In this state you are **not** finished: keep working the CI-monitoring / correction loop and do **not** exit.\n- **Final PR state reached; no further worker action pending** — your final branch state is pushed, the PR has been created/updated, the done-gate / CI-monitoring workflow required by the recipe has completed (required checks green and, when `claude-review` is required, the verdict token confirms approval for the current head), and no CI/review follow-up remains that you own.\n\nOnly in the second state — that is, **only after the final push, PR creation/update, done-gate confirmation, and CI/review monitoring have all completed** and no follow-up remains — cleanly end your worker session (for example by issuing the `/exit` command) so the worktree is released and no idle process lingers. Do **not** exit immediately after opening a PR while CI or review is still pending, and do not exit while there are unresolved CI failures, requested review changes, a merge conflict you own, or unpushed local commits. A clean `SessionEnd` is both the correct terminal lifecycle signal (the conductor folds it) and the point at which the worker should exit.\n\n## Return\n\nReport whether CI passed, failed, timed out, or was unavailable. If failed, list the failing checks with their failure summaries. For `fix_and_iterate`, also report the iteration count and whether iteration/wall-clock caps were hit. If you finalized (cleanly exited) as a Conductor worker, note that the session ended after all follow-up completed.\n",
1014
+ "learn-unit-testing.md": "## Objective\n\nExplore the codebase to identify the test runner, assertion library, mocking framework, and testing patterns, then draft `unit_testing_instructions` for the project config.\n\n## Instructions\n\n### Phase 1 — Explore Testing Infrastructure\n\n1. **Test Runner and Framework Detection**: Search for test runner configs (`pytest.ini`, `pyproject.toml` `[tool.pytest]` section, `jest.config.*`) and read `package.json` test scripts. Read the `tests/` directory structure.\n\n2. **Testing Patterns**: Read 3-5 representative test files in `tests/pytest/` to identify:\n - Assertion library and style (`assert`, `expect`, custom matchers)\n - Mocking framework (`unittest.mock`, `jest.mock`, `sinon`, etc.)\n - Fixture patterns (setup/teardown)\n - Test organization (by module, feature, layer)\n - Exemplary tests vs. weak tests\n\n3. **How to Run Tests**: Read `pyproject.toml`, `package.json`, and `Makefile` (if present) to determine exact commands for: full suite, single file, by name pattern, with verbose output.\n\n4. **Mocking vs. Fidelity**: Read test helper files in `tests/pytest/helpers/` to document how external APIs are mocked, whether integration tests exist alongside unit tests, and patterns for avoiding third-party calls in tests.\n\n### Phase 2 — Draft\n\nIf there is nothing repository-specific to cite, say that no repository evidence was found and write a short conservative standard; do not invent files, commands, or conventions.\n\nDraft `unit_testing_instructions` as clear, actionable instructions for an AI agent writing unit tests. Cover:\n- How to run tests (exact commands)\n- Which test framework and assertion library to use\n- How to mock external dependencies without calling third parties\n- How to structure test files and test functions\n- What constitutes a thorough test (not just happy path)\n- How to avoid shallow tests that pass but don't verify meaningful behavior\n- Guards against common AI weaknesses: tests that mock the thing being tested, trivially passing assertions, overly complex setup\n\nWrite the draft to `{docs_dir}/standards/unit_testing_instructions.md`.\n\n## Length Budget\n\nThe platform ceiling for a configuration field is **40,000 characters**, and the server enforces it\nat upload time: a draft over the limit is rejected outright and ``unit_testing_instructions`` is simply not learned. Bound\nthe draft here, at write time, rather than discovering the limit through a failed upload.\n\n1. When the draft is complete, measure its length in **characters** — not tokens, not words, not\n bytes.\n2. If it exceeds 40,000 characters, condense it: remove redundancy, collapse repetitive passages, and\n shorten or drop verbose code examples.\n3. Keep every required section listed above. Never meet the budget by deleting a required section,\n and never truncate the draft mid-sentence.\n4. Re-measure after condensing and repeat until the draft is at most 40,000 characters.\n\n## Return\n\nReturn a brief summary of what was learned about the project's unit testing setup (test runner, assertion library, mocking framework, run commands), citing the key files inspected, and confirm the draft was written to `{docs_dir}/standards/unit_testing_instructions.md`.\n\nAlso report the draft's final length in characters, the applicable maximum (40,000 characters),\nwhether the draft was condensed to meet that maximum (`condensed`: true/false), and — when it was\ncondensed — the reason it needed condensing.\n",
1015
+ "monitor-ci-checks.md": "Monitor CI checks for the most recent commit. The behavior is dispatched on the repo-specific `ci_followup_config` JSON value: `poll_only`, `fix_and_iterate`, or `custom`. Read this entire file once before doing anything, then follow only the matching branch.\n\n> **Warning**: Keep this file behaviorally in sync with `commands/src/check-ci.md` (and its scaffolded copies) to prevent drift (BAPI-462).\n\n**Required-check source**: Both the `poll_only` (Step 5) and `fix_and_iterate` (Step 6) branches gate progression on the *required* check subset, not the aggregate `all_passed` flag. Each check returned by `resolve_ci_checks`/`poll_ci_checks` carries a `required` field (from GitHub Branch Protection, or an LLM classification fallback) — treat `required: false` as non-required (e.g. `pip-audit`) and a missing field or `required: true` as required. This is the tool-provided proxy for the Conductor done-gate's authoritative required-checks set (`mcp_server/src/conductor/done-gate.ts`); do not re-derive required/non-required status in prose.\n\n## Entry state and ownership boundary\n\n**Re-resolve the git context before anything else.** Run `git branch --show-current`\nand `git rev-parse HEAD` when this step begins. In the implement pipeline the pull\nrequest is opened *before* the bounded post-finalization verification phase, and\nthat phase may have pushed a correction on top of the commit the PR was originally\nopened at. The head you monitor must be the branch's current pushed head, not a SHA\ncarried over from PR creation.\n\n**Ownership is split, and the split matters.** The bounded verification phase\n(`verify-plan.md`) owns findings produced by its own local, touched-area commands.\nThis step owns everything subsequently reported by the authoritative `ci` and\n`code_review` gates — failing required checks and requested review changes. Do not\nre-adjudicate the other phase's findings, and do not assume a finding it reported\nhas been fixed unless a pushed commit shows it.\n\n**Do not substitute a broad local run for the authoritative checks.** Running the\nfull local suite here does not establish that CI passed; it duplicates the work the\n`conductor-ci` gate already performs on the pull request, and it is exactly the\nbudget sink this protocol was reordered to avoid. Use the structured CI failure\ndetail from `poll_ci_checks` to target a fix, and scope any local reproduction to\nthe failing area.\n\n**Keep every correction on the existing pull request branch**, and commit *and push*\neach correction before polling resumes. Polling always restarts against the new\npushed head — an unpushed correction is invisible to CI, to review, and to the\nreconciler.\n\nThroughout, report the check and review states you **observed**. Observing that a\ncheck is green is not the same as issuing a verdict: the done-gate evaluation is\nserver-side and the reconciler decides. This step never emits a control signal of\nits own.\n\n## Step 3 — Parse `ci_followup_config`\n\nLook at the response from the immediately preceding `config_field` call (the pipeline step that ran right before this one). The response envelope's `value` field is itself a JSON string and must be parsed again with `JSON.parse` (i.e., the `value` is double-encoded — the outer envelope is JSON, and the inner `value` is a JSON-encoded string of the actual config object).\n\nIf ANY of the following hold, log a warning and use the defaults `{\"strategy\":\"poll_only\",\"max_iterations\":1,\"max_minutes\":10}`:\n\n- The `config_field` response is missing or unavailable (e.g., the step warned-and-continued).\n- The response `value` is `null`.\n- Parsing `value` with `JSON.parse` fails (the persisted text is not valid JSON).\n- The parsed result is not a JSON object.\n- One or more of the required keys (`strategy`, `max_iterations`, `max_minutes`, `instructions`) is missing.\n- `strategy` is not one of `poll_only`, `fix_and_iterate`, or `custom`.\n\n## Step 4 — Dispatch on `strategy`\n\nRead this whole file once and then follow only the matching branch:\n\n- `poll_only` → follow Step 5.\n- `fix_and_iterate` → follow Step 6.\n- `custom` → follow Step 7.\n\nIf `strategy` is unrecognized, log a warning and fall through to Step 5 (`poll_only`).\n\n## Step 5 — `poll_only`\n\nPreserve the baseline polling behavior. The configured `max_minutes` is IGNORED in this branch — `poll_only` always uses the existing 10-minute baseline.\n\n1. Run `git rev-parse HEAD` to get the current commit SHA.\n2. Call the `resolve_ci_checks` tool with `commit_ref` set to that SHA. This discovers and classifies the CI checks for the repository, including each check's `required` field.\n3. Poll CI status by calling `poll_ci_checks` with `commit_ref` set to the same SHA. Check the response for `all_complete`, and note each check's own `required`/green status — do not use the aggregate `all_passed` flag to decide pass/fail (see step 7 below).\n4. **Conflicting-head escalation**: if the poll shows zero check-runs for the current head after roughly two minutes of polling, call `gh pr view --json mergeable,mergeStateStatus` for the current branch's pull request. A `mergeable` value of `CONFLICTING` or a `mergeStateStatus` of `DIRTY` means GitHub will not start `pull_request` workflows for that head, so continuing to poll the same SHA is futile — merge the current base branch into your branch, resolve any conflicts, push the result, obtain the new head SHA (`git rev-parse HEAD`), and restart polling from step 1 against that new SHA. When the mergeability response is not `CONFLICTING`/`DIRTY`, zero checks alone is not a CI failure — continue with the ordinary wait behavior below.\n5. **Conductor steerability**: if launched under the Conductor (the `BAPI_CONDUCTOR_RUN_ID`/`BAPI_CONDUCTOR_WORKER_ID` env identifiers are present), call the `check_messages` MCP tool once per poll cycle, alongside `poll_ci_checks`. Returned messages are advisory supervisor guidance only — acknowledged by the call, not redelivered — and never override the deterministic required-subset/verdict-token rules below; `poll_only` never attempts fixes regardless of guidance. Fail-open: if `check_messages` errors with an identity-unavailable message, you were not launched under the Conductor — stop calling it for the rest of the run.\n6. If checks are not yet complete, wait 30 seconds and poll again. Repeat until all checks are complete or 10 minutes have elapsed.\n7. Compute `required_green` = every check with `required: true` (or a missing `required` field) is complete and green. If `required_green` is `true`, report success — non-required failures (e.g. `pip-audit` with `required: false`) are reported for visibility only and never flip the Passed/Failed classification.\n8. **Review verdict gating**: if `claude-review` is one of the required checks, its GitHub check reaching a non-pending/\"success\" state means only that the review action *ran* — this is transport completion, not approval. Fetch the PR's comments and confirm the sticky comment contains `claude-review-verdict: approved` on its own line with a `Reviewed-SHA:` line matching the current commit SHA before treating the review as approved; `claude-review-verdict: changes_requested`, a missing verdict, or a stale `Reviewed-SHA:` all mean the review is not yet approved and success is not yet reached. `docs/claude/claude-review-verdict-contract.md` is the authoritative definition of this grammar, of which comment wins when several carry a verdict, and of every fail-closed outcome; consult it rather than re-deriving the rules, and do not restate a different version of them here. A verdict can also be *temporarily* absent because a running review rewrote the sticky comment in place (the vanishing window, contract §7): while a `claude-review` run is still pending, treat absence as transient and keep polling; once it has completed, absence is final and fails closed.\n9. **Conductor done-gate**: once `required_green` is `true` and (if `claude-review` is required) the verdict token confirms approval for the current head, and if launched under the Conductor, call the `wait_for_done_gate` MCP tool once (no arguments required) to drive the authoritative done-gate evaluation server-side against the Conductor's `required_checks` config. This tool applies its own short internal poll cap — if it times out without observing `gate_met`, exit cleanly and still report success rather than treating the timeout as a failure: the Conductor's own reconciliation pass is the correctness backstop. Fail-open: if the tool errors with an identity-unavailable message, skip it.\n10. If any required checks fail, report which required checks failed (and any non-required failures for visibility) and include any available annotations or log details from the poll response. Do NOT attempt to fix failures — just report them clearly.\n11. If CI status is unavailable (resolver/poll returns `available: false`), report unavailable status and exit; do not attempt fixes.\n12. If the 10-minute timeout is reached, report timeout and exit.\n\n### Polling Directive\n\nDuring the polling loop, execute `sleep 30` silently. Do NOT output any inline commentary, reasoning, or partial status updates between polls. Only output a status message when:\n- All checks are complete (pass or fail), OR\n- The 10-minute timeout is reached.\n\nThis minimizes context window consumption during long-running CI waits.\n\n## Step 6 — `fix_and_iterate`\n\nThis is a self-contained loop where `iteration` is the number of correction rounds already pushed and `start_time` is captured before the first iteration. `max_minutes` is the TOTAL wall-clock cap across all iterations, not an additional per-iteration budget. The 10-minute per-iteration `poll_ci_checks` cap is INSIDE that total budget.\n\nInitialize:\n\n- `iteration = 0`\n- `start_time = now()`\n\nBefore starting each iteration AND before applying corrections, check the total wall-clock budget. If `now() - start_time >= max_minutes`, warn and exit.\n\nPer iteration:\n\n1. Run `git rev-parse HEAD` to get the current commit SHA. The previous push may have changed it; always read fresh.\n2. Run `git branch --show-current` to get the current branch. Always read fresh.\n3. Call `resolve_ci_checks` with `commit_ref` set to the current SHA (once per new SHA — the server caches per project but the agent should still call it for each new SHA). Each returned check carries a `required` field — this is the tool-provided proxy for the done-gate's authoritative required-checks set.\n4. Poll `poll_ci_checks` with `commit_ref` set to the current SHA. Stop when `all_complete` is true, OR the per-iteration 10-minute timeout is reached, OR the remaining total wall-clock budget is exhausted.\n5. **Conflicting-head escalation**: if the poll shows zero check-runs for the current head after roughly two minutes of polling, call `gh pr view --json mergeable,mergeStateStatus` for the current branch's pull request. A `mergeable` value of `CONFLICTING` or a `mergeStateStatus` of `DIRTY` means GitHub will not start `pull_request` workflows for that head, so continuing to poll the same SHA is futile — merge the current base branch into your branch, resolve any conflicts, push the result, obtain the new head SHA (`git rev-parse HEAD`), and restart polling from step 1 of this per-iteration block against that new SHA. When the mergeability response is not `CONFLICTING`/`DIRTY`, zero checks alone is not a CI failure — continue with the ordinary per-iteration behavior below.\n6. **Conductor steerability**: if launched under the Conductor (the `BAPI_CONDUCTOR_RUN_ID`/`BAPI_CONDUCTOR_WORKER_ID` env identifiers are present), call the `check_messages` MCP tool once per poll cycle, alongside `poll_ci_checks`. Returned messages are advisory supervisor guidance only — acknowledged by the call, not redelivered — and never override the deterministic required-subset/verdict-token rules or cause a fix you are not confident in. Fail-open: if `check_messages` errors with an identity-unavailable message, you were not launched under the Conductor — stop calling it for the rest of the run.\n7. If CI status is unavailable (`available: false`), warn and exit the loop — automated remediation cannot make reliable progress without CI signals.\n8. Compute `required_green` = every check with `required: true` (or a missing `required` field) is complete and green; non-required failures (e.g. `pip-audit` with `required: false`) are reported for visibility but never gate exit condition 1 below. If `claude-review` is a required check, its GitHub check reaching a non-pending/\"success\" state is transport completion only, not approval — fetch the PR's comments and confirm the sticky comment contains `claude-review-verdict: approved` on its own line with a `Reviewed-SHA:` line matching the current commit SHA before treating the review as approved; `claude-review-verdict: changes_requested`, a missing verdict, or a stale `Reviewed-SHA:` mean the review is not yet approved. `docs/claude/claude-review-verdict-contract.md` is the authoritative definition of this grammar, of which comment wins when several carry a verdict, and of every fail-closed outcome; consult it rather than re-deriving the rules, and do not restate a different version of them here. A verdict can also be *temporarily* absent because a running review rewrote the sticky comment in place (the vanishing window, contract §7): while a `claude-review` run is still pending, treat absence as transient and keep polling; once it has completed, absence is final and fails closed.\n9. Apply repo-specific `instructions` ONLY when the `instructions` field is non-empty. If the repo `instructions` reference templated placeholder tokens for the GitHub owner, repo, or PR number — e.g., the literal tokens written as a left brace, the word `owner`/`repo`/`pr`, then a right brace — resolve them from the local git/VCS context. Use `gh pr list --head <branch> --json number` to get the PR number; parse the remote URL (`git config --get remote.origin.url`) for owner/repo. If `instructions` is empty, skip repo-specific signal gathering and use only structured CI failure information.\n\n10. Evaluate exit conditions in this order:\n 1. `required_green` is true AND (if `claude-review` is required) the verdict token confirms approval for the current head AND any repo-specific exit criteria from `instructions` are met → success. If there are no repo-specific exit criteria, `required_green` (plus verdict-token approval when `claude-review` is required) alone satisfies the success condition. On success, if launched under the Conductor, call the `wait_for_done_gate` MCP tool once before returning (no arguments required) to drive the authoritative done-gate evaluation server-side; this tool applies its own short internal poll cap and, if it times out without observing `gate_met`, exit cleanly and still return success — the Conductor's own reconciliation pass is the correctness backstop, not this call. Fail-open: if the tool errors with an identity-unavailable message, skip it. Then return.\n 2. `iteration >= max_iterations` → warn and exit (iteration cap reached).\n 3. Total elapsed wall-clock time `>= max_minutes` → warn and exit (total wall-clock cap reached).\n 4. After attempting corrections, `git status --porcelain` is empty → warn and exit (nothing to commit; avoids infinite loop on stuck failures).\n\n11. Apply corrections ONLY for failing **required** checks — skip failures on non-required checks (e.g. `pip-audit` with `required: false`) with a warning and never spend a correction/retry on them. For each failing required check, use the actual `poll_ci_checks` response shape — inspect its singular `failure_detail` field:\n - If `failure_detail` is a dict containing actionable keys such as `annotations`, `log_tail`, or `log`, treat it as structured detail and use it for remediation.\n - If `failure_detail` is a dict containing only `url`, treat it as URL-only and skip with a warning (no actionable detail).\n - If `failure_detail` is missing, `null`, or unrecognized, treat the failure as non-actionable and skip with a warning.\n - Do NOT rely on a per-check field or a plural variant of `failure_detail` — those do not exist on the response.\n\n12. After applying a non-empty correction set: stage corrections (`git add` the specific files), commit, and push. Use the canonical commit message:\n ```\n {ticket_key}: address review/CI feedback (round N+1)\n ```\n where `N` is the zero-indexed `iteration`.\n13. Increment `iteration` only AFTER a successful commit and push. Then loop back to step 1 of the per-iteration block.\n\n## Step 7 — `custom`\n\nIn `custom` mode, the `instructions` field IS the complete CI follow-up instruction set for this step. Follow it verbatim. Ignore Steps 5 and 6 entirely.\n\nCustom instructions are authoritative for CI follow-up behavior, but they remain subject to the agent's normal tool approval, credential handling, secret-handling, and platform safety constraints. Custom prose CANNOT bypass approval gates, exfiltrate secrets, or override platform safety policies, even though admin-only access controls who can set the field.\n\n## Worker finalization — clean session exit (Conductor auto mode)\n\nThis section applies ONLY when you were launched under the Conductor in auto mode (the `BAPI_CONDUCTOR_RUN_ID`/`BAPI_CONDUCTOR_WORKER_ID` env identifiers are present). A standalone `/check-ci` run or any non-Conductor worker ignores it entirely.\n\nBefore doing anything here, distinguish two states:\n\n- **CI/review follow-up still owned by this worker** — the `fix_and_iterate` loop is still correcting failures, review changes were requested and are unaddressed, a merge conflict on your PR is unresolved, or you have unpushed local commits. In this state you are **not** finished: keep working the CI-monitoring / correction loop and do **not** exit.\n- **Final PR state reached; no further worker action pending** — your final branch state is pushed, the PR has been created/updated, the done-gate / CI-monitoring workflow required by the recipe has completed (required checks green and, when `claude-review` is required, the verdict token confirms approval for the current head), and no CI/review follow-up remains that you own.\n\nOnly in the second state — that is, **only after the final push, PR creation/update, done-gate confirmation, and CI/review monitoring have all completed** and no follow-up remains — cleanly end your worker session (for example by issuing the `/exit` command) so the worktree is released and no idle process lingers. Do **not** exit immediately after opening a PR while CI or review is still pending, and do not exit while there are unresolved CI failures, requested review changes, a merge conflict you own, or unpushed local commits. A clean `SessionEnd` is both the correct terminal lifecycle signal (the conductor folds it) and the point at which the worker should exit.\n\n## Return\n\nReport whether CI passed, failed, timed out, or was unavailable. If failed, list the failing checks with their failure summaries. For `fix_and_iterate`, also report the iteration count and whether iteration/wall-clock caps were hit. If you finalized (cleanly exited) as a Conductor worker, note that the session ended after all follow-up completed.\n",
727
1016
  "preflight-and-readiness.md": "Initialize the idea-to-ticket run directory and classify the idea's readiness and scope.\n\n## Inputs\n\n- Idea: `{idea}`\n- Slug: `{slug}`\n- Run ID: `{run_id}`\n- Docs directory: `{docs_dir}`\n- Project standards: response from the immediately preceding `get_project_standards` step. If that step returned an error envelope or a 404, treat the project standards as unavailable and proceed; do not halt.\n\n## Instructions\n\n1. Create the run directory:\n ```\n mkdir -p {docs_dir}/idea-to-ticket/{slug}-{run_id}\n ```\n Every artifact produced by this pipeline run lives under this run directory. No Jira mutation may occur in any later step until `run-manifest.json` has been written to this directory.\n\n2. Classify the idea on two independent axes:\n\n **Readiness** (one of):\n - `ready_to_draft` — the idea is concrete enough that a clear ticket draft can be produced.\n - `needs_clarification` — the idea is reasonable but missing key answers; clarifying questions must be raised in `open-questions.md` later.\n - `research_first` — drafting is blocked on external/codebase research; deep or narrow research must come first.\n - `too_vague_to_ticket` — the idea is not actionable yet; do not produce a ticket.\n\n **Scope** (one of):\n - `task` — a single Jira Task (default when ambiguous).\n - `spike` — a single Jira Spike for primarily discovery/research work.\n - `epic_candidate` — the idea decomposes into a Jira Epic plus multiple child tickets.\n\n3. Halt locally if readiness is `too_vague_to_ticket`. Write the manifest anyway (see step 4) so the local artifacts record the halt; then stop without continuing the rest of the pipeline. Do not attempt any Jira mutation.\n\n4. Write `run-manifest.json` to `{docs_dir}/idea-to-ticket/{slug}-{run_id}/run-manifest.json`. Required fields:\n - `idea` — the original `{idea}` text.\n - `slug` — `{slug}`.\n - `run_id` — `{run_id}`.\n - `run_dir` — `{docs_dir}/idea-to-ticket/{slug}-{run_id}/`.\n - `readiness` — one of the four readiness values above.\n - `scope` — one of the three scope values above.\n - `project_standards_available` — `true` if `get_project_standards` returned a usable result, `false` otherwise.\n - `idempotency_label` — `bapi-idea-to-ticket-{run_id}` (per-run label; lets downstream steps resume THIS run by label).\n - `stable_label` — `bapi-idea-hash-{idea_hash}` (stable across runs of the same idea; lets the duplicate-detection step catch a PRIOR run of the same idea by label, not just fuzzy text).\n - `created_at` — ISO 8601 timestamp.\n\n5. The manifest is the resumability artifact for the whole run. Do not include secrets or raw credentials. Keep the file under a few KB.\n\n## Return\n\nConfirm the run directory and `run-manifest.json` were created, and report the classified `readiness` and `scope`. If readiness is `too_vague_to_ticket`, also report that the pipeline must stop without Jira mutation.\n",
1017
+ "render-ticket-manifest.md": "Render every entry of an approved ticket manifest into a drafted body, one `jira-ticket-writer` invocation per entry, without re-deciding the split.\n\nThis stage sits between **decomposition** (which decided the split once and froze\nit) and **creation** (`upload-epic-hierarchy.md` for an epic, ordinary\n`create_ticket` calls for siblings). It owns exactly one job: turning frozen\nmanifest entries into drafted bodies. It decides nothing.\n\n## Inputs\n\n- **The approved manifest** — the ordered plan the decomposition pass froze.\n On the `idea-to-ticket` path this is `decomposition-plan.json` in the run\n directory, produced by `decompose-epic-candidate.md`. On the `plan-epic` path\n it is the sub-task list in `epic-plan.md`, produced by `decompose-epic.md`. On\n a client-driven path (`/explore-ticket` Stage 9) it is the approved outline\n shown at the gate.\n- **The approval state** for that manifest. Whether approval is an explicit\n affirmative or a pipeline auto-approval variable is the calling surface's own\n rule; this stage only needs to know it resolved.\n- **The research and framing artifacts** each entry's writer invocation needs —\n the same set the standalone drafting path passes.\n\n## Preflight — fail closed, before any body is rendered\n\nCheck all of the following first. Each one is a **hard failure** that stops the\nflow before a single writer invocation and long before any Jira mutation. None\nof them degrades to a partial run:\n\n1. **Approval unresolved.** The manifest's approval gate has not resolved, or\n resolved as declined. Nothing renders and nothing is created.\n2. **Manifest identity changed.** The manifest presented here is not the one that\n was approved — a different entry count, different ordering, renumbered\n entries, or an altered scope boundary. A substituted split never silently\n replaces the approved one.\n3. **An entry past the size ceiling.** An entry whose stated scope runs beyond\n roughly 40 files or ~3000 LOC is over the bound wherever it sits — a\n standalone ticket and an epic child alike. Fail and name the entry; do not\n draft it, do not split it here, and do not shrink its stated scope to fit. An\n `XL` entry below that ceiling is not a violation and needs no supporting\n evidence: `XL` is the preferred shape for work that does not fit in `L`.\n4. **An entry with no writer draft after rendering.** Every entry must have\n produced a body. A missing draft is a failure, not an entry to skip.\n\nFail-open applies **only** to enrichment and context gathering — an optional\nresearch artifact that is missing may degrade the context a body is written\nfrom. Approval, manifest identity, sizing validity, and writer-output\ncompleteness all fail closed.\n\n## Instructions\n\n1. Read the approved manifest in full. Record its entry count, its order, and\n each entry's identity, so the identity check above has something to compare\n against.\n\n2. **Fan out one `jira-ticket-writer` invocation per entry**, in manifest order —\n the epic parent, then each child; or each ordinary sibling. Each invocation\n is bound to exactly one entry and receives, verbatim:\n - the entry's identity, position in the order, scope boundary, **size band**,\n parent relationship, `depends_on`, and `recommended_after`;\n - the acceptance criteria and design material for that entry's slice;\n - the shared research, materials, and framing artifacts;\n - an explicit output path for the drafted body.\n\n State in every prompt that the decomposition is **frozen**: the invocation\n renders its entry and may not add, remove, merge, reorder, renumber, or\n rescope anything, and may not write about sibling entries as though it were\n deciding them.\n\n3. **Do not batch.** One invocation per entry, never one invocation asked to emit\n every body. Two failure modes are both real and this shape avoids both: N\n fully independent decisions overlap, omit dependencies, and contradict the\n parent — which is why the decomposition is frozen upstream rather than\n re-derived per child; and one invocation emitting the whole set produces\n unreliable output as the manifest grows. Separating the decision from the\n rendering is what lets the rendering fan out safely, so a large manifest is\n handled by *more* invocations, never by collapsing back to one.\n\n4. Every entry type gets the same treatment — the same research protocol,\n materials inventory, secret-redaction rules, required sections, and Jira\n description bounds. An epic parent is not a thinner document than a\n standalone ticket, and an epic child is not a thinner document than either.\n\n5. When every entry has a draft, re-run the preflight checks against the\n rendered set and hand off to creation:\n - **Epic** — `upload-epic-hierarchy.md`, unchanged. It creates the parent with\n `issue_type = \"Epic\"`, captures the resolved epic key, then calls\n `create_ticket(parent_key=<epic_key>)` per child in manifest order with\n `track_ticket` after each, and carries idempotency labels throughout so a\n partial failure resumes rather than duplicating. Nothing new is required to\n *create* an epic — only to propose one.\n - **Siblings** — ordinary `create_ticket` calls in manifest order, unparented,\n with no epic parent synthesized.\n\n6. This stage emits **no conductor invocation**. The handoff belongs to the\n calling surface and names exactly one entry point, `drive-epic`.\n\n## Return\n\nReport the manifest's shape (`epic` or `siblings`), the entry count, each entry's\ndrafted body path in manifest order, and the preflight verdict. On a preflight\nfailure, name the violated prerequisite and the offending entry, and confirm that\nno ticket was created.\n",
728
1018
  "request-prd.md": "# request_prd\n\nStart (or refresh) asynchronous generation of a **Product Requirements Document\n(PRD)** for a Jira ticket.\n\nA PRD is the most product/stakeholder-facing document in the design-document\nfamily. It frames product intent — the problem, goals, non-goals, target users,\nsuccess metrics, product requirements, scope, and risks — rather than the\ndetailed functional flows and acceptance behavior an FSD covers, or the\narchitecture/implementation guidance a TDD covers.\n\n## Async request/retrieve pattern\n\n`request_prd` only **starts** generation; it does not return the PRD directly\nunless you set `wait_for_result`. PRD generation typically takes **2–4 minutes**.\n\n1. Call `request_prd` with the `ticket_number`.\n2. Wait for processing to complete (2–4 minutes).\n3. Call `get_prd` with the same `ticket_number` to retrieve the result.\n\nSet `wait_for_result: true` to block and return the PRD content directly instead\nof polling separately.\n\n## Parameters\n\n| Parameter | Type | Default | Description |\n| --- | --- | --- | --- |\n| `ticket_number` | string | — | Jira ticket key in `PROJECT-NUMBER` format (e.g. `BAPI-123`). |\n| `wait_for_result` | boolean | `false` | When `true`, block and poll until the PRD is ready, then return it directly. |\n| `save_locally` | boolean | `true` | When `wait_for_result` is `true`, save the PRD to `BAPI_DOCS_DIR/prd/{ticket}-prd-plan.md`. |\n| `second_opinion` | string | — | Provider routing override for **this** generation request (e.g. `anthropic`, `openai`, `gemini`). This is **not** the standalone `second_opinion` tool — it only changes which provider produces this request's artifact, and takes precedence over `provider`. |\n| `provider` | string | — | Pure provider switch without second-opinion semantics. If both `provider` and `second_opinion` are set, `second_opinion` wins. |\n\n## Return\n\n- `202` when the request is accepted (async dispatch).\n- `404` if the ticket does not exist in Jira.\n- `403` if the API key is unauthorized.\n",
729
1019
  "research-decision.md": "Decide which research tools to run for this idea, biased toward cheap local research first.\n\n## Inputs\n\n- Run manifest: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/run-manifest.json` (must already exist from the preflight step).\n\n## Instructions\n\n1. Read `{docs_dir}/idea-to-ticket/{slug}-{run_id}/run-manifest.json`. This is the source of truth for `idea`, `readiness`, `scope`, and `run_id`. If the file does not exist, halt locally — the preflight step did not complete.\n\n2. Decide which research tools should run for this idea, in roughly this priority order:\n - **Local codebase research first.** Inspect the working tree (search, grep, file reads) for prior art, related modules, and existing tests. Prefer this for anything that touches code you already own.\n - **Narrow web search second.** Use targeted web search for short factual lookups: a specific library API, a known external standard, a public spec.\n - **Deep research only when justified.** Deep research is expensive and slow; it must be earned by one of the rubric items below.\n\n3. Deep-research allowance rubric. Deep research is only allowed when at least one of these is true:\n - **blast radius**: the change spans many systems or has high reversibility cost (e.g., schema migrations, auth, billing, public APIs).\n - **unfamiliar external domain**: the idea depends on a third-party domain or specification the repository has no prior coverage of.\n - **compliance/security uncertainty**: there is real compliance or security uncertainty (SOC2, PII, secret handling, access control).\n - **cheaper research failed**: a cheaper round (local + narrow web search) already happened in this run and left blocking unknowns.\n - **explicit user request**: the user explicitly asked for deep research.\n\n4. Write `{docs_dir}/idea-to-ticket/{slug}-{run_id}/research-plan.json`. Required fields:\n - `selected_tools` — array of tool identifiers to run, drawn from at least `[\"codebase_search\", \"web_search\", \"deep_research\"]`. Empty array is allowed when no research is needed.\n - `rationale` — short string explaining the choice in terms of the rubric above.\n - `deep_research_query` — string. Required when `deep_research` is in `selected_tools`, otherwise empty string.\n - `web_search_topics` — array of strings; may be empty.\n - `codebase_search_topics` — array of strings; may be empty.\n - `expected_unknowns` — array of strings describing what the research is expected to resolve.\n\n5. Do not invoke any research tool from this step — that happens in `execute-research.md`. This step only writes the plan.\n\n## Return\n\nConfirm `research-plan.json` was written, list `selected_tools`, and quote the rationale.\n",
730
- "screen-and-resolve.md": "Apply project standards and the minimum-evidence gate before drafting.\n\n## Inputs\n\n- Run manifest: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/run-manifest.json`.\n- Research pack: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/research-pack.md` / `.json` (may be partial or absent).\n- Duplicate assessment: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/duplicate-assessment.json`.\n- Project standards: the response from the earlier `get_project_standards` step in the same pipeline run. Treat the standards as unavailable when that response was an error envelope, a 404, or missing.\n\n## Instructions\n\n1. Produce three artifacts in the run directory:\n - `{docs_dir}/idea-to-ticket/{slug}-{run_id}/standards-checklist.json`\n - `{docs_dir}/idea-to-ticket/{slug}-{run_id}/open-questions.md`\n - `{docs_dir}/idea-to-ticket/{slug}-{run_id}/resolved-uncertainties.md`\n\n2. Standards checklist:\n - When `get_project_standards` returned a usable result, derive the checklist items from those standards. Each item is a `{requirement, satisfied, evidence}` triple where `evidence` either cites the research pack or notes \"deferred to draft\".\n - When standards are unavailable (404, error envelope, missing), generate a fallback baseline checklist covering at least: requirements clarity, acceptance criteria presence, testability, security/PII consideration, and rollback/observability when scope warrants it. Mark each as `satisfied: false` with `evidence: \"fallback baseline — no project standards available\"`.\n\n3. Open questions:\n - List every unresolved unknown that blocks drafting. Pull from the research pack's `unresolved_unknowns` and from your own reading of `idea`.\n - Each question gets its own bullet (no markdown tables, no `- [ ]` checkboxes — BAPI-320 hygiene).\n - If the question has a defensible best-guess answer, write it in `resolved-uncertainties.md` instead, with explicit assumption language (\"Assuming X because Y...\").\n\n4. Resolved uncertainties:\n - Mirror open questions that have defensible best-guess answers. Each entry must include `assumption`, `basis` (research-pack reference or codebase reference), and `confidence` (\"low\", \"medium\", \"high\").\n\n5. Minimum-evidence gate. Halt locally if ALL of the following are true:\n - The research pack contains no codebase references for the idea.\n - The standards checklist has zero items (even the fallback baseline is missing).\n - `resolved-uncertainties.md` records no explicit assumptions.\n When the gate fires, do not continue to drafting. Report the halt and direct the user to either run a smaller idea, run `--allow-duplicate` semantics for replays, or supply more context manually.\n\n## Return\n\nConfirm the three artifacts were written and whether the minimum-evidence gate fired.\n",
1020
+ "screen-and-resolve.md": "Apply project standards and the minimum-evidence gate before drafting.\n\n## Inputs\n\n- Run manifest: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/run-manifest.json`.\n- Research pack: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/research-pack.md` / `.json` (may be partial or absent).\n- Duplicate assessment: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/duplicate-assessment.json`.\n- Project standards: the response from the earlier `get_project_standards` step in the same pipeline run. Treat the standards as unavailable when that response was an error envelope, a 404, missing, or contains the onboarding placeholder sentence \"No project standards are configured yet for\" (a 200 response body that is telling the reader how to get standards, not standards content — it can appear alone or appended after a populated `## Project Context` section).\n\n## Instructions\n\n1. Produce three artifacts in the run directory:\n - `{docs_dir}/idea-to-ticket/{slug}-{run_id}/standards-checklist.json`\n - `{docs_dir}/idea-to-ticket/{slug}-{run_id}/open-questions.md`\n - `{docs_dir}/idea-to-ticket/{slug}-{run_id}/resolved-uncertainties.md`\n\n2. Standards checklist:\n - When `get_project_standards` returned a usable result — real standards content, not the onboarding placeholder — derive the checklist items from those standards. Each item is a `{requirement, satisfied, evidence}` triple where `evidence` either cites the research pack or notes \"deferred to draft\".\n - When standards are unavailable (404, error envelope, missing, or the onboarding placeholder), generate a fallback baseline checklist covering at least: requirements clarity, acceptance criteria presence, testability, security/PII consideration, and rollback/observability when scope warrants it. Mark each as `satisfied: false` with `evidence: \"fallback baseline — no project standards available\"`.\n\n3. Open questions:\n - List every unresolved unknown that blocks drafting. Pull from the research pack's `unresolved_unknowns` and from your own reading of `idea`.\n - Each question gets its own bullet (no markdown tables, no `- [ ]` checkboxes — BAPI-320 hygiene).\n - If the question has a defensible best-guess answer, write it in `resolved-uncertainties.md` instead, with explicit assumption language (\"Assuming X because Y...\").\n\n4. Resolved uncertainties:\n - Mirror open questions that have defensible best-guess answers. Each entry must include `assumption`, `basis` (research-pack reference or codebase reference), and `confidence` (\"low\", \"medium\", \"high\").\n\n5. Minimum-evidence gate. Halt locally if ALL of the following are true:\n - The research pack contains no codebase references for the idea.\n - The standards checklist has zero items (even the fallback baseline is missing).\n - `resolved-uncertainties.md` records no explicit assumptions.\n When the gate fires, do not continue to drafting. Report the halt and direct the user to either run a smaller idea, run `--allow-duplicate` semantics for replays, or supply more context manually.\n\n## Return\n\nConfirm the three artifacts were written and whether the minimum-evidence gate fired.\n",
731
1021
  "store-and-approve-epic-plan.md": "Store the approved epic plan DAG in the backend and approve it.\n\nThis step runs after the user has approved the decomposition in the\n`decompose-epic` step. It reads the machine-readable sidecar written by that\nstep and wires it into the backend durable store.\n\nThe whole step is a single deterministic command — `setup-epic`. Do **not**\nhand-roll the HTTP calls, and do **not** compute the plan hash yourself.\n\n## Variables\n\n- `{epic_key}` — Jira epic key (e.g. BAPI-405)\n- `{epic_slug}` — lowercase-hyphen slug derived from the epic key\n- `{docs_dir}` — base docs directory (e.g. `docs/tmp`)\n\n## Step 1 — Confirm the DAG sidecar exists\n\nThe sidecar written by `decompose-epic` lives at\n`{docs_dir}/epic-plans/{epic_slug}/epic-plan.dag.json`.\n\nIf it is missing, warn the user with:\n\n> \"The structured DAG sidecar (`epic-plan.dag.json`) is missing or invalid.\n> The plan cannot be stored automatically. To recover, you can reconstruct the\n> DAG manually by parsing the Jira dependency links for each sub-task\n> (deterministic Jira-link DAG builder — documented fallback, not built here).\"\n\nThen stop this step with a warning (do not raise an error that aborts the\nentire pipeline).\n\nThe DAG must always come from this sidecar — **never from re-parsing the\nmarkdown** (`epic-plan.md`).\n\n## Step 2 — Run `setup-epic`\n\n```bash\nnpx -y @bridge_gpt/mcp-server setup-epic \\\n --epic-key {epic_key} \\\n --plan-file {docs_dir}/epic-plans/{epic_slug}/epic-plan.dag.json \\\n --json\n```\n\nThis creates the epic run, stores the plan blob, and approves it — in the one\norder that is safe. It is idempotent: re-running it on an epic that already has\na live run reuses that run rather than creating a second one.\n\n**Never work around a failure by POSTing to `/jira/epic-runs/runs` directly.**\nCreating a second active run for an epic wedges it permanently — every later\nplan call fails with \"Multiple active runs\" — and double-charges billing.\n\nPass `--dry-run` first if you want to validate the plan and preview the calls\nwithout changing anything.\n\n## Step 3 — Interpret the result\n\n`setup-epic` exits `0` on success and prints a JSON object with `epic_run_id`,\n`plan_version`, `plan_hash`, `status`, and any `warnings`.\n\nOn a non-zero exit, relay its error message verbatim — it is already actionable.\nThe three you are most likely to see:\n\n- **\"already stored with a DIFFERENT hash\"** — the plan blob changed after being\n stored. Increment `plan_version` in the sidecar and re-run.\n- **\"MULTIPLE active runs\"** — the epic is wedged. It must be repaired by\n abandoning the duplicate run before anything else can proceed.\n- **\"no touched_files\"** — a **warning**, not a rejection. The three cases are\n distinct and only one of them fails:\n - **Omitted entirely** — the node **opts out** of preemptive file-overlap\n serialization. The plan is stored and approved normally. That node is simply\n never pre-serialized against an overlapping sibling; if a sibling merge does\n conflict with it, the conflict is caught reactively at the merge gate and\n rebased. Adding the files it is predicted to touch buys back the scheduling\n protection, and is worth doing, but is not required.\n - **`touched_files: []`** — an explicit \"no predicted files\" **declaration**.\n Different from omission: the node is treated as declared, and its pairs are\n evaluated normally (and found non-overlapping).\n - **Malformed** — a non-list, or a list containing a blank, absolute, or\n non-string path — is still a hard `VALIDATION` **rejection**. Data you did\n supply must be well-formed. Fix the offending node and re-run.\n\n## Updating one approved node's `ticket_spec` mid-run\n\nA specification larger than Jira's 32,767-character description cap does **not**\nhave to be cut down. Store it on the approved plan node instead:\n\n```\nPATCH /epic-runs/runs/<epic_run_id>/plan/nodes/<ticket_key>/ticket-spec\nbody: repo_name, expected_plan_hash, ticket_spec\n```\n\n- Bounded at **64,000 characters**, stored verbatim — never truncated. The\n reconciler consults the plan node **first** when building a spec-review prompt,\n so the reviewer sees the complete text on the next tick.\n- **Safe mid-run.** It changes only that node's `ticket_spec` (plus the plan hash),\n and never the plan version, the approval pointers, or any `epic_ticket_status`\n row. No ticket is re-seeded and no in-flight gate state is disturbed.\n- **`expected_plan_hash` is required** and must match the currently approved hash.\n A stale hash on a real content change returns **409**; re-read the approved plan\n and retry. Re-sending content that already matches is an idempotent no-op\n (`updated: false`) and succeeds even with a stale hash, because nothing would be\n overwritten.\n- This is **not** the same as re-storing the full plan at a new version and\n re-approving it. That path re-seeds ticket rows and moves approval pointers, and\n remains unsafe for casual mid-run spec edits.\n\nA `plan_hash` that differs from the locally computed hash is **not** an error:\nthe server re-hashes after applying file-overlap serialization, and the server's\nhash is authoritative.\n\n## Return\n\nReport:\n- The epic run ID.\n- The stored `plan_version`.\n- The `plan_hash` returned by `setup-epic`.\n- Whether the run reached `active`.\n- Any warnings.\n\nExample: \"Plan v1 stored and approved for epic run `<epic_run_id>`. Run is now\nactive — the server-side reconciler will pick it up within ~30s.\"\n",
732
1022
  "update-ticket-rewrite.md": "Rewrite the Jira ticket description for {ticket_key} using the generated clarifying questions and critique documents.\n\n1. Fetch the current ticket description using the `get_ticket` tool with ticket_number `{ticket_key}`.\n2. Read the clarifying questions from the local file saved by the previous step (check `{docs_dir}/clarifying-questions/` for `{ticket_key}-clarifying-questions.md`). For each best-guess answer, verify it against the codebase using file search and code grep. Accept verified answers, correct inaccurate ones with evidence, and let ambiguous ones stand.\n3. Read the critique from the local file saved by the previous step (check `{docs_dir}/ticket-critiques/` for `{ticket_key}-ticket-quality-critique.md`). Address all Requested Changes. Apply Points to Consider selectively — accept genuine improvements, skip stylistic preferences.\n4. Write the rewritten ticket in standard markdown format (not Jira wiki markup). Preserve the Summary, Requirements, and Acceptance Criteria structure.\n5. Save the output to `{docs_dir}/tickets/{ticket_key}.md`. Output only the clean rewritten ticket — no meta-commentary.\n\n## Return\n\nConfirm the rewritten ticket was saved to `{docs_dir}/tickets/{ticket_key}.md` and briefly note which clarifying-question answers were corrected against the codebase and which critique Requested Changes were addressed.\n",
733
- "upload-and-track.md": "Step-10 umbrella upload instruction. Idempotently create the Jira ticket(s) for this run, attach the full draft(s), and call `track_ticket`.\n\n## Inputs\n\n- Run manifest: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/run-manifest.json`.\n- Draft metadata: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/draft-metadata.json`.\n- For epic runs, this instruction is also responsible for producing or refreshing `{docs_dir}/idea-to-ticket/{slug}-{run_id}/decomposition-plan.json` before any Jira mutation, by following `decompose-epic-candidate.md` (hard cap `{max_children}`).\n- Pipeline variable `auto_approve_external` controls whether the external-mutation pause is skipped (for this run, `auto_approve_external` = `{auto_approve_external}`). Treat the literal string `\"true\"` as skip; any other value (including `\"false\"`, missing, or empty) means pause and ask.\n\n## Instructions\n\n> **Orchestrator-directed step.** This agent task is part of the full-automation chain and is authorized to call `get_tickets`, `create_ticket`, `attachment` (operations: `upload`, `list`), `update_ticket_description`, `track_ticket`, and `add_comment`, and to execute the shared `gather-and-attach-materials.md` instruction, as directed below — performing orchestrator-directed tool calls is not \"re-orchestrating\".\n\n1. Read `run-manifest.json` and `draft-metadata.json`. Branch internally based on the manifest's `scope`:\n - `task` or `spike` → follow the **Single-ticket path** below.\n - `epic_candidate` → follow the **Epic path** below.\n The orchestrator does not support conditional steps; this branching lives in agent logic.\n\n2. External approval gate, applied before any mutating MCP tool call:\n - If `auto_approve_external` is `\"false\"` (or any non-`\"true\"` value), summarize the exact planned Jira mutations — list every `create_ticket`, `attachment` (operation: `\"upload\"`), and `track_ticket` call with its key arguments — and ask the user for explicit confirmation in this agent task before proceeding.\n - If `auto_approve_external` is `\"true\"`, proceed without the confirmation pause.\n\n3. **Single-ticket path** (`scope` is `task` or `spike`):\n 1. Idempotency lookup. Call `get_tickets` with its `labels` parameter set to both the per-run label `<idempotency_label>` and the stable `bapi-idea-hash-{idea_hash}` label from `draft-metadata.json` (comma-separated). If a match is found by either label, reuse that ticket key and skip `create_ticket`.\n 2. If no match was found, call `create_ticket` with `summary`, `slim_description` as the description, `issue_type`, and `labels` exactly as written in the metadata. Capture the returned `ticket_key`.\n 3. Upload the full markdown draft via `attachment` (operation: `\"upload\"`) using `attachment_path`.\n 4. **Gather and attach referenced materials.** Execute the shared `gather-and-attach-materials.md` instruction as an `agent_task`, passing `ticket_number` = the resolved ticket key, `draft_file_path` = `attachment_path`, and `auto_approve_external` = the inherited `{auto_approve_external}` value. It attaches phase-eligible local materials (Planning Assets, Downloadable Assets, and Planning & Downloadable Assets) and records external/auth-gated and binary/image materials per its own warn-not-halt rules. Any attach failure it reports is recorded (via `update_ticket_description`) as `partial_success` and never halts this step.\n 5. Call `track_ticket` with the resolved ticket key so Bridge API picks the new ticket up.\n 6. Write `{docs_dir}/idea-to-ticket/{slug}-{run_id}/upload-state.json` describing the final state.\n\n4. **Epic path** (`scope` is `epic_candidate`):\n 1. If `decomposition-plan.json` does not yet exist for this run, follow `decompose-epic-candidate.md` first to produce it (hard cap `{max_children}`).\n 2. Draft any surviving children that lack a draft on disk by calling `jira-ticket-writer` per child with the `draft_path` from the decomposition plan. After drafting, extend `draft-metadata.json` so `children[]` mirrors the final list from the decomposition plan.\n 3. Parent first. Look up the Epic parent by `bapi-idea-to-ticket-{run_id}-parent` via `get_tickets`. If found, reuse that key; otherwise call `create_ticket` with the parent's summary, slim description, issue type `Epic`, and parent labels. Attach the Epic draft via `attachment` (operation: `\"upload\"`) using `parent.attachment_path`. Then **gather and attach the Epic parent's referenced materials** by executing the shared `gather-and-attach-materials.md` instruction as an `agent_task`, passing `ticket_number` = the Epic key, `draft_file_path` = `parent.attachment_path`, and `auto_approve_external` = the inherited `{auto_approve_external}` value. Then call `track_ticket` for the Epic key.\n 4. Children next. For each child in order:\n - Look up by the child's `idempotency_label`. If found, reuse that key.\n - Otherwise call `create_ticket(parent_key=<epic_key>)` with the child's `summary`, `slim_description`, `issue_type`, and `labels`. The `parent_key` is required so Jira's modern parent linkage is set.\n - Upload the child draft via `attachment` (operation: `\"upload\"`) using `draft_path`.\n - **Gather and attach this child's referenced materials** by executing the shared `gather-and-attach-materials.md` instruction as an `agent_task`, passing `ticket_number` = the child key, `draft_file_path` = `draft_path`, and `auto_approve_external` = the inherited `{auto_approve_external}` value.\n - Call `track_ticket` for the child key.\n 5. After every parent or child mutation, write partial progress to `{docs_dir}/idea-to-ticket/{slug}-{run_id}/upload-state.json` so a later resume can pick up exactly where the run stopped.\n 6. **Recommended implementation order comment.** Once the Epic parent and all surviving children exist (real keys known), post a single comment on the Epic via `add_comment` with `ticket_number` set to the Epic key. The comment carries (a) a short System Goals / Non-Functional Requirements summary from `goals-and-nfrs.md`, and (b) the **Recommended Implementation Order** — the children in order, each referenced by its real Jira key, derived from the `depends_on` / `recommended_after` / `order_rationale` fields in `decomposition-plan.json`. State that this is recommended sequencing only — do **not** create Jira dependency links and do **not** attach a separate markdown doc. Skip this only if the run reused a pre-existing comment for the same run (idempotency); do not post duplicate order comments on resume.\n\n5. Required child label set whenever any child is created: `ai-generated`, `idea-to-ticket`, `idea-to-ticket-child`, and `bapi-idea-to-ticket-{run_id}-child-<N>` (1-based index from the decomposition plan).\n\n6. Partial-failure recovery rules:\n - If `create_ticket` succeeds but `attachment` (operation: `\"upload\"`) fails, record the outcome as `partial_success` in `upload-state.json` and continue with the next planned mutation; do not retry inside this step.\n - If the Epic parent is created successfully but one or more children fail, preserve the parent key and any completed child keys in `upload-state.json` before raising the failure.\n - On resume of any prior run, search by every relevant idempotency label first (`bapi-idea-to-ticket-{run_id}` for single tickets, `bapi-idea-to-ticket-{run_id}-parent`, and each `bapi-idea-to-ticket-{run_id}-child-<N>`) before considering any `create_ticket` call. Idempotency labels are how this pipeline avoids creating duplicate tickets across retries.\n\n## Return\n\nConfirm the run's final upload outcome: attachment results, `track_ticket` outcome, and any `partial_success` rows recorded in `upload-state.json`.\n\nThen, as the FINAL content of your reply, emit a fenced ```json block holding the authoritative payload for this run — and nothing else. The chain reads ONLY this final fenced JSON block to pick its review / start-tickets targets, so it must contain exactly the keys from `upload-state.json` and never any key you merely looked up during duplicate detection. Duplicate-detection / looked-up keys must not appear in this authoritative payload unless they are the final created/reused ticket for this run.\n\nThere are exactly two authoritative final payload shapes:\n\n- **Single-ticket path** (`scope` is `task` or `spike`): emit strictly `created_ticket_keys` containing **exactly one** implementable ticket key. `created_ticket_keys` is only for the single-ticket `task`/`spike` path and must contain exactly one implementable ticket key:\n\n ```json\n {\"created_ticket_keys\": [\"BAPI-331\"]}\n ```\n\n- **Epic path** (`scope` is `epic_candidate`): emit the Epic parent key separately as `epic_parent_key`, and the implementable children as `child_ticket_keys`:\n\n ```json\n {\"epic_parent_key\": \"BAPI-400\", \"child_ticket_keys\": [\"BAPI-401\", \"BAPI-402\"]}\n ```\n\n `child_ticket_keys` contains **only** implementable child Task/Spike ticket keys, listed in final decomposition order. `child_ticket_keys` must **never** include the Epic parent key.\n",
1023
+ "upload-and-track.md": "Step-10 umbrella upload instruction. Idempotently create the Jira ticket(s) for this run, attach the full draft(s), and call `track_ticket`.\n\n## Inputs\n\n- Run manifest: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/run-manifest.json`.\n- Draft metadata: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/draft-metadata.json`.\n- For epic runs, this instruction is also responsible for producing or refreshing `{docs_dir}/idea-to-ticket/{slug}-{run_id}/decomposition-plan.json` before any Jira mutation, by following `decompose-epic-candidate.md` (hard cap `{max_children}`).\n- Pipeline variable `auto_approve_external` controls whether the external-mutation pause is skipped (for this run, `auto_approve_external` = `{auto_approve_external}`). Treat the literal string `\"true\"` as skip; any other value (including `\"false\"`, missing, or empty) means pause and ask.\n\n## Instructions\n\n> **Orchestrator-directed step.** This agent task is part of the full-automation chain and is authorized to call `get_tickets`, `create_ticket`, `attachment` (operations: `upload`, `list`), `update_ticket_description`, `track_ticket`, and `add_comment`, and to execute the shared `gather-and-attach-materials.md` instruction, as directed below — performing orchestrator-directed tool calls is not \"re-orchestrating\".\n\n1. Read `run-manifest.json` and `draft-metadata.json`. Branch internally based on the manifest's `scope`:\n - `task` or `spike` → follow the **Single-ticket path** below.\n - `epic_candidate` → follow the **Epic path** below.\n The orchestrator does not support conditional steps; this branching lives in agent logic.\n\n2. External approval gate, applied before any mutating MCP tool call:\n - If `auto_approve_external` is `\"false\"` (or any non-`\"true\"` value), summarize the exact planned Jira mutations — list every `create_ticket`, `attachment` (operation: `\"upload\"`), and `track_ticket` call with its key arguments — and ask the user for explicit confirmation in this agent task before proceeding.\n - If `auto_approve_external` is `\"true\"`, proceed without the confirmation pause.\n\n3. **Single-ticket path** (`scope` is `task` or `spike`):\n 1. Idempotency lookup. Call `get_tickets` with its `labels` parameter set to both the per-run label `<idempotency_label>` and the stable `bapi-idea-hash-{idea_hash}` label from `draft-metadata.json` (comma-separated). If a match is found by either label, reuse that ticket key and skip `create_ticket`.\n 2. If no match was found, call `create_ticket` with `summary`, `slim_description` as the description, `issue_type`, and `labels` exactly as written in the metadata. Capture the returned `ticket_key`.\n 3. Upload the full markdown draft via `attachment` (operation: `\"upload\"`) using `attachment_path`.\n 4. **Gather and attach referenced materials.** Execute the shared `gather-and-attach-materials.md` instruction as an `agent_task`, passing `ticket_number` = the resolved ticket key, `draft_file_path` = `attachment_path`, and `auto_approve_external` = the inherited `{auto_approve_external}` value. It attaches phase-eligible local materials (Planning Assets, Downloadable Assets, and Planning & Downloadable Assets) and records external/auth-gated and binary/image materials per its own warn-not-halt rules. Any attach failure it reports is recorded (via `update_ticket_description`) as `partial_success` and never halts this step.\n 5. Call `track_ticket` with the resolved ticket key so Bridge API picks the new ticket up.\n 6. Write `{docs_dir}/idea-to-ticket/{slug}-{run_id}/upload-state.json` describing the final state.\n\n4. **Epic path** (`scope` is `epic_candidate`):\n 1. If `decomposition-plan.json` does not yet exist for this run, follow `decompose-epic-candidate.md` first to produce it (hard cap `{max_children}`). If it **does** already exist, it is the approved manifest and this step performs **no fresh decomposition** — read it and use it as written. Decomposition happens once; re-deriving the split immediately before upload is how children end up overlapping or contradicting the parent they are about to be created under.\n 2. Render bodies against the frozen manifest by following `render-ticket-manifest.md`: one `jira-ticket-writer` invocation per entry that lacks a draft on disk, each bound to its own entry's fixed boundary and size band, using the `draft_path` from the decomposition plan. A rendering invocation may not re-split, merge, reorder, renumber, or rescope. Its preflight is fail-closed — an unapproved manifest, a changed manifest identity, an XL epic child, or a missing writer draft stops the flow **before** any Jira mutation. After drafting, extend `draft-metadata.json` so `children[]` mirrors the final list from the decomposition plan.\n 3. Create only from writer-produced drafts. Every ticket body uploaded below came from `jira-ticket-writer`; nothing here composes a description inline.\n 4. Parent first. Look up the Epic parent by `bapi-idea-to-ticket-{run_id}-parent` via `get_tickets`. If found, reuse that key; otherwise call `create_ticket` with the parent's summary, slim description, issue type `Epic`, and parent labels. Attach the Epic draft via `attachment` (operation: `\"upload\"`) using `parent.attachment_path`. Then **gather and attach the Epic parent's referenced materials** by executing the shared `gather-and-attach-materials.md` instruction as an `agent_task`, passing `ticket_number` = the Epic key, `draft_file_path` = `parent.attachment_path`, and `auto_approve_external` = the inherited `{auto_approve_external}` value. Then call `track_ticket` for the Epic key.\n 5. Children next. For each child in order:\n - Look up by the child's `idempotency_label`. If found, reuse that key.\n - Otherwise call `create_ticket(parent_key=<epic_key>)` with the child's `summary`, `slim_description`, `issue_type`, and `labels`. The `parent_key` is required so Jira's modern parent linkage is set.\n - Upload the child draft via `attachment` (operation: `\"upload\"`) using `draft_path`.\n - **Gather and attach this child's referenced materials** by executing the shared `gather-and-attach-materials.md` instruction as an `agent_task`, passing `ticket_number` = the child key, `draft_file_path` = `draft_path`, and `auto_approve_external` = the inherited `{auto_approve_external}` value.\n - Call `track_ticket` for the child key.\n 6. After every parent or child mutation, write partial progress to `{docs_dir}/idea-to-ticket/{slug}-{run_id}/upload-state.json` so a later resume can pick up exactly where the run stopped.\n 7. **Recommended implementation order comment.** Once the Epic parent and all surviving children exist (real keys known), post a single comment on the Epic via `add_comment` with `ticket_number` set to the Epic key. The comment carries (a) a short System Goals / Non-Functional Requirements summary from `goals-and-nfrs.md`, and (b) the **Recommended Implementation Order** — the children in order, each referenced by its real Jira key, derived from the `depends_on` / `recommended_after` / `order_rationale` fields in `decomposition-plan.json`. State that this is recommended sequencing only — do **not** create Jira dependency links and do **not** attach a separate markdown doc. Skip this only if the run reused a pre-existing comment for the same run (idempotency); do not post duplicate order comments on resume.\n\n5. Required child label set whenever any child is created: `ai-generated`, `idea-to-ticket`, `idea-to-ticket-child`, and `bapi-idea-to-ticket-{run_id}-child-<N>` (1-based index from the decomposition plan).\n\n6. Partial-failure recovery rules:\n - If `create_ticket` succeeds but `attachment` (operation: `\"upload\"`) fails, record the outcome as `partial_success` in `upload-state.json` and continue with the next planned mutation; do not retry inside this step.\n - If the Epic parent is created successfully but one or more children fail, preserve the parent key and any completed child keys in `upload-state.json` before raising the failure.\n - On resume of any prior run, search by every relevant idempotency label first (`bapi-idea-to-ticket-{run_id}` for single tickets, `bapi-idea-to-ticket-{run_id}-parent`, and each `bapi-idea-to-ticket-{run_id}-child-<N>`) before considering any `create_ticket` call. Idempotency labels are how this pipeline avoids creating duplicate tickets across retries.\n\n## Return\n\nConfirm the run's final upload outcome: attachment results, `track_ticket` outcome, and any `partial_success` rows recorded in `upload-state.json`.\n\nThen, as the FINAL content of your reply, emit a fenced ```json block holding the authoritative payload for this run — and nothing else. The chain reads ONLY this final fenced JSON block to pick its review / start-tickets targets, so it must contain exactly the keys from `upload-state.json` and never any key you merely looked up during duplicate detection. Duplicate-detection / looked-up keys must not appear in this authoritative payload unless they are the final created/reused ticket for this run.\n\nThere are exactly two authoritative final payload shapes:\n\n- **Single-ticket path** (`scope` is `task` or `spike`): emit strictly `created_ticket_keys` containing **exactly one** implementable ticket key. `created_ticket_keys` is only for the single-ticket `task`/`spike` path and must contain exactly one implementable ticket key:\n\n ```json\n {\"created_ticket_keys\": [\"BAPI-331\"]}\n ```\n\n- **Epic path** (`scope` is `epic_candidate`): emit the Epic parent key separately as `epic_parent_key`, and the implementable children as `child_ticket_keys`:\n\n ```json\n {\"epic_parent_key\": \"BAPI-400\", \"child_ticket_keys\": [\"BAPI-401\", \"BAPI-402\"]}\n ```\n\n `child_ticket_keys` contains **only** implementable child Task/Spike ticket keys, listed in final decomposition order. `child_ticket_keys` must **never** include the Epic parent key.\n",
734
1024
  "upload-epic-hierarchy.md": "Standalone Epic upload protocol. Use as the detailed reference for the Epic path triggered from `upload-and-track.md`.\n\n## Inputs\n\n- Run manifest: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/run-manifest.json` with `scope == \"epic_candidate\"`.\n- Draft metadata: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/draft-metadata.json` with a populated `parent` and `children`.\n- Decomposition plan: `{docs_dir}/idea-to-ticket/{slug}-{run_id}/decomposition-plan.json`.\n- Pipeline variable `auto_approve_external` governs the external-mutation pause as in `upload-and-track.md` (for this run, `auto_approve_external` = `{auto_approve_external}`).\n\n## Instructions\n\n1. Parent idempotency lookup. Search Jira via `get_tickets` for issues carrying the label `bapi-idea-to-ticket-{run_id}-parent`. If a match exists, reuse that ticket key as the Epic parent and skip `create_ticket` for the parent. Otherwise call `create_ticket` with the parent's summary, slim description, `issue_type = \"Epic\"`, and labels including `ai-generated`, `idea-to-ticket`, and `bapi-idea-to-ticket-{run_id}-parent`. After creation or reuse, upload the Epic draft via `attachment` (operation: `\"upload\"`) and call `track_ticket`.\n\n2. Capture the resolved Epic key into a local variable `epic_key`. Every subsequent child mutation must reference this exact key.\n\n3. Per-child idempotency lookup. For each child in `decomposition-plan.json` (in order), search Jira by the child's `idempotency_label` (`bapi-idea-to-ticket-{run_id}-child-<N>`). If a match exists, reuse that key and skip `create_ticket` for that child. Otherwise call `create_ticket(parent_key=<epic_key>)` with:\n - `summary` — child summary.\n - `slim_description` — child slim description.\n - `issue_type` — typically `Task` (or `Spike` when the child is primarily discovery).\n - `labels` — `ai-generated`, `idea-to-ticket`, `idea-to-ticket-child`, and the child's own `bapi-idea-to-ticket-{run_id}-child-<N>` label.\n The `parent_key` argument is REQUIRED for every child `create_ticket` call so Jira sets the modern parent relationship; never omit it.\n\n4. After each child is created or reused, upload its draft via `attachment` (operation: `\"upload\"`) using the child's `draft_path`, then call `track_ticket` for that child key, then append the child outcome to `upload-state.json` in the run directory.\n\n5. On partial failure (e.g., parent succeeded, third child failed), preserve `epic_key` plus every completed child key in `upload-state.json`. The next run of this protocol must rediscover those keys via the idempotency-label lookups in steps 1 and 3 before considering any new `create_ticket` call.\n\n## Return\n\nConfirm the Epic key, the number of children created vs reused vs failed, and the path of the updated `upload-state.json`.\n",
735
1025
  "verify-plan.md": "Close the remaining plan gaps for ticket {ticket_key}, now that the pull request is open.\n\nThe plan's work has already been executed. The production phase authored the\nartifacts, the checkpoint pushed them, and the pre-PR verification phase ran the\nplan's review steps, test commands, and rendered-UI remediation — publishing each\nmaterial correction as it went. The pull request was then opened on top of all of it.\n\nThis phase exists for the narrow remainder: the plan obligations that genuinely could\n**not** be reached before a pull request existed, plus corrections attributable to this\nticket.\n\nTwo consequences follow, and both are deliberate:\n\n- **This phase does not re-run completed work.** A step the durable ledger records as\n `executed` or `adapted` stays settled unless a later correction invalidated its\n evidence. Re-running it duplicates work the pre-PR phase already did and burns the\n budget this protocol was reordered to protect.\n- **This phase never issues a verdict.** You report what you observed. The\n authoritative pass/fail belongs to the pipeline's `ci` and `code_review` gates,\n which the reconciler observes independently. Worker self-verification has\n demonstrably reported green while the full suite was red; that is exactly why the\n gates, not this phase, decide.\n\n**Tool and scope boundary.** Use only the tools this recipe names, the repo's own tooling, and MCP\ncapabilities provisioned for this repository. Keep all work confined to this worktree unless\nexplicitly told to do otherwise.\n\n**Execution mode for this run: `{execution_mode}`.** Under `orchestrated`\n(`run_pipeline` / `resume_pipeline`) orchestration appends the routed phase context and\nparses the fenced result envelope you return. Under `inline` (`get_pipeline_recipe`)\nthere is no orchestrator: the ledger is read with a tool call and written with one.\nFollow the branch that matches wherever the two are named.\n\n---\n\n## Step 1 — Establish that the durable artifact exists\n\nBefore running any check:\n\n1. Run `git branch --show-current` and `git rev-parse HEAD`, then verify the branch\n has been pushed and the local head is present on the remote (for example via\n `git status -sb` showing no unpushed ahead-count, or `git ls-remote origin <branch>`).\n2. Verify that a usable pull request URL was obtained by the preceding PR step —\n either a newly opened pull request or an already-open one on this head branch.\n\nIf the branch is not pushed, or no usable pull request URL exists, then\n**stop this phase** and report the missing prerequisite. This phase exists only to\nadd work on top of an open pull request.\n\n## Step 2 — Recover what remains from durable state\n\n1. Call the `get_plan` tool for `{ticket_key}`. The local copy at\n `{docs_dir}/plans/{ticket_key}-plan.md` may be used as a reference.\n2. Recover the durable ledger, by mode:\n - **orchestrated** — read the **Routed phase context** block appended to this\n instruction: `ledger` carries every disposition earlier phases recorded, and\n `ownedSteps` carries anything routed directly to this phase.\n - **inline** — call `get_phase_context` with `ticket_key` `{ticket_key}` and\n `phase` `post_pr_gap_close`. It returns the same `ownedSteps` plus the `ledger`\n merged from every earlier phase's artifact, `terminalStepIds` for what is\n already settled, and `unresolved` for the escalations that are this phase's\n actual subject.\n\nRecover the remaining work from that durable record, not from conversation. A\ncompaction or a resumed session loses the conversation; the ledger survives both,\nwhich is why it replaced the conversational hand-off.\n\n## Step 3 — Select only genuine gaps\n\nRun only:\n\n- Steps the ledger records as `escalated` **because a capability was unavailable\n before the pull request existed**, or **because the check genuinely requires an open pull request** — and\n which are now satisfiable.\n- Corrections clearly attributable to this ticket's change.\n\nReport — do not attempt to fix — failures that are unrelated to this ticket, flaky,\nenvironmental, pre-existing on the base branch, or outside the declared file scope.\nSpeculative edits made under budget pressure are how a correction round turns into a\nregression.\n\nApply the same adaptation boundary the earlier phases use: **locator-correction**,\n**repository-command-correction**, and **equivalent-implementation-recognized** are\nmechanical and may be applied; anything touching design, schema, public API,\ndependencies, or security escalates instead.\n\nBefore beginning any correction, apply the **low-budget guard**: only start if enough\nsession budget clearly remains to make the edit, commit it, *and* push it. Starting a\nfix you cannot finish and publish is strictly worse than reporting the finding and\nletting the CI and review gates handle it — an unpushed correction is invisible to\nthose gates.\n\n## Step 4 — Report what you observed, honestly\n\nFor every check you run, record the exact command, its observed result, and — on\nfailure — the relevant failure detail (the failing test names, the error output, the\ndiagnostic lines).\n\nReport all of it, including failures you did not fix. Never soften or omit a failing\nresult.\n\nDescribe only what you observed. Do not write that CI passed, that the gate is met,\nthat the review is approved, or any equivalent claim about the pipeline's verdict —\nthose states are decided by the `ci` and `code_review` gates and observed by the\nreconciler, never asserted by this phase.\n\n## Step 5 — Correct only what is clearly yours, and push it immediately\n\nFor each accepted correction:\n\n1. Make the edit.\n2. Stage the specific files, commit, and **push immediately** — the commit and its\n push are one consecutive sequence, never separated by another check. A local\n commit that is never pushed is not visible to the pull request, to CI, or to the\n reconciler.\n3. Run `git rev-parse HEAD` again and record the new pushed head as\n `last_commit_sha`.\n\n## Step 6 — Final git-state audit\n\nBefore returning, run `git status --porcelain` and resolve the working tree:\n\n- Legitimate corrections still uncommitted → commit and push them (Step 5's\n commit-then-push-immediately rule applies).\n- Accidental diagnostic edits — debug prints, scratch files, temporary config\n tweaks made while investigating a failure → revert them when it is safe to do so.\n- Anything you cannot safely resolve → leave it and **report it explicitly**,\n naming each remaining dirty path.\n\nNever return leaving unpushed commits unreported.\n\n## Step 7 — Hand unresolved findings forward\n\nAn unresolved local finding is normally **not** a reason to stop the pipeline. The\npull request is open and the authoritative gates will evaluate it. Report the\nfinding and let CI monitoring and code review take it from there.\n\nStop only when continuing would be unsafe or impossible — for example the durable\nartifact from Step 1 turned out to be missing, or the working tree is in a state you\ncannot resolve without risking the pushed branch.\n\n## Return\n\nReturn a summary containing:\n\n- the branch and the pull request URL,\n- the latest pushed `last_commit_sha`,\n- every gap-closing command run, with its observed outcome,\n- the correction commit, if one was made and pushed,\n- every unresolved finding and every unresolved dirty path.\n\nState these as worker observations. Do not include a pass/fail verdict for the `ci`\nor `code_review` gates.\n\nThen record the machine-readable phase result, by mode, so the durable ledger records\nhow the remaining gaps closed.\n\n### orchestrated\n\nEnd your result with the envelope in a fenced block tagged `bapi-phase-result`, which\norchestration parses, validates, and persists:\n\n```bapi-phase-result\n{\"version\":1,\"phase\":\"post_pr_gap_close\",\"lastCommitSha\":\"<sha>\",\"records\":[]}\n```\n\n### inline\n\nCall the `record_phase_result` tool with `ticket_key` `{ticket_key}` and\n`phase_result` set to that same envelope object; the tool validates and persists it.\n**Do not also emit a fenced `bapi-phase-result` block** — nothing parses one on this\npath.\n\nThe tool call is this phase's final action, **not the end of your turn.** When it\nreturns successfully, continue immediately with the next recipe step — the ticket\nstatus transition, the CI follow-up config, and CI monitoring. The pull request is\nopen and its gates are still pending; stopping here abandons the run before anything\nobserves them.\n\nIf the call fails, fix what it reports and call it again.\n",
736
1026
  "write-epic-summary.md": "Synthesize all sub-task explorations into a final overview document.\n\n## Instructions\n\n1. First, use a terminal command or glob pattern to list all files in `{docs_dir}/epic-plans/{epic_slug}/explorations/`. Then read each file. Do not guess filenames — discover them dynamically.\n\n2. Also read:\n - `{docs_dir}/epic-plans/{epic_slug}/research-findings.md`\n - `{docs_dir}/epic-plans/{epic_slug}/epic-plan.md`\n - `{docs_dir}/epic-plans/{epic_slug}/goals-and-nfrs.md` (the goals/NFR framing; carry its System Goals, NFRs, and any Recommended Implementation Order through to the overview).\n\n3. Synthesize the information into an overview and write it to `{docs_dir}/epic-plans/{epic_slug}/overview.md` with the following required sections:\n\n```markdown\n# Epic Overview: {epic title derived from description}\n\n## Epic Description and Goals\n{Summary of the epic's purpose, scope, and desired outcomes. Lead with the business goal and desired end-state from goals-and-nfrs.md.}\n\n## Non-Functional Requirements\n{The classified NFRs from goals-and-nfrs.md — each with its category, requirement, implication, and final status (confirmed/assumed). Any NFRs the user clarified should now read as confirmed/assumed, not open.}\n\n## Research Summary\n{Key external findings that informed the decomposition. If no research was performed, state \"No external research was needed.\"}\n\n## Sub-task List\n{Numbered list of all sub-tasks with relative markdown links to their exploration docs.}\n1. [Sub-task title](explorations/01-subtask-slug.md) — one-line summary\n2. [Sub-task title](explorations/02-subtask-slug.md) — one-line summary\n...\n\n## Dependency Graph\n{Textual list showing execution ordering and dependencies between sub-tasks.}\n- Sub-task 1: No dependencies (start here)\n- Sub-task 2: Depends on Sub-task 1\n- Sub-task 3: Depends on Sub-task 1\n- Sub-task 4: Depends on Sub-tasks 2, 3\n...\n\n## Recommended Implementation Order\n{The recommended order in which to implement the sub-tasks, reconciling the provisional order from goals-and-nfrs.md with the approved decomposition. For each sub-task give the position, its hard prerequisites (depends on), any soft sequencing preferences (recommended after), and a one-line rationale. This is recommended sequencing only — no Jira dependency links are created.}\n\n## Next Steps\n{One-line summaries for each sub-task, specifically formatted so they can be copy-pasted directly into the `/write-ticket` command. Each line should be a self-contained ticket description.}\n```\n\n4. After writing the overview, display the file path to the user and summarize the epic plan.\n\n5. **Push the goals/NFRs + recommended order into the Jira epic (only when `{epic_key}` is non-empty).** The `epic_key` is empty when this run was started from free-form text rather than an existing Epic; in that case skip this step. When `{epic_key}` is a real Jira key, post the System Goals, the final NFRs, and the Recommended Implementation Order as a **comment** on that epic by calling the `add_comment` MCP tool with `ticket_number` set to `{epic_key}` and a concise comment containing those three parts. Do not create Jira dependency links and do not attach a separate markdown doc — the comment is the delivery. Display: `\"Posted epic goals/NFRs and recommended implementation order to {epic_key}\"`.\n\n## Return\n\nConfirm the overview was written to `{docs_dir}/epic-plans/{epic_slug}/overview.md` and report the total sub-task count along with a one-line summary of the epic plan. State whether the goals/NFRs + recommended order were posted as a comment on `{epic_key}` or skipped because no epic key was provided.\n"