@bridge_gpt/mcp-server 0.2.66 → 0.2.67
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +252 -77
- package/build/agent-capabilities/probes.js +15 -5
- package/build/agent-launchers/claude-executor-adapter.js +43 -16
- package/build/agent-launchers/executor-adapter.js +7 -4
- package/build/agents.generated.js +1 -1
- package/build/bounded-wait.js +67 -10
- package/build/commands.generated.js +11 -11
- package/build/conductor/bridge-api-client.js +143 -1
- package/build/conductor/bring-up-facts.js +1 -1
- package/build/conductor/doctor.js +3 -3
- package/build/conductor/epic-implementer-cli.js +22 -6
- package/build/conductor/errors.js +4 -3
- package/build/conductor/guided-repair.js +6 -2
- package/build/conductor/hook-runtime-qualification.js +1 -1
- package/build/conductor/merge-execution-contract.js +27 -8
- package/build/conductor/merge-execution-eligibility.js +3 -2
- package/build/conductor/pr-ci-producer.js +1 -1
- package/build/conductor/readiness-cli.js +18 -2
- package/build/conductor/readiness-sections.js +41 -6
- package/build/conductor/readiness.js +181 -29
- package/build/conductor/recovery-cli.js +200 -8
- package/build/conductor/recovery-operations.js +69 -3
- package/build/conductor/repair-contract.js +54 -3
- package/build/conductor/repository-workflow-remedy.js +125 -0
- package/build/conductor/tools.js +2 -2
- package/build/conductor-bundle-artifacts.js +8 -0
- package/build/docs.generated.js +8 -3
- package/build/drive-epic-key.js +6 -5
- package/build/drive-epic.js +85 -54
- package/build/epic-coherence-input.js +2 -1
- package/build/epic-implementer-bin.js +22 -20
- package/build/epic-implementer-bundle-cli.js +2 -2
- package/build/executor/cli.js +3 -2
- package/build/executor/completion-contract.js +48 -5
- package/build/executor/completion-supervision.js +4 -0
- package/build/executor/env.js +19 -0
- package/build/executor/heartbeat.js +1 -1
- package/build/executor/http-client.js +20 -0
- package/build/executor/job-errors.js +25 -0
- package/build/executor/job-runner.js +225 -41
- package/build/executor/observation.js +6 -1
- package/build/executor/preflight.js +51 -14
- package/build/executor/required-work-ledger.js +6 -1
- package/build/executor/resume-pre-spawn.js +2 -0
- package/build/executor/runner.js +8 -2
- package/build/executor/service-unit.js +7 -1
- package/build/executor/worker-finalization.js +82 -29
- package/build/executor/worker-guard-hook.js +137 -33
- package/build/executor/worktree.js +82 -1
- package/build/index.js +403 -429
- package/build/install-bridge-conductor.js +8 -6
- package/build/install-bridge.js +3 -3
- package/build/phase-context-git-probe.js +68 -0
- package/build/phase-result-artifacts.js +173 -5
- package/build/pipelines.generated.js +18 -24
- package/build/plane/alembic-head.js +12 -3
- package/build/plane/base-worktree-guard.js +147 -0
- package/build/plane/cli.js +217 -17
- package/build/plane/code-snapshot.js +468 -0
- package/build/plane/defaults.js +48 -3
- package/build/plane/endpoint.js +41 -0
- package/build/plane/manifest.js +120 -1
- package/build/plane/member-roster.js +94 -19
- package/build/plane/preflight.js +30 -2
- package/build/plane/process-cwd.js +67 -0
- package/build/plane/restart.js +542 -0
- package/build/plane/shutdown.js +31 -0
- package/build/plane/status.js +143 -8
- package/build/plane/supervisor.js +586 -179
- package/build/plane/types.js +54 -2
- package/build/printed-remedy-commands.js +37 -0
- package/build/readiness-check.js +1 -1
- package/build/readme.generated.js +1 -1
- package/build/review-generation.js +22 -0
- package/build/setup-epic.js +44 -19
- package/build/sfcc/config.js +9 -0
- package/build/sfcc/credentials.js +100 -38
- package/build/sfcc/hostname.js +67 -0
- package/build/sfcc/permissions.js +17 -13
- package/build/sfcc/register.js +1 -1
- package/build/sfcc/tool-wrapper.js +39 -43
- package/build/sfcc/write-grants.js +17 -11
- package/build/sfcc/write-result.js +4 -3
- package/build/sfcc/writes-site-preference.js +20 -8
- package/build/start-tickets.js +179 -31
- package/build/tool-error-envelope.js +61 -0
- package/build/version.generated.js +3 -3
- package/build/visual-diff.js +22 -1
- package/build/worker-guard-hook-bin.js +3 -6
- package/build/worker-guard-hook-main.js +47 -0
- package/build/worktree-core.js +130 -4
- package/docs/EPIC-IMPLEMENTER-INSTALL.md +849 -0
- package/docs/EPIC-IMPLEMENTER.md +662 -0
- package/docs/install/mcp-tool-integrations.md +24 -15
- package/docs/install/sfcc-integration.md +12 -4
- package/package.json +1 -1
- package/pipelines/plan-epic.json +0 -5
- package/pipelines/review-ticket.json +1 -1
- package/docs/CONDUCTOR.md +0 -605
- package/pipelines/idea-to-pr.json +0 -49
package/docs/CONDUCTOR.md
DELETED
|
@@ -1,605 +0,0 @@
|
|
|
1
|
-
# Conductor — epic & multi-agent orchestration
|
|
2
|
-
|
|
3
|
-
Conductor is the **opt-in, off-by-default** coordination layer for running many
|
|
4
|
-
agent sessions together (epic supervision, inter-agent messaging, done-gate
|
|
5
|
-
evaluation, and conditional auto-merge). A normal `start-tickets` run does **not**
|
|
6
|
-
involve Conductor — you opt in per run with `--conductor`.
|
|
7
|
-
|
|
8
|
-
This document is the reference for Conductor's architecture, epic setup,
|
|
9
|
-
observability stream, local git hooks, and the per-repo done-gate / auto-merge
|
|
10
|
-
config. For the everyday `start-tickets` flags and cross-platform behavior, see
|
|
11
|
-
[README → CLI Subcommands](../README.md#cli-subcommands).
|
|
12
|
-
|
|
13
|
-
## Epic Conductor v2 — how an epic is actually driven
|
|
14
|
-
|
|
15
|
-
**The v1 `epic-implementer epic-tick` command is frozen.** It throws
|
|
16
|
-
`EPIC_TICK_V1_FROZEN` on every invocation and advances nothing. There is nothing
|
|
17
|
-
to schedule locally. If you have an epic-tick schedule registered from an earlier
|
|
18
|
-
release, remove it — it is a dead timer.
|
|
19
|
-
`epic-implementer doctor` flags one if it finds it. (S4/BAPI-1080 renamed the
|
|
20
|
-
event-ledger CLI from `conductor` to `epic-implementer`; the retained
|
|
21
|
-
`conductor` bin name is now a fixed migration pointer.)
|
|
22
|
-
|
|
23
|
-
v2 splits the old local tick into two halves:
|
|
24
|
-
|
|
25
|
-
| Half | Where it runs | What it does |
|
|
26
|
-
| --- | --- | --- |
|
|
27
|
-
| **Reconciler** | **Server-side**, on the Bridge API worker dyno, every 30s | Selects every epic run whose status is `active`, evaluates gates, and enqueues executor jobs. Nothing to install or schedule. |
|
|
28
|
-
| **Executor** | **Locally**, on your machine | Polls for jobs, claims them, spawns worker agents, heartbeats. This is the only piece you run. |
|
|
29
|
-
|
|
30
|
-
> **⚠️ For a run against a LOCAL server, do NOT use `npx`.** The npm-published
|
|
31
|
-
> package routinely lags `main` — that is the normal steady state right after any
|
|
32
|
-
> conductor ticket merges. When the server is built from a newer `main` it mints
|
|
33
|
-
> `prompt_spec` placeholders the older published executor rejects, and the job dies
|
|
34
|
-
> instantly with `ContractError.Prompt` (`prompt_spec for job N declares unsupported
|
|
35
|
-
> placeholder '…'`). Confirmed on the BAPI-716 run: published `0.2.34` vs. local
|
|
36
|
-
> `0.2.35`, over BAPI-699's `RELATED_CONTEXT`. Build and run the local tree so the
|
|
37
|
-
> executor and server come from the same commit:
|
|
38
|
-
>
|
|
39
|
-
> ```
|
|
40
|
-
> cd mcp_server && npm run build
|
|
41
|
-
> node mcp_server/build/index.js executor --repo <name>
|
|
42
|
-
> ```
|
|
43
|
-
|
|
44
|
-
For a run against a deployed server whose version you match, the published CLI is
|
|
45
|
-
fine:
|
|
46
|
-
|
|
47
|
-
```
|
|
48
|
-
npx -y @bridge_gpt/mcp-server executor --repo <name>
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
The reconciler drives a run purely off `epic_runs.status = 'active'` — there is no
|
|
52
|
-
local schedule, and no per-repo "enable the tick" flag. (An `epic_tick_enabled`
|
|
53
|
-
config field existed briefly and was inert; it has been removed.)
|
|
54
|
-
|
|
55
|
-
## Setting up an epic
|
|
56
|
-
|
|
57
|
-
One command creates the run, stores the plan, and approves it:
|
|
58
|
-
|
|
59
|
-
```
|
|
60
|
-
npx -y @bridge_gpt/mcp-server setup-epic --epic-key BAPI-405 \
|
|
61
|
-
--plan-file docs/tmp/epic-plans/<slug>/epic-plan.dag.json
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
The plan sidecar (`epic-plan.dag.json`) is produced by the `decompose-epic`
|
|
65
|
-
pipeline step. `setup-epic` validates it locally (unique ticket keys, resolvable
|
|
66
|
-
`depends_on`/edge references, acyclicity) before sending anything, so a malformed
|
|
67
|
-
plan fails legibly instead of as a bare HTTP 400.
|
|
68
|
-
|
|
69
|
-
### Branch strategy: the epic branch is the default (BAPI-1009)
|
|
70
|
-
|
|
71
|
-
The command above — no branch flag — now runs a **multi-ticket** epic on a
|
|
72
|
-
dedicated `epic/<KEY>` branch. Every child-ticket PR targets that branch, and one
|
|
73
|
-
**draft** integration PR targets the repository base branch. This applies on every
|
|
74
|
-
path, CI and `--json` included. `drive-epic` forwards both flags below to
|
|
75
|
-
`setup-epic`, so the universal entry point can select either strategy.
|
|
76
|
-
|
|
77
|
-
| You want | Pass |
|
|
78
|
-
|---|---|
|
|
79
|
-
| The epic branch on a plan with **2 or more** nodes | *nothing* — it is the default |
|
|
80
|
-
| Your own branch name, or a branch for a **single-node** plan | `--feature-branch <name>` |
|
|
81
|
-
| The old direct-to-base behavior | `--into-base` |
|
|
82
|
-
|
|
83
|
-
> **Behavior change.** Before BAPI-1009 a run with no branch flag went straight to
|
|
84
|
-
> the repository base branch. Automation that relied on that must now pass
|
|
85
|
-
> `--into-base`. A single-node plan still keeps the base branch by default, matching
|
|
86
|
-
> plain `start-tickets`. `--feature-branch` and `--into-base` together are a parse
|
|
87
|
-
> error, refused before any file read, credential resolution, or network call.
|
|
88
|
-
|
|
89
|
-
On an epic-branch run, a shadow-index verification that cannot be proven
|
|
90
|
-
immediately resolves as bounded **hold-then-park** rather than blocking the run:
|
|
91
|
-
independent tickets proceed, a dependent holds, and past the deadline it parks
|
|
92
|
-
through the ordinary `needs_human` path. The epic run's gate snapshot carries a
|
|
93
|
-
run-level `shadow_hold` object naming the reason and the recovery while that is
|
|
94
|
-
live. This addresses BAPI-993 run issue A1.
|
|
95
|
-
|
|
96
|
-
Since BAPI-754 it then calls the server's **real** validator
|
|
97
|
-
(`POST /jira/epic-runs/plan/validate`) before creating anything, so a plan the
|
|
98
|
-
server would reject — a node missing `status`, say, which the local checks do not
|
|
99
|
-
cover — no longer creates a run and incurs an automation-start charge on its way
|
|
100
|
-
to failing.
|
|
101
|
-
|
|
102
|
-
It is **idempotent**: re-running it on an epic that already has a live run reuses
|
|
103
|
-
that run rather than minting a second one. Use `--dry-run` to run that
|
|
104
|
-
authoritative validation and preview the calls without mutating anything; add
|
|
105
|
-
`--local-only` to skip the server entirely, which reports
|
|
106
|
-
`partial (local checks only)` and is never a substitute for the server's verdict.
|
|
107
|
-
|
|
108
|
-
Once the plan is approved the run becomes `active`, the server-side reconciler
|
|
109
|
-
picks it up within ~30s, and your local `executor` starts claiming jobs.
|
|
110
|
-
|
|
111
|
-
### `touched_files` — optional, but declare it anyway
|
|
112
|
-
|
|
113
|
-
**Plan-time file-overlap serialization is live and on by default.**
|
|
114
|
-
`planner_file_overlap_serialization_enabled` (BAPI-495, defaulted on by BAPI-677 /
|
|
115
|
-
migration 0126, on `config_code_repositories`) makes overlapping siblings gain a
|
|
116
|
-
`depends_on`/edge on each other *before* the DAG is validated, hashed, and stored,
|
|
117
|
-
while siblings with disjoint files stay parallel.
|
|
118
|
-
|
|
119
|
-
`touched_files` is the plan-time metadata that pass reads. It is a **best-effort
|
|
120
|
-
prediction, never reconciled against the real diff**, so BAPI-722 made it optional.
|
|
121
|
-
Three outcomes, and the difference between the first two is the point
|
|
122
|
-
(`_extract_predicted_touched_files`, `api/library/db/epic_runs.py:3195`):
|
|
123
|
-
|
|
124
|
-
| In the node | Meaning | Result |
|
|
125
|
-
|---|---|---|
|
|
126
|
-
| Field **absent** | The node **opts out** of preemptive serialization | **Accepted.** Every pair involving it is skipped (`undeclared_pairs_skipped`); no edge is added in either direction |
|
|
127
|
-
| `"touched_files": []` | A deliberate "this ticket predicts no file overlap" | Accepted as a *declared* node with an empty set |
|
|
128
|
-
| Field present but **malformed** | A non-list, or a list holding a blank, absolute, or non-string path | **400** — `EpicRunConflictError(code="VALIDATION")` |
|
|
129
|
-
|
|
130
|
-
Only omission is relaxed. Data you *did* supply must be well-formed. Paths are
|
|
131
|
-
repo-root-relative.
|
|
132
|
-
|
|
133
|
-
```json
|
|
134
|
-
{
|
|
135
|
-
"plan_version": 1,
|
|
136
|
-
"nodes": [
|
|
137
|
-
{ "ticket_key": "BAPI-405", "status": "planned", "depends_on": [],
|
|
138
|
-
"touched_files": ["api/routes/jira_api.py"] }
|
|
139
|
-
],
|
|
140
|
-
"edges": []
|
|
141
|
-
}
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
> **`setup-epic --dry-run` warns on a missing `touched_files`, and that warning is
|
|
145
|
-
> a planning-quality signal, not a predicted failure.** The server accepts the
|
|
146
|
-
> plan. What you lose is scheduling protection: an undeclared node is never
|
|
147
|
-
> pre-serialized against its siblings.
|
|
148
|
-
|
|
149
|
-
Declare it anyway. Serialization only ever **adds** edges — it never removes an
|
|
150
|
-
authored one — so hand-authored `depends_on` and `touched_files` compose safely.
|
|
151
|
-
Author both: the edges as your reviewable statement of intent, `touched_files` so
|
|
152
|
-
the scheduler can catch overlaps you did not anticipate.
|
|
153
|
-
|
|
154
|
-
> **Serialization is a scheduling optimization, not a safety mechanism.** It avoids
|
|
155
|
-
> *dispatching* file-overlapping siblings in parallel; it does not resolve a
|
|
156
|
-
> conflict once one exists. The safety net is reactive and independent of any
|
|
157
|
-
> prediction: a PR that a sibling's merge turns `CONFLICTING` is observed live and
|
|
158
|
-
> routed to `JOB_TYPE_REBASE` (`_merge_entry_failure_requires_rebase` in the
|
|
159
|
-
> reconciler). That backstop covers declared and undeclared nodes alike — which is
|
|
160
|
-
> exactly why omitting `touched_files` is safe to accept.
|
|
161
|
-
|
|
162
|
-
## The conductor CI gate — `epic-implementer-ci / gate` (BAPI-695)
|
|
163
|
-
|
|
164
|
-
**Before driving a run you want CI-gated, wire its policy.** The reconciler's
|
|
165
|
-
per-ticket `ci` gate reads **`policy_json.required_checks`** (a declared
|
|
166
|
-
`RunPolicy` key) and observes those check-run conclusions at the ticket PR's
|
|
167
|
-
**current head SHA**. If that list is empty, the observer short-circuits to
|
|
168
|
-
`PASSED` with `{"no_required_checks": true}` and **runs no CI at all** — the loop
|
|
169
|
-
merges on hand-verification only. `.github/workflows/epic-implementer-ci.yml` publishes
|
|
170
|
-
the real, always-reporting required context **`epic-implementer-ci / gate`**; set
|
|
171
|
-
|
|
172
|
-
```json
|
|
173
|
-
"policy_json": { "required_checks": ["epic-implementer-ci / gate"] }
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
on every run you want gated. The workflow existing is not enough — the context has
|
|
177
|
-
to be in the run policy.
|
|
178
|
-
|
|
179
|
-
> **Wire it at creation with `--policy-file` (BAPI-754).** There is still no
|
|
180
|
-
> `--required-checks` option, but `setup-epic --policy-file <path>` sends the
|
|
181
|
-
> **complete** run policy on the create that mints the run, so the run is born with
|
|
182
|
-
> `required_checks` already in force. A run created *without* a policy still starts
|
|
183
|
-
> with an **empty** `required_checks` and a `ci` gate that short-circuits to
|
|
184
|
-
> `PASSED` — and because `setup-epic` also approves the plan, making the run
|
|
185
|
-
> `active` and visible to the reconciler within ~30s, there is no comfortable window
|
|
186
|
-
> to fix that afterwards. Supply the policy file up front.
|
|
187
|
-
>
|
|
188
|
-
> To change the policy on a run that is already live, re-run with `--policy-file`:
|
|
189
|
-
> an identical policy is a no-op, and a divergent one is **refused** with a redacted
|
|
190
|
-
> diff unless you also pass `--replace-policy` (which applies it as a complete
|
|
191
|
-
> replacement).
|
|
192
|
-
|
|
193
|
-
> **This is NOT `done_gate_config`.** The per-run `policy_json.required_checks`
|
|
194
|
-
> read by the reconciler `ci` gate is a *different* surface from the supervisor
|
|
195
|
-
> `done_gate_config.required_ci_checks_green` documented under "Supervisor config"
|
|
196
|
-
> below. BAPI-695 wires only the former, per run; it does **not** change
|
|
197
|
-
> `done_gate_config` defaults, repo-default policy, or GitHub branch protection.
|
|
198
|
-
|
|
199
|
-
**Once gated, expect this behavior:**
|
|
200
|
-
|
|
201
|
-
- The `ci` gate now waits for `epic-implementer-ci / gate` on the ticket PR's current head.
|
|
202
|
-
A complete **red** aggregator enters the bounded `ci_fix` remediation path; a
|
|
203
|
-
**missing or still-running** current-head context stays pending (never a hard
|
|
204
|
-
fail).
|
|
205
|
-
- A **green from an older head** can never satisfy the gate after a rebase or
|
|
206
|
-
force-push — the poll is bound to the current head SHA, and the workflow's per-PR
|
|
207
|
-
concurrency cancels the superseded run so the new head publishes a fresh context.
|
|
208
|
-
- Out-of-scope PRs (forks, `dependabot/*`, non-`feature/BAPI-*`) get an intentional
|
|
209
|
-
fast green — cost control, **not** proof the suites ran.
|
|
210
|
-
|
|
211
|
-
For the exact new-run/PATCH procedure (and the **whole-object `policy_json`
|
|
212
|
-
replacement** caution — GET, merge `required_checks` locally, PATCH the whole
|
|
213
|
-
object), and the immutable case-sensitive matching of the context string, see the
|
|
214
|
-
operator runbook, **§4a "Wire the conductor CI required check"**
|
|
215
|
-
([`docs/claude/epic-implementer-v2-operator-runbook.md`](../../docs/claude/epic-implementer-v2-operator-runbook.md)).
|
|
216
|
-
|
|
217
|
-
## Conductor observability (opt-in via `--conductor`, BAPI-394)
|
|
218
|
-
|
|
219
|
-
Conductor is **off by default** — without `--conductor` no `BAPI_EPIC_IMPLEMENTER_*` env,
|
|
220
|
-
supervisor tab, or message-relay prompt is produced. With `--conductor`, a run mints
|
|
221
|
-
a single conductor `run_id` and emits one canonical `run.started` event into the
|
|
222
|
-
local conductor ledger (`~/.config/bridge/events.db`), attributing each worker by
|
|
223
|
-
`worker_id`, ticket key, and worktree path, and opens a supervisor peer tab. When
|
|
224
|
-
the selected agent is **Claude Code**, the CLI also injects a conductor lifecycle
|
|
225
|
-
hook into each created worktree's `.claude/settings.local.json` (preserving any
|
|
226
|
-
existing hooks) so the spawned session streams local `run.started` / `run.stopped` /
|
|
227
|
-
`agent.notification` (and, with `BAPI_EPIC_IMPLEMENTER_ENABLE_PRE_TOOL_USE=1`,
|
|
228
|
-
`tool.intent`) events. Per-worker conductor identity is passed only via secret-free
|
|
229
|
-
environment scoped to that one terminal/tab/session — no credentials are ever placed
|
|
230
|
-
in the env, hook command, or run metadata. Override the gate/supervisor labels with
|
|
231
|
-
`BAPI_EPIC_IMPLEMENTER_GATE_NAME` / `BAPI_EPIC_IMPLEMENTER_SUPERVISOR_MODE`. Inspect the stream
|
|
232
|
-
with `epic-implementer doctor`. Observability is best-effort: a conductor failure never
|
|
233
|
-
blocks or aborts a spawn, and `--dry-run` performs no conductor side effects.
|
|
234
|
-
(Epic dispatch always enables conductor internally, independent of this flag.)
|
|
235
|
-
|
|
236
|
-
When `--conductor` is set, the spawn boundary also injects
|
|
237
|
-
`BRIDGE_MCP_PROFILE=conductor` so each worker registers the 8 conductor/event/
|
|
238
|
-
supervisor MCP tools (a plain `start-tickets` run stays on the default `core`
|
|
239
|
-
profile). See [README → Environment Variables](../README.md#environment-variables).
|
|
240
|
-
|
|
241
|
-
### Environment-variable names and the legacy-name refusal (BAPI-1190)
|
|
242
|
-
|
|
243
|
-
Every Epic Implementer environment variable uses the `BAPI_EPIC_IMPLEMENTER_`
|
|
244
|
-
prefix, and the worker Node path is `EPIC_IMPLEMENTER_NODE_PATH`. These replaced
|
|
245
|
-
the old Conductor names by a plain token swap: `CONDUCTOR` became
|
|
246
|
-
`EPIC_IMPLEMENTER` and the rest of the name stayed the same. For example, the old
|
|
247
|
-
run-id variable is now `BAPI_EPIC_IMPLEMENTER_RUN_ID`. There are no aliases and no
|
|
248
|
-
dual reads.
|
|
249
|
-
|
|
250
|
-
**Old names are refused, not ignored.** If any variable with the old
|
|
251
|
-
`BAPI_CONDUCTOR_` prefix, or the old `CONDUCTOR_NODE_PATH`, is set, these entry
|
|
252
|
-
points refuse to start:
|
|
253
|
-
|
|
254
|
-
| Entry point | On an old name |
|
|
255
|
-
|---|---|
|
|
256
|
-
| MCP server (bare startup) and every `npx @bridge_gpt/mcp-server <subcommand>` | stderr diagnostic, exit `1`, before any spawn, network call, ledger open, or file write |
|
|
257
|
-
| `epic-implementer` bin | stderr diagnostic, exit `1`, before argv reaches the CLI |
|
|
258
|
-
| `epic-implementer-claude-hook` bin | same stderr diagnostic, **exit `0`**, no ledger event |
|
|
259
|
-
|
|
260
|
-
The hook is the one exception to the non-zero exit. A hook must never block the
|
|
261
|
-
Claude tool loop, so it stays loud but exits `0`. The two removed `conductor`
|
|
262
|
-
stub bins are not guarded; they already fail with a fixed migration message.
|
|
263
|
-
|
|
264
|
-
The diagnostic lists **every** offending key, sorted, in one message. Each line
|
|
265
|
-
gives the exact successor, a "retired — remove it" note (the old MCP shadowing
|
|
266
|
-
override), or, for an unknown old name, the generic advice: rename the prefix if
|
|
267
|
-
it is still a current setting, otherwise remove it. It prints **key names only,
|
|
268
|
-
never values**, because these variables can hold secrets. Old names copied from
|
|
269
|
-
frozen records (`docs/BAPI-*.md`, run notes) are refused the same way. Stale
|
|
270
|
-
names usually live in shell profiles, hand-edited executor service units, or CI
|
|
271
|
-
environment configuration.
|
|
272
|
-
|
|
273
|
-
**Version skew.** A worker environment injected by an **older** published package
|
|
274
|
-
still carries the old names. A **newer** TypeScript process started inside it (for
|
|
275
|
-
example the MCP server launched through `mcp-invoke`) will refuse to start. This
|
|
276
|
-
is intended: it is the loud outcome, not a silent half-configured worker.
|
|
277
|
-
|
|
278
|
-
- **Remedy:** re-spawn the worker so the launcher and the process it starts run
|
|
279
|
-
the same package version.
|
|
280
|
-
- **If that does not fix it:** send the MCP package owner the launcher version,
|
|
281
|
-
the process version, and the offending **key names only** — never their values.
|
|
282
|
-
|
|
283
|
-
The executor's worker-environment filter rejects both whole prefixes and both
|
|
284
|
-
node-path spellings, so an old ambient name never reaches a worker it spawns.
|
|
285
|
-
|
|
286
|
-
**No Python guard.** No production Python reads these variables, so they are
|
|
287
|
-
inert there, and no broad Python `BAPI_*` guard is added. They are refused when
|
|
288
|
-
inherited by a TypeScript child or MCP process. The Python dead-man variables
|
|
289
|
-
(`CONDUCTOR_DEAD_MAN_ONLY`, `CONDUCTOR_DEADMAN_*`) are a separate exact-name
|
|
290
|
-
contract owned by BAPI-1231.
|
|
291
|
-
|
|
292
|
-
## `epic-implementer install-git-hooks` (BAPI-395)
|
|
293
|
-
|
|
294
|
-
Installs local git hooks that opportunistically emit conductor git/PR/CI events into
|
|
295
|
-
the local ledger:
|
|
296
|
-
|
|
297
|
-
```
|
|
298
|
-
epic-implementer install-git-hooks [--json]
|
|
299
|
-
```
|
|
300
|
-
|
|
301
|
-
The installed hooks are **local, unversioned, opportunistic, and bypassable**: they
|
|
302
|
-
live in the worktree's git hooks directory (resolved via
|
|
303
|
-
`git rev-parse --git-common-dir`), insert only a clearly-delimited managed block
|
|
304
|
-
(preserving any existing user hook content), launch the producer **detached in the
|
|
305
|
-
background** so a commit or ref update is never blocked, and tolerate every failure
|
|
306
|
-
(`|| true`). A directory that is not a git worktree, or an existing hook that looks
|
|
307
|
-
binary/unsafe, is left untouched and reported as a **degraded optional capability** —
|
|
308
|
-
never a fatal error. The hooks installed are `post-commit` (emits
|
|
309
|
-
`git.commit_created`) and `reference-transaction` (emits `worktree.changed` for
|
|
310
|
-
committed ref updates).
|
|
311
|
-
|
|
312
|
-
Missing hooks do **not** prevent PR/CI gate evaluation — `epic-implementer doctor` reads
|
|
313
|
-
hook presence and managed-snippet status **read-only** (a new `git hooks` section /
|
|
314
|
-
`git_hooks` JSON object alongside the ledger report), and the reconciler drives CI
|
|
315
|
-
polling and gate evaluation regardless of whether hooks are installed.
|
|
316
|
-
|
|
317
|
-
### Durable hook installation (BAPI-1290)
|
|
318
|
-
|
|
319
|
-
A managed hook embeds one absolute entry path and runs it on every commit for as
|
|
320
|
-
long as the hook file lives. So the installer embeds only a **qualified persistent
|
|
321
|
-
runtime** and refuses anything that can disappear under it.
|
|
322
|
-
|
|
323
|
-
**Supported installation.** Install the package persistently, then install the hooks
|
|
324
|
-
from that install:
|
|
325
|
-
|
|
326
|
-
```bash
|
|
327
|
-
npm i -g @bridge_gpt/mcp-server
|
|
328
|
-
epic-implementer install-git-hooks
|
|
329
|
-
```
|
|
330
|
-
|
|
331
|
-
A user-owned persistent prefix (`npm i -g --prefix <dir>`, with `<dir>/bin` on PATH)
|
|
332
|
-
works the same way. The hook embeds the fixed package entry
|
|
333
|
-
`<prefix>/lib/node_modules/@bridge_gpt/mcp-server/build/epic-implementer-bin.js`
|
|
334
|
-
(POSIX single-quoted, so spaces and shell metacharacters stay literal) and launches it
|
|
335
|
-
with `node` from PATH. It never embeds npm's `.bin` shim and never pins
|
|
336
|
-
`process.execPath`.
|
|
337
|
-
|
|
338
|
-
Running the installer through `npx` is allowed only as a front door: it looks up an
|
|
339
|
-
**already installed** persistent global package with a bounded, offline
|
|
340
|
-
`npm root -g` (at install time only) and embeds that package's entry. When no
|
|
341
|
-
qualified persistent install exists, it refuses.
|
|
342
|
-
|
|
343
|
-
**What "qualified" means.** Before touching any hook, the installer checks, read-only:
|
|
344
|
-
|
|
345
|
-
| Check | Refusal classification |
|
|
346
|
-
|---|---|
|
|
347
|
-
| The entry is not inside the prunable `_npx` cache | `ephemeral-cache` |
|
|
348
|
-
| The canonical entry is a regular `build/epic-implementer-bin.js` | `missing` |
|
|
349
|
-
| It is not the retired `conductor-bin.js` name | `legacy-name` |
|
|
350
|
-
| It sits at `…/node_modules/@bridge_gpt/mcp-server/build/`, and the adjacent `package.json` has that `name`, a compatible `version` (`>=0.2.0 <1.0.0`), and `bin.epic-implementer` → `./build/epic-implementer-bin.js` | `unqualified-runtime` |
|
|
351
|
-
| The platform is macOS or Linux | `unqualified-runtime` |
|
|
352
|
-
| `node` from PATH launches the entry's private probe within 15 seconds | `node-unavailable` / `unknown` |
|
|
353
|
-
| That Node is an even LTS line ≥ 20 | `unqualified-runtime` |
|
|
354
|
-
| `better-sqlite3` loads for that Node | `native-unavailable` |
|
|
355
|
-
|
|
356
|
-
A refusal exits 1, prints only the classification and a fixed action (never a path or
|
|
357
|
-
error text), and leaves both existing hooks byte-for-byte and mode-for-mode unchanged.
|
|
358
|
-
Hooks are optional observability: a refusal is **not** an engine-admission failure and
|
|
359
|
-
not a setup-permission stop.
|
|
360
|
-
|
|
361
|
-
**Qualified matrix.** Recorded evidence, not inference:
|
|
362
|
-
|
|
363
|
-
| OS | Install | Node | Native | Status |
|
|
364
|
-
|---|---|---|---|---|
|
|
365
|
-
| macOS 26 (Darwin 25.6, arm64) | persistent `npm i -g --prefix` (npm 11.13.0) | v24.16.0 (ABI 137) | better-sqlite3 13.0.3 | qualified by `packaged-git-hooks-lifetime.test.ts` |
|
|
366
|
-
| Linux | persistent `npm i -g` | even LTS ≥ 20 | better-sqlite3 13.x | accepted by policy; **not yet locally qualified** |
|
|
367
|
-
| Windows | any | any | any | **unqualified** — always refused |
|
|
368
|
-
| Any | `npx` cache only, version-manager switches (nvm/fnm/volta), other layouts | — | — | **unqualified** |
|
|
369
|
-
|
|
370
|
-
A package-layout change or a Node/native ABI change must be requalified by re-running
|
|
371
|
-
`npm run test:hooks-lifetime` before this table changes.
|
|
372
|
-
|
|
373
|
-
**Publication.** The installer plans both hooks before writing either. It refuses a
|
|
374
|
-
symlinked, non-regular, unreadable, or binary hook with a per-hook reason and changes
|
|
375
|
-
nothing. Each hook is then written to a same-directory temporary file with its original
|
|
376
|
-
mode plus only the missing execute bits, re-checked against its planned snapshot, and
|
|
377
|
-
renamed atomically over the target. A hook edited in the meantime is left untouched
|
|
378
|
-
(`intervening-edit`). Two renames are not a transaction: if one hook publishes and the
|
|
379
|
-
other does not, the result is `PARTIAL` (exit 1), the published hook is not rolled
|
|
380
|
-
back, and re-running the installer converges to exactly one managed block per hook.
|
|
381
|
-
User content before and after the managed block is preserved.
|
|
382
|
-
|
|
383
|
-
**Behavior changes to note.** A symlinked hook is refused rather than written
|
|
384
|
-
through; one unsafe hook (symlinked, unreadable, binary, or not a regular file)
|
|
385
|
-
blocks installing both; a source-checkout build is refused as
|
|
386
|
-
`unqualified-runtime`; and on Windows `doctor` always reports `unqualified-runtime`.
|
|
387
|
-
A persistent install older than this change cannot answer the runtime probe and is
|
|
388
|
-
reported `unknown` — upgrade it with `npm i -g @bridge_gpt/mcp-server@latest`.
|
|
389
|
-
|
|
390
|
-
**Diagnosis.** `epic-implementer doctor` (and `install doctor`) report each hook's
|
|
391
|
-
`state` and `usable` verdict read-only: no npm lookup, no download, no hook rewrite,
|
|
392
|
-
no service start, no ledger write. Its only subprocess is the bounded runtime probe.
|
|
393
|
-
A hook that exists but points into the npx cache (`ephemeral-cache`), at a removed
|
|
394
|
-
install (`missing`), at the retired name (`legacy-name`), or at a runtime whose native
|
|
395
|
-
binding no longer loads (`native-unavailable`) is **unavailable**, never healthy. The
|
|
396
|
-
raw embedded path appears only in `--json` output (`embedded_bin_path`).
|
|
397
|
-
|
|
398
|
-
**Repairing old cache-backed hooks.** Hooks installed from `npx` before BAPI-1290
|
|
399
|
-
point into the npx cache. Doctor reports them as `ephemeral-cache`. To repair:
|
|
400
|
-
install persistently (`npm i -g @bridge_gpt/mcp-server`), then re-run
|
|
401
|
-
`epic-implementer install-git-hooks` (or `install conductor` and accept the
|
|
402
|
-
local-observability consent). The managed block is replaced in place; user content,
|
|
403
|
-
modes, and the local ledger are preserved.
|
|
404
|
-
|
|
405
|
-
**Upgrades and rollback.** `npm i -g @bridge_gpt/mcp-server@<version>` replaces the
|
|
406
|
-
package in place, so the embedded path is unchanged and hooks keep working **without
|
|
407
|
-
being rewritten**. While the replacement is in progress, or if it is interrupted,
|
|
408
|
-
optional delivery may be temporarily unavailable and doctor reports it as degraded;
|
|
409
|
-
completing the install (or re-running it) restores delivery. To roll back, install the
|
|
410
|
-
previous compatible version the same way, then run `epic-implementer doctor` and, if
|
|
411
|
-
it reports degraded, `epic-implementer install-git-hooks`. Removing the global package
|
|
412
|
-
deliberately is outside the availability guarantee but stays diagnosable (`missing`).
|
|
413
|
-
|
|
414
|
-
**What is not promised.** Events that occur while hook delivery is unavailable are
|
|
415
|
-
**lost and not replayed**; the ledger is not complete for those intervals, and anyone
|
|
416
|
-
relying on a missed observation must reconcile it from Git itself. Git commits and ref
|
|
417
|
-
updates are never blocked or slowed by an unavailable runtime (the hook is detached and
|
|
418
|
-
ends in `|| true`), and engine admission never depends on hooks. No run-speed
|
|
419
|
-
improvement is claimed. Activating a hook build against a live or shared control plane
|
|
420
|
-
remains a separately authorized operation.
|
|
421
|
-
|
|
422
|
-
## Supervisor config — where the done gate and auto-merge live
|
|
423
|
-
|
|
424
|
-
> **These are not config fields.** They used to be `conductor_done_gate` and
|
|
425
|
-
> `conductor_auto_merge_enabled` on the generic config-field route; BAPI-438 moved
|
|
426
|
-
> them onto dedicated supervisor endpoints. Setting them via
|
|
427
|
-
> `PUT /jira/config-field/...` returns **HTTP 400 "Invalid config field"** — that
|
|
428
|
-
> rejection is correct, not a bug. Use the endpoints below.
|
|
429
|
-
|
|
430
|
-
Two scopes exist for each: a per-repo **project default**, and a per-epic override.
|
|
431
|
-
|
|
432
|
-
| Setting | Endpoint (project default) |
|
|
433
|
-
| --- | --- |
|
|
434
|
-
| `done_gate_config` | `PUT /jira/epic-runs/supervisor-setup/defaults/?repo_name=<repo>` |
|
|
435
|
-
| `auto_merge_enabled`, `merge_approval_required` | `PUT /jira/epic-runs/supervisor-config/defaults/?repo_name=<repo>` (see note below) |
|
|
436
|
-
|
|
437
|
-
Since BAPI-1288 every project-default write is protected. A write that removes a
|
|
438
|
-
configured requirement needs a preview (`POST …/defaults/preview/`) and the exact
|
|
439
|
-
approval it returns. Turning `auto_merge_enabled` **on**, or dropping a required
|
|
440
|
-
`merge_approval_required`, on the project default is refused (`409 NON_RELAXABLE`)
|
|
441
|
-
whatever the approval. That is an operator decision; see the account-settings
|
|
442
|
-
operator runbook (`docs/claude/account-settings-operator-runbook.md`).
|
|
443
|
-
|
|
444
|
-
Swap `…/defaults/` for `…/runs/{epic_key}/…` to scope a setting to one epic. The
|
|
445
|
-
matching `GET` on each returns the effective value, with a `source` of `epic`,
|
|
446
|
-
`project_default`, or `none` (nothing configured — fails closed).
|
|
447
|
-
|
|
448
|
-
### `done_gate_config`
|
|
449
|
-
|
|
450
|
-
Defines the done gate. It supports exactly one condition,
|
|
451
|
-
`required_ci_checks_green`:
|
|
452
|
-
|
|
453
|
-
```json
|
|
454
|
-
{
|
|
455
|
-
"enabled": true,
|
|
456
|
-
"conditions": [
|
|
457
|
-
{ "type": "required_ci_checks_green", "required_checks": ["build", "test"] }
|
|
458
|
-
]
|
|
459
|
-
}
|
|
460
|
-
```
|
|
461
|
-
|
|
462
|
-
`gate.met` is emitted (exactly once per `repo + pr_number + head_sha + effective
|
|
463
|
-
config`) only when every listed required check is present, complete, and green for
|
|
464
|
-
the bound PR head SHA. The gate **fails closed**: an unset, disabled (`enabled` not
|
|
465
|
-
strictly `true`), malformed, empty, or unsupported config emits no `gate.met`.
|
|
466
|
-
|
|
467
|
-
### `auto_merge_enabled` (C6 conditional auto-merge)
|
|
468
|
-
|
|
469
|
-
When a worker's PR meets the done gate (`gate.met`), the supervisor can autonomously
|
|
470
|
-
merge it — but **only** when the repo has explicitly opted in.
|
|
471
|
-
`auto_merge_enabled` on the supervisor-config endpoint is the opt-in switch. On the
|
|
472
|
-
project default it can no longer be turned on through the API (BAPI-1288); an
|
|
473
|
-
operator sets it, as the note above describes. A per-epic override is unchanged.
|
|
474
|
-
|
|
475
|
-
**Auto-merge is disabled by default.** Behavior:
|
|
476
|
-
|
|
477
|
-
- **Disabled / unset / unconfigured → dry-run.** Anything other than an explicit
|
|
478
|
-
`true` — including a repo with no supervisor config at all (`source: "none"`) —
|
|
479
|
-
fails **closed**: the supervisor records a `merge.dry_run` event and **no PR is
|
|
480
|
-
ever merged**.
|
|
481
|
-
- **Enabled → autonomous merge** when the gate is met and the deterministic guards
|
|
482
|
-
pass.
|
|
483
|
-
- **Kill-switch.** Set `auto_merge_enabled` to `false` to immediately stop
|
|
484
|
-
autonomous merges. The protected merge endpoint **independently re-enforces** the
|
|
485
|
-
flag, so even a conductor that calls it cannot merge while the flag is off.
|
|
486
|
-
|
|
487
|
-
Merge authority is **deterministic code, never an LLM**. The deterministic guards,
|
|
488
|
-
all bound to **PR number + expected head SHA (never a branch name)**:
|
|
489
|
-
|
|
490
|
-
- the per-repo enablement flag (off → dry-run),
|
|
491
|
-
- the PR is still open,
|
|
492
|
-
- the merge is bound to the PR number plus the expected head SHA — **head-SHA drift
|
|
493
|
-
between gate evaluation and merge aborts the merge**,
|
|
494
|
-
- required CI checks are **revalidated green immediately before merge**.
|
|
495
|
-
|
|
496
|
-
Idempotency is crash-safe and race-safe: a TTL lease keyed by the deterministic
|
|
497
|
-
action key `merge:{repo}:{pr}:{head_sha}:{gate}` is claimed before acting, and an
|
|
498
|
-
existing `merge.succeeded` for that key is terminal — the supervisor never
|
|
499
|
-
double-merges across a crash/restart or two racing instances. The conductor never
|
|
500
|
-
holds VCS write credentials: it calls the protected Bridge API endpoint
|
|
501
|
-
`POST /vcs/pull-requests/{pr_number}/merge`, which owns the privileged merge, and
|
|
502
|
-
records the returned `merge.dry_run` / `merge.attempted` / `merge.succeeded` /
|
|
503
|
-
`merge.failed` / `merge.pending_approval` events into the local ledger.
|
|
504
|
-
`merge.failed` is **retryable** (a drifted head SHA produces a new action key);
|
|
505
|
-
`merge.pending_approval` is **nonterminal** — the worker remains active until a human
|
|
506
|
-
redeems the approval token and the server returns `merge.succeeded`.
|
|
507
|
-
**`merge.succeeded` is the only terminal merge event.** The local SQLite conductor
|
|
508
|
-
store uses schema version 5 (BAPI-413) to accommodate the `merge.pending_approval`
|
|
509
|
-
type in the `events.type` CHECK constraint.
|
|
510
|
-
|
|
511
|
-
**Who performs the write (BAPI-832).** "Never holds VCS write credentials" above
|
|
512
|
-
describes the *credential*, not the *typing*, and it remains true everywhere. Two
|
|
513
|
-
paths now perform the provider write with the operator's local `gh` rather than
|
|
514
|
-
with the GitHub App installation token, because GitHub's merge endpoint requires
|
|
515
|
-
`contents: write` and the Bridge GPT App installation is `contents: read`:
|
|
516
|
-
|
|
517
|
-
- the executor's `merge` job, via `makeLocalMergeExecutor` — the arrangement
|
|
518
|
-
BAPI-572 locked in; and
|
|
519
|
-
- the `merge_pull_request` MCP tool, which since BAPI-832 sends
|
|
520
|
-
`execution: "local"`, receives a non-terminal `approved_for_local_execution`
|
|
521
|
-
from the route above, merges through that same `conductor/local-merge.ts` seam,
|
|
522
|
-
and reports the outcome to
|
|
523
|
-
`POST /vcs/pull-requests/{pr_number}/merge/complete`.
|
|
524
|
-
|
|
525
|
-
In both, the server still owns every decision — the guards, the lease, and the
|
|
526
|
-
terminal ledger row — and neither path holds a credential of its own; they borrow
|
|
527
|
-
the operator's `gh` session. Mode is `merge_execution` on `epic_supervisor_config`
|
|
528
|
-
(`local` by default, `server` for installations whose App holds
|
|
529
|
-
`contents: write`). A caller that dies mid-merge is recovered by lease expiry, not
|
|
530
|
-
by a new mechanism: the next call re-runs the whole tail, and the provider re-read
|
|
531
|
-
reports `already_merged` if the merge landed.
|
|
532
|
-
|
|
533
|
-
## Durable execution environment hardening (BAPI-527)
|
|
534
|
-
|
|
535
|
-
Worker ledger operations are made **reliable again** — this is a robustness fix for
|
|
536
|
-
the worker-facing path, **not** a new primary completion backstop. The done gate and
|
|
537
|
-
supervisor remain the authority; this change only stops the worker's own ledger calls
|
|
538
|
-
from failing.
|
|
539
|
-
|
|
540
|
-
**The invariant.** Worker-facing ledger operations run through `conductor-bin.js`
|
|
541
|
-
executed **under the captured conductor Node** (`EPIC_IMPLEMENTER_NODE_PATH`). **The worker
|
|
542
|
-
Node must never load `better-sqlite3`.** Dispatch captures the conductor process's
|
|
543
|
-
own Node executable and threads it to each worker as `EPIC_IMPLEMENTER_NODE_PATH`, paired
|
|
544
|
-
with `BAPI_EPIC_IMPLEMENTER_CLI_FILE` (the packaged `conductor-bin.js` path). The
|
|
545
|
-
worker-side boundary (`src/conductor/worker-ledger-cli.ts`) shells out with a fixed
|
|
546
|
-
**list argv** via `execFile` (never `shell: true`, never a command string) and
|
|
547
|
-
passes event JSON over **stdin** (`--data-json-stdin`), so raw payloads/secrets
|
|
548
|
-
never enter the process argument list. Conductor-**owned** processes (the CLI,
|
|
549
|
-
supervisor, and epic runtimes) keep calling `store.ts` directly — they legitimately
|
|
550
|
-
own the native load.
|
|
551
|
-
|
|
552
|
-
**Fail loud, never fall back.** A missing/invalid `EPIC_IMPLEMENTER_NODE_PATH` or absent
|
|
553
|
-
`BAPI_EPIC_IMPLEMENTER_CLI_FILE` raises the typed `ConductorLedgerSubprocessRuntimeError`,
|
|
554
|
-
surfaced to callers as the `LEDGER_SUBPROCESS_RUNTIME_UNAVAILABLE` (503) envelope
|
|
555
|
-
with a fixed, path-free, secret-free `details: { env_var, reason }` bag
|
|
556
|
-
(`reason ∈ missing | invalid | cli_missing | spawn_failed | malformed_stdout`). The
|
|
557
|
-
boundary **never** falls back to the worker's `process.execPath` — doing so would
|
|
558
|
-
re-introduce the native load into the worker Node, the exact failure this removes.
|
|
559
|
-
`EPIC_IMPLEMENTER_NODE_PATH` is **non-secret operational metadata**: it is inherited from
|
|
560
|
-
the worker shell env injected at the spawn boundary and is **not** persisted into
|
|
561
|
-
`.mcp.json`, which stays intentionally env-free.
|
|
562
|
-
|
|
563
|
-
**Rejected alternatives.**
|
|
564
|
-
|
|
565
|
-
- **Local HTTP sidecar** (a long-lived ledger daemon the worker talks to over a
|
|
566
|
-
port) — rejected: port conflicts, zombie-process lifecycle management, capability-
|
|
567
|
-
token handling, and a large failure surface, all disproportionate for a
|
|
568
|
-
file-backed local ledger.
|
|
569
|
-
- **WASM / pure-JS SQLite in the worker** — rejected: it replaces the proven
|
|
570
|
-
`better-sqlite3` WAL / synchronous-performance behavior and is a poor fit for a
|
|
571
|
-
database file shared by multiple processes (the conductor Node and any workers).
|
|
572
|
-
|
|
573
|
-
### Three protective guarantees (operator view)
|
|
574
|
-
|
|
575
|
-
BAPI-527 adds three independent reliability guarantees. Each is a protective
|
|
576
|
-
default; none changes the interactive (non-conductor) `start-tickets` contract.
|
|
577
|
-
|
|
578
|
-
1. **Ledger operations are isolated from the worker path.** Worker-facing ledger
|
|
579
|
-
operations run in a subprocess under `EPIC_IMPLEMENTER_NODE_PATH` (details above), so a
|
|
580
|
-
Node ABI mismatch degrades a worker convenience, never the whole session.
|
|
581
|
-
|
|
582
|
-
2. **Dispatch does not alter your branch.** Unattended/epic dispatch is
|
|
583
|
-
**non-mutating**: it resolves the base with a fetch-only
|
|
584
|
-
`origin/<base>` lookup and cuts new worktrees from that fetched SHA. It never
|
|
585
|
-
checks out, fast-forwards (`git merge --ff-only`), force-moves
|
|
586
|
-
(`git branch --force`), or stashes the operator's live checkout. A **live-source
|
|
587
|
-
guard** additionally refuses dispatch (or, with an explicit override, warns
|
|
588
|
-
loudly) when the conductor's base repo path equals the operator's live
|
|
589
|
-
dev-server checkout:
|
|
590
|
-
- `BAPI_EPIC_IMPLEMENTER_LIVE_SOURCE_PATH` — the operator's live dev-server checkout
|
|
591
|
-
path to protect.
|
|
592
|
-
- `BAPI_EPIC_IMPLEMENTER_ALLOW_LIVE_SOURCE_DISPATCH` — truthy (`1`/`true`/`yes`/`on`)
|
|
593
|
-
override that downgrades a collision from fatal to a loud warning.
|
|
594
|
-
The read-only `doctor` command surfaces the guard state
|
|
595
|
-
(`not-configured` / `safe` / `collision`) without mutating anything.
|
|
596
|
-
|
|
597
|
-
3. **Parse runs in a separate process.** The post-merge repository re-index runs
|
|
598
|
-
out-of-process via `asyncio.create_subprocess_exec`, so the FastAPI event loop
|
|
599
|
-
stays responsive to concurrent dispatch calls while a CPU-bound parse runs. The
|
|
600
|
-
ticket enters **`parse_pending`** after merge and folds to **`done`** only when
|
|
601
|
-
`/parse-status` reports terminal success; a permanent parse failure (or the
|
|
602
|
-
`max_wait_ms` budget being exhausted) folds it to **`blocked`** with a sanitized
|
|
603
|
-
reason and never silently wedges the epic. Parse status values are `idle`,
|
|
604
|
-
`queued`, `in_progress`, `succeeded`, and `failed`; terminal failure reasons are
|
|
605
|
-
bounded and sanitized (no stderr, tracebacks, argv, or secrets).
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "idea-to-pr",
|
|
3
|
-
"description": "Drive an idea end-to-end: create ticket(s) (idea-to-ticket), review each ticket (review-ticket fan-out), then hand off to the /start-tickets CLI seam to spawn worktrees.",
|
|
4
|
-
"variables": [
|
|
5
|
-
"idea",
|
|
6
|
-
"auto_approve",
|
|
7
|
-
"scheduled_at",
|
|
8
|
-
"max_children",
|
|
9
|
-
"allow_duplicate",
|
|
10
|
-
"agent"
|
|
11
|
-
],
|
|
12
|
-
"stages": [
|
|
13
|
-
{
|
|
14
|
-
"pipeline_name": "idea-to-ticket",
|
|
15
|
-
"description": "Convert the idea into one or more Jira tickets.",
|
|
16
|
-
"variables": {
|
|
17
|
-
"idea": "{idea}",
|
|
18
|
-
"max_children": "{max_children}",
|
|
19
|
-
"allow_duplicate": "{allow_duplicate}",
|
|
20
|
-
"auto_approve_external": "{auto_approve}"
|
|
21
|
-
},
|
|
22
|
-
"outputs": {
|
|
23
|
-
"child_ticket_keys": "child_ticket_keys",
|
|
24
|
-
"epic_parent_key": "epic_parent_key",
|
|
25
|
-
"created_ticket_keys": "created_ticket_keys"
|
|
26
|
-
}
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
"pipeline_name": "review-ticket",
|
|
30
|
-
"description": "Review each created ticket (one child pipeline run per ticket).",
|
|
31
|
-
"fan_out_input": "child_ticket_keys",
|
|
32
|
-
"fan_out_variable": "ticket_key",
|
|
33
|
-
"variables": {
|
|
34
|
-
"ticket_key": "{ticket_key}"
|
|
35
|
-
},
|
|
36
|
-
"outputs": {
|
|
37
|
-
"reviewed_ticket_keys": "reviewed_ticket_keys"
|
|
38
|
-
}
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
"pipeline_name": "start-tickets",
|
|
42
|
-
"description": "Hand off the reviewed tickets to the /start-tickets CLI seam (agent-task; not a server pipeline).",
|
|
43
|
-
"fan_out_input": "reviewed_ticket_keys",
|
|
44
|
-
"outputs": {
|
|
45
|
-
"started_ticket_keys": "started_ticket_keys"
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
]
|
|
49
|
-
}
|