@bridge_gpt/mcp-server 0.2.27 → 0.2.29
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 +43 -28
- package/build/agent-registry.js +5 -0
- package/build/commands.generated.js +2 -2
- package/build/conductor/bridge-api-client.js +98 -1
- package/build/conductor/epic-reconcile.js +28 -0
- package/build/conductor/epic-runtime.js +28 -1
- package/build/conductor-bin.js +2 -2
- package/build/connect-github-api.js +9 -0
- package/build/connect-github.js +10 -0
- package/build/doctor.js +2 -2
- package/build/env-flags.js +23 -0
- package/build/index.js +79 -32
- package/build/install-bridge.js +602 -187
- package/build/mcp-host-targets.js +12 -0
- package/build/readme.generated.js +1 -1
- package/build/setup-epic.js +198 -4
- package/build/start-tickets.js +44 -13
- package/build/tool-surface-gating.js +13 -2
- package/build/version.generated.js +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -31,11 +31,14 @@ npx -y @bridge_gpt/mcp-server@latest install-bridge
|
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
Run bare like that in a terminal and it starts by asking
|
|
34
|
-
**`Do you have a Bridge API key? [Y/n]`**:
|
|
34
|
+
**`Do you have a Bridge API key or invite? [Y/n]`**:
|
|
35
35
|
|
|
36
36
|
- **Yes** (or just press Enter) — the existing-key flow. It asks for your **API key**
|
|
37
37
|
(generate one on the Bridge API web UI **Security** page) and a **repo name**
|
|
38
|
-
matching your server-side registration; everything else is derived.
|
|
38
|
+
matching your server-side registration; everything else is derived. A
|
|
39
|
+
`bapi_inv_…` credential entered here instead of a full API key is automatically
|
|
40
|
+
detected and redeemed as a **bootstrap invite** — it creates a brand-new project
|
|
41
|
+
and mints your admin API key rather than looking up an existing repository.
|
|
39
42
|
- **No** — the **self-serve** flow. It asks for an **email**, then a name for your new
|
|
40
43
|
Bridge project, and creates the workspace and your own admin API key for you. No
|
|
41
44
|
account, no key, and no invite needed beforehand. Same as passing
|
|
@@ -96,12 +99,14 @@ works as a fallback.)
|
|
|
96
99
|
(`~/.config/bridge/credentials.json`, target `bapi:<repo>`) so shell-spawned
|
|
97
100
|
tooling (e.g. `start-tickets`) can resolve it.
|
|
98
101
|
5. **Opens a fresh agent session** that runs `/install-bridge` to derive and apply
|
|
99
|
-
the remaining config fields from your codebase, presents
|
|
100
|
-
(
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
the remaining config fields from your codebase, presents a concise capability
|
|
103
|
+
report ("What Bridge can help with"), and recommends `/learn-repository` as the
|
|
104
|
+
next step. It does not chain into running `/learn-repository` itself — that's
|
|
105
|
+
your next explicit invocation. There is no indexing question anywhere: indexing
|
|
106
|
+
starts automatically once the repository reaches full parse readiness (VCS
|
|
107
|
+
credentials, the code index prerequisites, and project description), so you
|
|
108
|
+
never need to ask for it or run `/parse-repository` yourself as part of
|
|
109
|
+
onboarding.
|
|
105
110
|
|
|
106
111
|
In this **existing-key** flow the only inputs are an **API key** and a **repo name**
|
|
107
112
|
(everything else is derived). Resolution order:
|
|
@@ -110,7 +115,11 @@ In this **existing-key** flow the only inputs are an **API key** and a **repo na
|
|
|
110
115
|
prompt. Generate one first on the Bridge API web UI **Security** page (see
|
|
111
116
|
[Generate an API Key](#2-generate-an-api-key)); in this flow the command consumes
|
|
112
117
|
a key, it never mints one — **`--email` and `--invite` are the two exceptions**
|
|
113
|
-
(below), and each mints your first key.
|
|
118
|
+
(below), and each mints your first key. All three of `--api-key`, `BAPI_API_KEY`,
|
|
119
|
+
and the hidden prompt also accept a bootstrap-invite value (`bapi_inv_…`) —
|
|
120
|
+
detected automatically and redeemed the same way `--invite` is, skipping
|
|
121
|
+
repository lookup entirely. `--invite` and `--email` remain the preferred,
|
|
122
|
+
explicit entry points for a new project. The key is **never printed or logged**.
|
|
114
123
|
- **Repo name:** `--repo <name>` and `BAPI_REPO_NAME` remain the deterministic
|
|
115
124
|
short-circuits and compatibility fallbacks — when either is set it is used
|
|
116
125
|
directly, with no network round-trip. When **neither** is set, a compatible
|
|
@@ -139,7 +148,7 @@ shown**.
|
|
|
139
148
|
The email may instead come from the `BAPI_SIGNUP_EMAIL` environment variable or a
|
|
140
149
|
**visible** interactive prompt (email is not a secret, so it is echoed as you type —
|
|
141
150
|
unlike the API key and the invite token, which use a hidden prompt). That prompt is
|
|
142
|
-
what answering **no** to `Do you have a Bridge API key? [Y/n]` on a bare run reaches,
|
|
151
|
+
what answering **no** to `Do you have a Bridge API key or invite? [Y/n]` on a bare run reaches,
|
|
143
152
|
so `install-bridge --email you@example.com` and a bare `install-bridge` + "no" land
|
|
144
153
|
in the same place. The email is still **never written to a log line**. No email
|
|
145
154
|
verification is performed and no message is sent to the address — it only labels your
|
|
@@ -387,17 +396,10 @@ These features are useful for most tickets.
|
|
|
387
396
|
- **How to use it:** `/start-tickets BAPI-248 BAPI-250` (see [CLI Subcommands](#cli-subcommands) for the full flag table and cross-platform behavior).
|
|
388
397
|
- **Flags:** `--auto` skip the approval gates · `--base-branch <branch>` branch off something other than the default · `--workflow implement|review-and-implement` selects which slash command each spawned worktree runs (default `implement`, byte-identical to today; `review-and-implement` runs `/review-ticket` inline then, after a per-ticket halt gate, hands off to a **fresh** `/implement-ticket` session reusing the same worktree) · `--rounds=1|2` review-only, forwarded to the review phase, valid only with `--workflow review-and-implement` · `--tier cheap|basic|premium` coarse model-routing override (see [CLI Subcommands](#cli-subcommands)).
|
|
389
398
|
|
|
390
|
-
**2b. Review and Start**
|
|
391
|
-
- **What it does:** Spawns one worktree per ticket; each session reviews the ticket inline and, after a per-ticket human proceed/halt gate, hands off to a **fresh implementation session** that reuses the same worktree — review and implementation run in two separate agent contexts, not one shared session.
|
|
392
|
-
- **When it's useful:** (Refinement | Implementation | Automation) The **recommended front door** for "review these tickets, then implement the ones that pass," starting from existing ticket keys (unlike `/full-automation`, which only accepts an idea).
|
|
393
|
-
- **How to use it:** `/review-and-start BAPI-248 BAPI-250` (single or multiple keys flow through the identical code path).
|
|
394
|
-
- **Flags:** `--auto` a single chain-level flag that auto-approves both the review and the implementation phase of every spawned session · `--rounds=1|2` forwarded to the review phase · `--agent`, `--base-branch`, `--max-parallel`, `--dry-run` mirror `/start-tickets`.
|
|
395
|
-
- Under the hood, this is a thin shim over `start-tickets --workflow review-and-implement <KEYS>` — the lower-level launcher seam documented above; the review→gate→fresh-implementation-handoff logic lives in the spawned `/review-and-implement` session, never in this command or the CLI. On approval, that session reuses its review-time model tier by passing it to the fresh implementation launcher via `--tier`.
|
|
396
|
-
|
|
397
399
|
**3. Council**
|
|
398
|
-
- **What it does:** Fans your problem out to two different LLMs and returns their approaches directly. Runs in one of four modes, selected via `mode`: **`technical`** (default — implementation/architecture approaches), **`design`** (UI/UX and visual direction), **`discovery`** (stakeholder discovery questions for early/vague tasks, grouped into `Technical Discovery Questions` and `Business / Stakeholder Discovery Questions` and tagged `[HUMAN]`/`[CODE]`/`[TICKET]`), or **`general`** (brief-driven
|
|
399
|
-
- **When it's useful:** (Architecture | Refinement) Early, when you want a spread of approaches — `technical` for *how to build it*, `design` for *how it should look*, `discovery` for *what we still need to figure out* before a real ticket exists, `general` for a quick brief-driven
|
|
400
|
-
- **How to use it:** ask your agent to
|
|
400
|
+
- **What it does:** Fans your problem out to two different LLMs and returns their approaches directly. Runs in one of four modes, selected via `mode`: **`technical`** (default — implementation/architecture approaches), **`design`** (UI/UX and visual direction), **`discovery`** (stakeholder discovery questions for early/vague tasks, grouped into `Technical Discovery Questions` and `Business / Stakeholder Discovery Questions` and tagged `[HUMAN]`/`[CODE]`/`[TICKET]`), or **`general`** (brief-driven ideation from your task description and concerns alone). `technical` and `discovery` are codebase-grounded — they retrieve from the repository index and need a successfully indexed repo. `general` needs no code index at all, so it works immediately after install, before `/parse-repository` has ever run. The legacy boolean `design=true` still works and maps to `mode: "design"`.
|
|
401
|
+
- **When it's useful:** (Architecture | Refinement) Early, when you want a spread of approaches — `technical` for *how to build it*, `design` for *how it should look*, `discovery` for *what we still need to figure out* before a real ticket exists, `general` for a quick brief-driven council before the repository is indexed.
|
|
402
|
+
- **How to use it:** ask your agent to convene a council — *"Convene a council on approaches for adding rate limiting to the LLM client; fan it out to multiple models."* For a design pass: *"Run a design council for the evidence-freshness dashboard UI."* For early discovery: *"Run a discovery council — `request_council` with `mode: "discovery"` — for this vague request so we can collect the questions stakeholders need to answer first."* For a fresh, unindexed repo: *"Run a general council — `request_council` with `mode: "general"` — on launch options for this idea."*
|
|
401
403
|
|
|
402
404
|
**4. Deep Research**
|
|
403
405
|
- **What it does:** Runs multi-source, fact-checked web research on a technical topic and returns a cited report.
|
|
@@ -527,6 +529,13 @@ These features are useful once in a while, but you probably won't need them ever
|
|
|
527
529
|
- **When it's useful:** (Setup/Learning) When you notice the agents missing a convention and want to correct it in one sentence.
|
|
528
530
|
- **How to use it:** `/teach-bridge <teaching>` — *"Teach Bridge: always use data-testid selectors in E2E tests."*
|
|
529
531
|
|
|
532
|
+
**10. Review and Start**
|
|
533
|
+
- **What it does:** Spawns one worktree per ticket; each session reviews the ticket inline and, after a per-ticket human proceed/halt gate, hands off to a **fresh implementation session** that reuses the same worktree — review and implementation run in two separate agent contexts, not one shared session.
|
|
534
|
+
- **When it's useful:** (Refinement | Implementation | Automation) The **recommended front door** for "review these tickets, then implement the ones that pass," starting from existing ticket keys (unlike `/full-automation`, which only accepts an idea).
|
|
535
|
+
- **How to use it:** `/review-and-start BAPI-248 BAPI-250` (single or multiple keys flow through the identical code path).
|
|
536
|
+
- **Flags:** `--auto` a single chain-level flag that auto-approves both the review and the implementation phase of every spawned session · `--rounds=1|2` forwarded to the review phase · `--agent`, `--base-branch`, `--max-parallel`, `--dry-run` mirror `/start-tickets`.
|
|
537
|
+
- Under the hood, this is a thin shim over `start-tickets --workflow review-and-implement <KEYS>` — the lower-level launcher seam documented above; the review→gate→fresh-implementation-handoff logic lives in the spawned `/review-and-implement` session, never in this command or the CLI. On approval, that session reuses its review-time model tier by passing it to the fresh implementation launcher via `--tier`.
|
|
538
|
+
|
|
530
539
|
### Operational commands
|
|
531
540
|
|
|
532
541
|
Workflow commands you'll reach for during implementation and CI, beyond the tiers above:
|
|
@@ -535,8 +544,8 @@ Workflow commands you'll reach for during implementation and CI, beyond the tier
|
|
|
535
544
|
|---|---|
|
|
536
545
|
| `/code-ticket PROJ-123` | Download the implementation plan and questions, then execute the plan inline |
|
|
537
546
|
| `/commit-ticket PROJ-123` | Stage, commit, and push changes; transition Jira status; post a smoke-test comment |
|
|
538
|
-
| `/create-pr PROJ-123` |
|
|
539
|
-
| `/check-ci
|
|
547
|
+
| `/create-pr PROJ-123` | Resolve the base branch and open a pull request for the ticket's branch (run after `/commit-ticket`) |
|
|
548
|
+
| `/check-ci PROJ-123` | Monitor CI checks for the current branch, triage failures, apply fixes, and report results |
|
|
540
549
|
| `/parse-repository` | Queue a background job to index the repository for Bridge AI agents |
|
|
541
550
|
| `/check-parse-status` | Check whether a background repository parse job is still running |
|
|
542
551
|
| `/scan-tickets` | Sync recently-updated Jira tickets and backfill workflow timestamps |
|
|
@@ -555,7 +564,7 @@ Behind-the-scenes capabilities an agent gains from the MCP tools — mostly invo
|
|
|
555
564
|
- **Pipeline machinery:** list/inspect pipeline recipes and run/resume/list/delete pipeline runs (the engine under the orchestration commands).
|
|
556
565
|
- **Decision page** generation for capturing human review decisions as structured data.
|
|
557
566
|
- **Connectivity & identity checks:** ping Bridge, check your role, resolve the local docs directory.
|
|
558
|
-
- **Retrieve any generated artifact** (`get_*` for plans, critiques, questions,
|
|
567
|
+
- **Retrieve any generated artifact** (`get_*` for plans, critiques, questions, councils, research, architecture) without regenerating it.
|
|
559
568
|
- **Tiered-section execution telemetry** recording (internal measurement).
|
|
560
569
|
|
|
561
570
|
## Salesforce B2C Commerce (SFCC) Tools
|
|
@@ -637,7 +646,7 @@ Beyond `--init` / `--upgrade`, the package ships operational subcommands of the
|
|
|
637
646
|
|
|
638
647
|
### `start-tickets`
|
|
639
648
|
|
|
640
|
-
Spawns one Worktrunk worktree + selected-agent session per Jira ticket and backs the `/start-tickets` slash command. The agent defaults to **Claude Code** (`claude`) and is configurable via `--agent`. For existing ticket keys, `/review-and-start` (see [Usage Documentation → Review and Start](#tier-
|
|
649
|
+
Spawns one Worktrunk worktree + selected-agent session per Jira ticket and backs the `/start-tickets` slash command. The agent defaults to **Claude Code** (`claude`) and is configurable via `--agent`. For existing ticket keys, `/review-and-start` (see [Usage Documentation → Review and Start](#tier-3--now-and-then)) is the recommended enriched front door over this CLI's `--workflow review-and-implement` seam; using the CLI directly (below) remains the advanced/lower-level path.
|
|
641
650
|
|
|
642
651
|
```
|
|
643
652
|
npx -y @bridge_gpt/mcp-server start-tickets [flags] KEY [KEY ...]
|
|
@@ -765,6 +774,7 @@ If a custom pipeline has the same key as a built-in pipeline, the custom version
|
|
|
765
774
|
| `BAPI_TMUX_SESSION` | No | `bridge-start-tickets` | Override the tmux session-name prefix used by `start-tickets` on Linux |
|
|
766
775
|
| `BAPI_MCP_UPGRADE_ADVICE_ENABLED` | No | _(enabled)_ | MCP-local opt-out for proactively surfacing upgrade advice in pipeline recipe preambles. Set to `false`/`0`/`no`/`off`/`disabled` to suppress. Disabling it does **not** change the `/jira/ping` response or server-side upgrade computation — it only gates the recipe-preamble convention |
|
|
767
776
|
| `BAPI_MCP_TOOL_SURFACE_GATING_ENABLED` | No | _(enabled)_ | MCP-local kill switch for **dynamic tool-surface capability gating** (see [Dynamic tool-surface gating](#dynamic-tool-surface-gating-capability-availability)). Default-on; set to `false`/`0`/`no`/`off`/`disabled` to skip the startup probe, the recurring poll, and the custom `tools/list` handler entirely, restoring the SDK's previous full profile-derived surface. Fail-open: any probe timeout, unreachable backend, non-2xx, malformed payload, incomplete evaluation, or unsupported schema advertises the full profile |
|
|
777
|
+
| `BAPI_MCP_TOOL_SURFACE_POLL_ENABLED` | No | _(disabled)_ | Opt IN to the recurring tool-surface **poll**. Default-**off**: a session gates once via the startup probe and never re-probes. Set to `true`/`1`/`yes`/`on`/`enabled` to restore the jittered 12–18 s heartbeat that pushes `notifications/tools/list_changed` on mid-session capability changes. No effect when gating itself is disabled |
|
|
768
778
|
| `CURSOR_API_KEY` | No | _(none)_ | API key used to authenticate `cursor-agent` when launching `start-tickets`/scheduled runs with `--agent cursor-agent`. Not needed for the default Claude Code agent |
|
|
769
779
|
| `BRIDGE_MCP_PROFILE` | No | `core` | Startup-time tool registration profile — a **comma-separated** list of groups controlling which tool groups are registered when the server starts. `core` is always implicitly included. Valid groups: `core` (default — normal coding tools only), `conductor` (+ 8 conductor/event/supervisor tools), `pipeline-authoring` (+ 5 pipeline run/admin tools — `get_pipeline_recipe` is NOT gated; it stays in `core` because the recipe-driven slash commands depend on it), `sfcc` (+ the 7 heavy SFCC read tools — see [Salesforce B2C Commerce (SFCC) Tools](#salesforce-b2c-commerce-sfcc-tools); the `sfcc_setup_status`/`check_permissions` diagnostics are always registered regardless), and `full` (shortcut that expands to every group). Example: `sfcc,conductor`. Unknown, blank, or malformed tokens are dropped (falling back to `core`). Dynamic mid-session switching via `tools/list_changed` is unsupported — groups are resolved once at process startup. Conductor/epic sessions launched via `start-tickets --conductor` automatically inject `BRIDGE_MCP_PROFILE=conductor` at the spawn boundary; a normal `start-tickets` run stays on `core`. |
|
|
770
780
|
|
|
@@ -781,10 +791,15 @@ The **effective advertised tool surface** is the intersection of three things:
|
|
|
781
791
|
|
|
782
792
|
On startup the server issues one bounded probe to that endpoint and installs a
|
|
783
793
|
custom `tools/list` handler that subtracts the backend-blocked IDs (intersected
|
|
784
|
-
with the locally advertised surface) from what it advertises.
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
794
|
+
with the locally advertised surface) from what it advertises. That single startup
|
|
795
|
+
probe is the default: the surface is gated once per session and the server does
|
|
796
|
+
not re-probe. Installed integrations change rarely and MCP clients re-list on
|
|
797
|
+
reconnect, so a permanent per-session heartbeat — multiplied across every
|
|
798
|
+
concurrent worktree/agent session — was pure request noise against the backend.
|
|
799
|
+
Opt in with `BAPI_MCP_TOOL_SURFACE_POLL_ENABLED=true` to restore the jittered
|
|
800
|
+
12–18 s re-probe that emits `notifications/tools/list_changed` whenever the
|
|
801
|
+
effective visible set actually changes, so a connected client converges to the
|
|
802
|
+
current surface mid-session without a reconnect.
|
|
788
803
|
|
|
789
804
|
**Fail-open by design.** A probe timeout, unreachable backend, non-2xx response,
|
|
790
805
|
malformed payload, incomplete evaluation, or unsupported schema version all
|
package/build/agent-registry.js
CHANGED
|
@@ -76,6 +76,11 @@ export const AGENT_REGISTRY = {
|
|
|
76
76
|
basic: "claude-4.6-sonnet-medium",
|
|
77
77
|
premium: "claude-opus-4-8-thinking-high",
|
|
78
78
|
},
|
|
79
|
+
// BAPI-662: cursor-agent's interactive TUI blocks on a workspace-trust
|
|
80
|
+
// prompt the spawned tab/session can't answer, hanging the launch. Interactive
|
|
81
|
+
// builders already `cd`/`Set-Location` into the target worktree before
|
|
82
|
+
// launching, so `--workspace` (headless-only) is not needed here.
|
|
83
|
+
interactiveLaunchArgs: ["--trust"],
|
|
79
84
|
},
|
|
80
85
|
};
|
|
81
86
|
/** The default agent used when `--agent` is omitted. */
|
|
@@ -14,8 +14,8 @@ export const COMMANDS = {
|
|
|
14
14
|
"full-automation.md": "---\nschedulable: true\narguments: {\"positionals\":[],\"flags\":[{\"name\":\"ideaFile\",\"flag\":\"--idea-file\",\"type\":\"string\",\"required\":true},{\"name\":\"auto\",\"flag\":\"--auto\",\"type\":\"boolean\"}]}\n---\n\nRun the end-to-end full-automation chain (idea-to-ticket → review-ticket → start-tickets) via the server-side chain orchestrator.\n\n$ARGUMENTS\n\n---\n\n# Instructions\n\nThis command drives Phase A's server-side full-automation chain. The only orchestration tools you may drive are `run_full_automation` and `resume_full_automation`; any other Bridge API MCP call you make must be one a server `agent_task` instruction explicitly directs. The server owns all orchestration — ticket creation, review fan-out, and the start-tickets handoff. Do NOT enrich, re-implement, or second-guess any of that work on the client side.\n\n## Stage 0 — Parse arguments\n\n1. Tokenize `$ARGUMENTS` on whitespace. Recognize the following position-independent flags. Each flag supports both the space form (`--flag value`) and the equals form (`--flag=value`) where a value is taken:\n - `--idea <text>` / `--idea=<text>`\n - `--idea-file <path>` / `--idea-file=<path>`\n - `--auto`\n - `--require-approval`\n - `--scheduled-at <ISO-8601>` / `--scheduled-at=<ISO-8601>`\n - `--chain-run-id <UUID>` / `--chain-run-id=<UUID>`\n - `--max-children N` / `--max-children=N`\n - `--allow-duplicate`\n\n2. Value-consumption rules:\n - `--idea` (space form) consumes every subsequent token until the next recognized flag — the idea may contain spaces.\n - `--idea-file`, `--scheduled-at`, `--chain-run-id`, and `--max-children` each consume exactly one value token (the immediately following token, or the text after `=`).\n - `--auto`, `--require-approval`, and `--allow-duplicate` are boolean toggles and consume no value.\n\n3. Free-form idea: all non-flag tokens become the free-form `idea` text **only when both `--idea` and `--idea-file` are absent**. Join those tokens back together preserving order and trim surrounding whitespace. When `--idea` or `--idea-file` is present, there must be no leftover non-flag tokens: reject any stray non-flag token (for example, text following `--idea=<text>` or following the `--idea-file <path>` value) before any MCP tool call rather than silently dropping it.\n\n4. Reject **unknown flags** (any token beginning with `--` that is not one of the recognized flags above) before making any MCP tool call. Stop and report the offending flag.\n\n5. Reject **combined `--idea` and `--idea-file`** before making any MCP tool call:\n ```text\n Provide exactly one of --idea or --idea-file; do not pass both.\n ```\n\n6. Missing-input rule: unless `--chain-run-id` is present, an idea is required. If `--chain-run-id` is absent **and** no idea was supplied (no `--idea`, no `--idea-file`, and no free-form idea tokens), stop immediately and display exactly:\n ```text\n Usage: /full-automation (--idea \"<text>\" | --idea-file <path> | <free-form idea>) [--require-approval] [--scheduled-at <ISO-8601>] [--chain-run-id <UUID>] [--max-children N] [--allow-duplicate]\n ```\n\n7. `--chain-run-id` is the resume path and does **not** require any idea content — when it is present, skip the missing-input check above and proceed to resume.\n\n8. `--idea-file` is forwarded as a path. The skill must **not** read the file contents locally; the server resolves the file.\n\n9. Resolve the derived values:\n - `auto_approve` defaults to `true` (full automation is hands-off by default). It is `false` **only** when `--require-approval` is present. `--auto` is accepted but redundant (a no-op that restates the default), and `--scheduled-at` likewise runs hands-off. When `--require-approval` is present, the chain pauses at external-mutation and review-decision gates for confirmation.\n - `max_children` is the parsed positive integer when `--max-children` is present; otherwise omit it entirely so the server default applies.\n - `allow_duplicate` is `true` only when `--allow-duplicate` is present; otherwise omit it.\n\n## Stage 1 — Drift-check gate\n\nThis gate runs immediately after parsing and **before any MCP tool call**.\n\n1. If `--scheduled-at` is absent, skip this entire stage.\n2. Compute `delta_seconds = now_utc - scheduled_at` (both in UTC).\n3. If `delta_seconds <= 60`, proceed silently to Stage 2.\n4. If `delta_seconds > 60`, present this prompt verbatim (substituting the bracketed values):\n ```text\n Scheduled at <T-iso> UTC; running now at <now-iso> UTC (<Δ human-readable> late). The laptop was likely asleep or unavailable at the scheduled time. Confirm to proceed with the chain, or cancel.\n ```\n Offer the user the choices: `[Confirm] / [Cancel]`.\n5. On `Confirm`, proceed to Stage 2.\n6. On `Cancel`, print this message verbatim and stop:\n ```text\n Chain cancelled by user (drift confirmation declined). No Jira tickets created.\n ```\n When the user cancels, `run_full_automation` must **not** be called.\n7. The 60-second threshold is fixed and must not be made configurable.\n\n## Stage 2 — Run or resume the chain\n\nThe chain is driven entirely by the server-side orchestrator. Announce progress using each envelope's `preamble`, preserving its `Stage N of M — <title>` shape.\n\n### Stage 2a — Start (when `--chain-run-id` is absent)\n\nCall **only** `run_full_automation`. Build the payload, **omitting** any optional value that was not provided (never send `null` or empty strings):\n```json\n{\n \"idea\": \"<resolved inline/free-form idea, when provided>\",\n \"idea_file\": \"<idea-file path, when provided>\",\n \"auto_approve\": \"<resolved boolean>\",\n \"scheduled_at\": \"<scheduled-at value, when provided>\",\n \"max_children\": \"<parsed integer, when provided>\",\n \"allow_duplicate\": \"<true, when provided>\"\n}\n```\n\n### Stage 2b — Resume (when `--chain-run-id` is present)\n\nCall **only** `resume_full_automation` first, with:\n```json\n{\n \"chain_run_id\": \"<UUID>\",\n \"agent_result\": \"Manual resume requested from /full-automation --chain-run-id.\"\n}\n```\n\n### Stage 2c — Envelope loop\n\nFor each envelope returned by `run_full_automation` / `resume_full_automation`, dispatch on `status` / `next_action.kind`:\n\n- `status: \"failed\"` → stop chain progression and render the final report (Stage 3) with the failure status. Do **not** advance to any later stage.\n- `status: \"completed\"` or `next_action.kind: \"complete\"` → render the final report (Stage 3).\n- `status: \"needs_agent_task\"` with `next_action.kind: \"agent_task\"` → display the envelope `preamble`, perform the agent task exactly as the `next_action.instruction` directs, then call `resume_full_automation` with `chain_run_id` set to the envelope's `chain_run_id` and `agent_result` set to the resulting text. Loop back and process the next envelope.\n\nSpecial case — the stage-3 handoff: when the agent-task instruction names a `/start-tickets ...` command, invoke that slash command in **this same session**, summarize the outcome in one line, and pass that one-line summary as `agent_result` to `resume_full_automation`.\n\nConstraints:\n- On your own initiative, the skill must **not** call any Bridge API MCP tool other than `run_full_automation` / `resume_full_automation` — in particular, never independently drive orchestration (`run_pipeline`, `resume_pipeline`, `get_pipeline_recipe`) or enrich tickets (`get_ticket`, `update_ticket_description`, etc.). **However, when a `needs_agent_task` instruction returned by the server explicitly directs you to call a specific Bridge API MCP tool** (for example an orchestrator-directed `get_tickets`, `create_ticket`, `attachment`, or `track_ticket`), **you must invoke that tool exactly as instructed** — performing an orchestrator-directed agent task is not re-orchestrating.\n- If a v1 envelope unexpectedly returns `next_action.kind: \"mcp_call\"`, stop with a clear protocol error instead of bypassing the server-side orchestrator:\n ```text\n Protocol error: chain returned next_action.kind \"mcp_call\", which is out of scope for /full-automation v1. Stopping.\n ```\n\n## Stage 3 — Final report\n\nWhen the chain completes or fails, render this skeleton verbatim:\n\n```markdown\n## Full Automation Complete\n\nChain run: <chain_run_id>\nIdea: <first 80 chars of idea>...\nStages:\n 1. idea-to-ticket: <stages[0].summary>\n 2. review-ticket: <stages[1].summary>\n 3. start-tickets: <stages[2].summary>\n\nTotal Jira tickets created: N\nTotal worktrees spawned: M\nStatus: Success / Failed at stage N — <reason>\n```\n\n- Stage summaries come from the chain envelope or manifest when present.\n- When the completed envelope does not include full stage objects, use the summaries already surfaced in the prior `preamble` text rather than calling additional tools.\n- A stage-1 `too_vague_to_ticket` failure must render the upstream halt reason and set `Status: Failed at stage 1 — <reason>`.\n- Failed chains must not advance to later stages after a failed envelope is received.\n",
|
|
15
15
|
"idea-to-ticket.md": "Convert a short human idea into a Jira ticket (or Epic plus child tickets) via the server-side idea-to-ticket pipeline.\n\n$ARGUMENTS\n\n---\n\n# Instructions\n\nThis command is recipe-driven. Do not call MCP tools directly other than `get_pipeline_recipe` — the recipe determines which tools to call and with what parameters.\n\n## Stage 0 — Parse arguments\n\n1. Tokenize `$ARGUMENTS` on whitespace. Recognize the following position-independent flags (any other tokens are part of the idea):\n - `--allow-duplicate`\n - `--max-children=N` where `N` is a positive integer\n - `--auto`\n\n Flags are optional. Treat absence of a flag as the default; never pass empty-string or null as a placeholder.\n\n2. Everything that is not a recognized flag is the free-form `idea` text. Join those tokens back together preserving order. Trim surrounding whitespace.\n\n3. If the resulting `idea` is empty, stop immediately and display:\n ```\n Usage: /idea-to-ticket <idea> [--allow-duplicate] [--max-children=N] [--auto]\n ```\n\n## Stage 1 — Derive pipeline variables\n\n4. Derive `slug` from the first 6-8 meaningful words of the idea: lowercase, kebab-case, strip non-alphanumeric characters except hyphens, and truncate to roughly 60 characters. Skip stop-words such as \"the\", \"a\", \"an\" when picking the 6-8 meaningful words.\n\n5. Derive `run_id` as `<YYYYMMDD-HHMMSS>-<short-uuid>` using the current UTC time and a short UUID suffix (8 hex chars is enough). The combination of `slug` and `run_id` uniquely identifies this run's artifact directory.\n\n6. Derive the boolean-as-string variables:\n - `allow_duplicate` is `\"true\"` if `--allow-duplicate` was present, otherwise `\"false\"`.\n - `auto_approve_external` is `\"true\"` if `--auto` was present, otherwise `\"false\"`.\n - `max_children` is the integer following `--max-children=` as a string, or `\"10\"` when the flag is absent.\n\n## Stage 2 — Call the recipe\n\n7. Call the `get_pipeline_recipe` MCP tool with:\n - `pipeline`: `\"idea-to-ticket\"`\n - `variables`: `{ \"idea\": \"<idea>\", \"slug\": \"<slug>\", \"run_id\": \"<run_id>\", \"allow_duplicate\": \"<allow_duplicate>\", \"auto_approve_external\": \"<auto_approve_external>\", \"max_children\": \"<max_children>\" }`\n\n Do NOT pass `docs_dir` or `idea_hash` in variables — both are auto-injected by the pipeline system (`docs_dir` from `BAPI_DOCS_DIR`; `idea_hash` is a stable hash derived from the `idea`).\n\n If the tool returns an error, stop and report the failure.\n\n8. Read and strictly obey the `agent_instructions` field in the response. Execute each step in order, announcing each as **Step N of M: <description>**.\n\n The recipe drives the ordered stages for you — do not invoke them directly. In order they are: preflight-and-readiness → research-decision → execute-research → duplicate-and-context-scan → screen-and-resolve → frame-goals-and-nfrs → **comp-analysis** (a gated, backend-safe perception step that maps any attached/referenced design comp to existing components, templates, SCSS/CSS tokens, and routes before drafting; it short-circuits for backend-only or no-comp work) → draft-and-critique → upload-and-track.\n\n## Stage 3 — Final summary\n\n9. After all steps complete, display a summary:\n ```\n ## Pipeline Complete\n\n **Idea**: <first 80 characters of idea>...\n **Slug**: <slug>\n **Run directory**: <docs_dir>/idea-to-ticket/<slug>-<run_id>/\n **Steps executed**: N of M\n **Status**: Success / Failed at step N\n ```\n",
|
|
16
16
|
"implement-ticket.md": "# Implement Ticket\n\n$ARGUMENTS\n\n---\n\n# Instructions\n\nThis command is recipe-driven. Do not call MCP tools directly -- the recipe determines which tools to call and with what parameters.\n\n## CONDUCTOR_MESSAGE_RELAY ##\n\nIf you were launched under the Conductor (your environment carries a conductor run/worker identity), cooperatively poll for supervisor guidance while you work: at natural checkpoints — after reading context, before major code changes, after major implementation chunks, while polling CI checks during the post-PR correction loop, and before your final response — call the `check_messages` MCP tool. The tool reads and acknowledges any messages addressed to you, and acknowledged messages are not redelivered, so a later call returns only new guidance. This is cooperative polling only — it is not prompt injection and never mutates a live session. If the tool or conductor identity is unavailable, continue the task without derailing.\n\n1. Parse `$ARGUMENTS` to extract:\n - A required `ticket_key` matching the Jira key pattern (`[A-Z][A-Z0-9]+-\\d+`).\n - An optional position-independent `--auto` flag.\n\n Tokenize `$ARGUMENTS` on whitespace. The first token matching the Jira key pattern is the `ticket_key`; ignore any additional ticket-key tokens. The presence of a `--auto` token (anywhere in `$ARGUMENTS`) sets `auto_approve` to `true`.\n\n If `$ARGUMENTS` is empty or contains no token matching the Jira key pattern, stop immediately and display:\n ```\n Invalid ticket key format. Expected: PROJ-123 [--auto]\n Usage: /implement-ticket <ticket_key> [--auto]\n ```\n\n2. Call the `get_pipeline_recipe` MCP tool with:\n - `pipeline`: `\"implement-ticket\"`\n - `variables`: `{ \"ticket_key\": \"<ticket_key>\" }`\n - `auto_approve`: `true` — only when `--auto` was passed; otherwise omit this field entirely.\n\n If the tool returns an error, stop and report the failure.\n\n3. Read and strictly obey the `agent_instructions` field in the response. Execute each step in order, announcing each as **Step N of M: <description>**.\n\n4. After all steps complete, display a summary:\n ```\n ## Pipeline Complete\n\n **Ticket**: <ticket_key>\n **Steps executed**: N of M\n **Status**: Success / Failed at step N\n ```\n\n---\n\n# Worker scope discipline and clean exit (Conductor auto mode)\n\nThese rules apply only when you were launched under the Conductor in auto mode (`--auto`); a normal interactive `/implement-ticket` run is unaffected.\n\n## Declared file-scope discipline (N-2)\n\nIf your environment carries `BAPI_CONDUCTOR_DECLARED_TOUCHED_FILES_JSON` (a JSON array of the ticket's declared touched files), stay within that declared file boundary: do **not** create, modify, or delete files outside the declared set. Those out-of-scope files typically belong to a sibling ticket, and editing them risks a merge conflict or a silent clobber of the sibling's merged work. The pre-PR file-scope guard (run at the PR-creation step) will warn about any out-of-scope diff — treat that warning as a signal to re-check your scope, not as a blocker. When the variable is absent, empty, or invalid there is no declared boundary and this rule does not apply.\n\n## Clean session exit (D2)\n\nAfter the final pipeline step completes, cleanly end your worker session (for example by issuing the `/exit` command) so the worktree is released and no idle process lingers — but **only when no follow-up remains that you still own**. Do **not** exit while any of the following is true:\n\n- there are unresolved CI failures you are still correcting (the post-PR CI-correction loop in the CI-monitoring step still owns work),\n- review changes were requested and you have not yet addressed them,\n- there is a merge conflict on your PR that you still own,\n- you have unpushed local commits.\n\nExit only after your final branch state is pushed, the done-gate / CI-monitoring workflow required by the recipe has completed, and no CI/review follow-up remains. A clean `SessionEnd` is both the correct terminal lifecycle signal and the point at which the worker should exit.\n",
|
|
17
|
-
"install-bridge.md": "Bootstrap Bridge API project configuration from the local codebase via the easy-install manifest.\n\n$ARGUMENTS\n\n---\n\n# Instructions\n\nThis command takes no arguments; any `$ARGUMENTS` text is ignored.\n\nCommand contract version: **4**. The install manifest returns a `command_contract_version`; Stage 2\ncompares it to this number to detect a stale scaffolded copy of this command.\n\nThis command performs a one-time \"easy install\" of Bridge API project configuration. It reads the\nserver-owned install manifest once, derives values for the **unset** bootstrap fields from the local\ncodebase, asks for human approval of the project description, applies everything in a single atomic\ncall, and then presents a **capability report** derived from a fresh read-after-write manifest read.\nThe server owns all skip-if-set, conflict, and confirmation semantics — this command never makes its\nown skip-if-set decisions — and the server owns the complete tool catalog, its grouping and ordering,\nand every gate and dependency relationship; this command formats the server's contract and never\nrecomputes it from prose.\n\nThis command is portable across Claude Code, Cursor, and Copilot: it only drives the Bridge API MCP\ntools (`get_my_role`, `get_install_manifest`, `apply_install_manifest`).\n\nIf any stage fails, stop immediately and report which stage failed and why.\n\n**Install-spawn context.** When the spawning instruction explicitly says this `/install-bridge` run is\nin the \"install-spawn context\" (it was launched by the `install-bridge` CLI's fresh agent session),\nStage 8, Stage 9, and Stage 10 are SKIPPED and the single closing interaction is the index-consent\nquestion the spawn prompt owns. When you invoke `/install-bridge` directly (manual invocation),\nStages 8, 9, and 10 run normally.\n\n## Stage 1 — Admin preflight\n\n1. Call the `get_my_role` MCP tool (no parameters). Inspect the response:\n - If `source` is `\"legacy\"`: proceed (legacy keys are permitted).\n - Else if `role` is `\"admin\"`: proceed.\n - Otherwise (a non-admin `user_access` key): stop immediately and display:\n ```\n Admin role required to apply install configuration. Ask a project admin to run /install-bridge,\n or use an admin API key.\n ```\n\n## Stage 2 — Read the manifest (once, pre-apply)\n\n1. Call the `get_install_manifest` MCP tool exactly once here.\n2. Keep the returned `snapshot_token` verbatim — you must pass the SAME token to the apply call in\n Stage 5. Do not call `get_install_manifest` again before applying; a fresh read would invalidate\n the token you are about to use. (Stage 7 performs a SEPARATE, later read-after-write manifest read\n for the current capability status — that is deliberate and does not reuse this token.)\n3. The manifest contains ordered `groups` of bootstrap fields (each with `field_name`, `is_set`,\n `current_value`, `guidance`, `examples`, `validation_summary`, `requires_confirmation`), a list of\n `deferred_fields`, a `next_step`, `done_criteria`, a `command_contract_version`, and the\n `snapshot_token`. It also carries the additive capability fields (`integrations`, `configured`,\n `learned`, `indexed`, `tool_capabilities`, `locked_tools`, `unlocked_tools`) — but ignore those\n here; the accurate capability status is the post-apply read in Stage 7. `tool_capabilities` is the\n COMPLETE catalog-backed report field (one entry per registered MCP tool, grouped and ordered by the\n server); `locked_tools` / `unlocked_tools` are LEGACY compatibility data covering only the VCS/index\n policy cases and are NOT the tool inventory.\n4. Compare the manifest's `command_contract_version` to this command's contract version (4, stated at\n the top of this file). If the manifest's version is HIGHER, this scaffolded command copy is stale:\n warn the user to refresh it by running `npx -y @bridge_gpt/mcp-server --init` in the project root,\n then proceed conservatively — wherever the manifest's `guidance`, `next_step`, or `done_criteria`\n differ from these instructions, follow the manifest.\n\n## Stage 3 — Derive values for UNSET bootstrap fields only\n\n1. Consider ONLY fields whose `is_set` is `false`. Never re-derive or overwrite a field that is\n already set — the server will also skip already-set fields, but you should not even propose them.\n2. For each unset field, derive a value from the local codebase following that field's `guidance`\n (e.g. infer `working_in` from frameworks/manifests, `version_control_system` from the git remote,\n testing stacks from test config and dependencies, directory fields from the repo layout).\n3. Omit any field you are uncertain about or that does not apply — leaving it unset is always\n acceptable. Do NOT guess.\n4. Specifically, do NOT invent a `version` when the platform has no meaningful version concept; leave\n it unset.\n5. Before deriving `version_control_system` or `base_branch`, confirm the git repository root is THIS\n project's root: `git rev-parse --show-toplevel` must resolve to the project directory itself. If\n the git root is an ancestor directory (the project is nested inside another repository), OMIT both\n fields and note why in the final report — deriving them would describe the parent repository, not\n this project.\n6. If you find real evidence for TWO OR MORE plausible values for a field (e.g. configs for multiple\n test frameworks, multiple frameworks in a monorepo), treat the value as ambiguous and omit it\n rather than picking one.\n7. Do NOT derive `selected_mcp_slugs`, even though it appears as an unset bootstrap-eligible field in\n the manifest. MCP validation manual selection is deferred to `/learn-repository`, which derives and\n confirms it with the codebase already researched. Install neither proposes nor applies this field.\n\n## Stage 4 — Human approval for confirmation-requiring fields\n\n`project_description` is the ONLY confirmation-requiring field install proposes. It carries\n`requires_confirmation: true` in the manifest, so it is never applied on derivation alone — it needs\nexplicit human approval. (`selected_mcp_slugs` also requires confirmation, but install does not\nderive it at all; `/learn-repository` asks for it. See Stage 3 step 7.)\n\n1. If `project_description` is unset and you can draft a concise description from the codebase,\n present the proposed `project_description` text to the human and ask for explicit approval before\n including it.\n2. Include `project_description` in the apply payload ONLY as\n `{ \"value\": <approved value>, \"confirmed\": true }`, and only after the human approves it. If the\n human does not approve it, omit the field entirely.\n3. If no human response can be obtained (e.g. a non-interactive / headless session), do NOT stall and\n do NOT abandon the install: proceed to Stage 5 with `project_description` omitted, and report it\n as \"pending human input\" in the final summary. The other derived fields must still be applied — an\n unapproved description never blocks them.\n\n## Stage 5 — Apply (one call)\n\n1. Make exactly ONE call to the `apply_install_manifest` MCP tool, passing:\n - `snapshot_token`: the exact token from Stage 2.\n - `fields`: a map of the derived fields. Scalar fields may be passed directly\n (e.g. `\"base_branch\": \"main\"`); an approved `project_description` must use the\n `{ \"value\": ..., \"confirmed\": true }` object form from Stage 4. Never include\n `selected_mcp_slugs` in this payload — install does not derive or apply it (Stage 3 step 7).\n2. Do not implement any client-side skip-if-set, conflict, or confirmation logic — the server owns\n all of that and returns the outcome buckets.\n3. The apply is partial-tolerant: fields that fail server-side validation are returned in the\n `rejected` bucket while the valid fields still commit. A rejected field is therefore reported,\n not fatal — do NOT retry the whole apply just because one field was rejected.\n\n## Stage 6 — Persist the routing credential\n\nThe Bash-spawned `start-tickets` CLI runs in a DIFFERENT runtime surface than this MCP server, so a\nkey that lives only in `.mcp.json` / `.cursor/mcp.json` is invisible to it and difficulty→model\nrouting silently degrades. This final stage persists the already-validated key into the user-scoped\ncredential store (`~/.config/bridge/credentials.json`) so shell-spawned CLI commands can resolve it.\n\n1. Call the `persist_routing_credential` MCP tool with `repo_name` set to the repository name for\n this install (the configured `BAPI_REPO_NAME`). Pass `repo_name` ONLY — this tool resolves the\n API key inside the MCP server process. NEVER pass or display `BAPI_API_KEY` (nor `api_key`,\n `apiKey`, `secret`, or `token`); the key value must not appear in the tool call or in any output.\n2. On success, tell the user the credential was stored, echoing the returned `target` and `path`,\n e.g. `Stored routing credential for bapi:<repo_name> at <path>`.\n3. On failure, do NOT block the install — show the rest of the install summary anyway, then tell the\n user the routing credential could not be stored. They can rerun `/install-bridge` after setting\n `BAPI_API_KEY` in the environment, or run\n `npx -y @bridge_gpt/mcp-server credentials migrate-agent-config --write-credentials` to migrate an\n existing key. Until then, `start-tickets` model routing fails open (assumes a hard ticket and\n defaults to premium/Opus when available) — this is the MOST EXPENSIVE model tier, so unexplained\n cost is the symptom of leaving this unfixed. Recommend verifying credential resolution afterwards\n with `npx -y @bridge_gpt/mcp-server doctor`.\n\n## Stage 7 — Summarize the outcome, then present the capability report\n\nFirst, begin with an explicit applied count: \"Applied N of M derivable fields\" (M = the unset\nbootstrap fields you derived values for). An install that applied 0 fields must say so loudly — the\ninstall is NOT complete until the apply call reports applied fields.\n\nThen summarize the six buckets from the apply response for the user:\n- `applied` — fields written this run.\n- `skipped` — fields already set (left untouched).\n- `conflict` — fields that changed since the manifest was read (re-run /install-bridge to retry).\n- `needs_confirmation` — fields awaiting explicit confirmation (e.g. project_description).\n- `rejected` — fields that failed validation or were not bootstrap-eligible (the other fields still\n applied; fix or drop the rejected values before any retry).\n- `deferred` — fields intentionally not auto-written by install (owned by /learn-repository or set\n deliberately). `selected_mcp_slugs` belongs here: report it as deferred to `/learn-repository`,\n never as approved, declined, or pending an install-time decision.\n\n### Read-after-write: fetch current capability status\n\nAfter the bucket summary, call the `get_install_manifest` MCP tool ONCE MORE. This post-apply read\nreflects the configuration you just wrote, so its capability fields are current (the Stage-2 read was\npre-apply and is stale for this purpose). This read does not need the snapshot token. Use ONLY this\npost-write response for the capability report below.\n\nThe response carries:\n\n- the `integrations` checklist — each item has `label`, `is_configured`, `required_for`,\n `configure_in`, and MAY have `optional_components` (a list of optional, non-gating add-ons nested\n under that integration, each with `id`, `label`, `is_configured`, `required_for`, `configure_in`,\n and `gating: false`);\n- the separate `configured` / `learned` / `indexed` readiness values;\n- `tool_capabilities` — the COMPLETE catalog-backed collection. It is an ordered array of groups, each\n `{id, name, description, tools}`. Each tool is `{tool, display_name, description, group, profile,\n availability, availability_text, effect, missing, semantics, variants}`, one entry per registered MCP\n tool, keyed by physical tool id;\n- `locked_tools` / `unlocked_tools` — LEGACY compatibility arrays covering only the VCS/index policy\n cases, keyed by policy-case id. They are NOT the tool inventory and you do not render them.\n\nServer authority: the server owns catalog membership, grouping, ordering, gates, and dependency\nrelationships. Never recompute any of them, and never derive them from `docs/mcp-tool-integrations.md`\nor any other documentation — cite that file only for a human explanation of a gate's \"why\".\n\nIf the post-write response has no capability fields at all (no `integrations` / `tool_capabilities`\nkeys, e.g. the additive enrichment was omitted), print exactly:\n`No capability status is available yet; configuration can still continue.` and skip the five sections.\n\nOtherwise, open with a short plain-language framing before the first section: explain that Bridge\nconnects the user's systems (their tracker, their code host, their platform) to their local code\neditor, and that connecting more of those systems unlocks richer capabilities. Then render exactly\nthese five sections, in this order, with these exact headings:\n\n**Connected ✓**\n\n- List each configured integration's `label` from the post-write `integrations` checklist, with a\n restrained `✓` marker. (Do not let the `✓` markers dominate the report.)\n- If a listed integration has `optional_components`, list each component beneath its parent as a\n sub-item, and describe it as optional and not required for the parent to work. A component with\n `gating: false` NEVER blocks anything: it is not a separate integration, it does not belong in the\n top-level list, and its state must not change how you describe its parent.\n\n**Not yet connected ✗**\n\n- List each unconfigured integration: its `label`, its `required_for` items, and the exact\n `configure_in` pointer. Do NOT include `setup_instructions` content — the pointer is the only\n configuration direction you emit. Apply the same `optional_components` rule as above.\n- The pointer is per-integration and is NOT always the setup UI. Emit whatever the server sent,\n VERBATIM: `github_app` points at the terminal command `npx -y @bridge_gpt/mcp-server@latest\n connect-github --repo <repo_name>`; `sfcc` points at the guide `docs/install/sfcc-integration.md`;\n Jira and `vcs_access_token` point at the setup UI. Never rewrite a command pointer into \"the setup\n UI\", and never replace GitHub's command with a guide.\n- STRICT INVARIANT: you DIRECT the human to that pointer; you never ask for, accept, echo, or\n transport an integration credential (Jira, GitHub, VCS, webhook, or SFCC — API token, access token,\n access key, webhook secret) in any form. This is unchanged for GitHub: the connect-github command\n authenticates the human to GitHub in their own browser, and no GitHub credential ever reaches Bridge\n or an agent. The SFCC pointer is a guide the human follows themselves — it is never an invitation to\n hand you credentials.\n\n**Tools you can use now**\n\n- Render from `tool_capabilities`, using the server's group order and, within each group, the server's\n tool order. Show each group's `name`, then each tool's `display_name`, its one-sentence\n `description`, and its `availability_text` exactly as provided.\n- Include here every tool whose `availability` is `available_now`, `available_with_less_context`,\n `profile_required`, or `varies_by_variant`.\n- Describe an `available_with_less_context` tool as available now with less codebase context — never\n as failed or unavailable.\n- For a `profile_required` tool, emit the server's neutral profile wording as given. Do NOT claim the\n profile is or is not registered locally: the server cannot observe that, so neither can you.\n- A `varies_by_variant` tool belongs here because at least one of its options is usable now. Render\n its variants beneath it and rely on each variant's own `availability_text` for accuracy — do NOT\n describe the whole tool as blocked, and do NOT describe it as fully available. (This is a common\n state, not a corner case: `create_doc` reports it whenever VCS is connected but the repository is\n not yet indexed, since `tdd` needs the index while `fsd`/`prd` do not.)\n- If a tool has a non-empty `variants` array, render the variants as sub-items BENEATH that one tool,\n each with its `label` and its own `availability_text`. Variants are options of a single tool\n (`create_doc`'s tdd/fsd/prd, `request_council`'s modes) — never present them as separately\n registered tools, and never invent an id like `create_doc:tdd`.\n- NEVER emit `BLOCK` or `DEGRADE`. The `effect` field is internal metadata that remains in the\n payload for compatibility; it is not for display. `availability_text` is what a human reads.\n\n**Tools you'll unlock**\n\n- Render every tool whose `availability` is `available_after_dependencies`, in the same server-provided\n grouping and order, each with its `display_name`, `description`, and `availability_text`. Where a\n tool's `variants` differ, show the variants beneath it so the user can see which options are already\n usable.\n- The `availability_text` already names the connection needed in plain language, including \"or\"\n relationships (an unknown VCS provider yields \"the GitHub App or a VCS access token\") and the\n separately-required repository index. Emit it as given rather than re-deriving it from `missing` or\n `semantics`.\n- Cite `docs/mcp-tool-integrations.md` briefly for a gate's human \"why\" — but never recompute\n membership from it.\n\n**Recommended next step + why**\n\n- Make this section visually strongest through ordering and concise wording. Choose the single most\n valuable next action using this deterministic priority based only on the server output. Jira or SFCC\n never displaces this order unless the server itself reports it as a dependency:\n 1. If any tool's `missing` includes a VCS integration (`github_app` / `vcs_access_token`), recommend\n connecting VCS first, using that integration's own `configure_in` pointer verbatim (for\n `github_app` that is the `connect-github` terminal command, not the setup UI).\n 2. Else if any tool's `missing` includes `code_index`, recommend running repository indexing\n (`/parse-repository`) next.\n 3. Else if `learned` is false, recommend running `/learn-repository` to populate the deeper\n instruction-tier configuration.\n- If `indexed` is `null` (unknown), include this exact warning:\n `Index status could not be confirmed—check again before relying on codebase-grounded tools.`\n\n## Stage 8 — Offer the next steps\n\n**Skip this entire stage in the install-spawn context** (the spawning instruction identifies it); the\nspawn prompt owns the single closing index-consent question there. On direct manual `/install-bridge`\ninvocation, run it normally:\n\n1. Offer to continue with `/learn-repository` to populate the deeper instruction-tier configuration\n (architecture, review, testing, and correctness standards), matching the manifest's `next_step`.\n Mention that it also derives and confirms the applicable MCP validation manuals\n (`selected_mcp_slugs`) — it researches the codebase first, so it can propose them with evidence\n and explain what they do, which is why install leaves that field alone.\n2. After `/learn-repository` completes (or if the human declines it), offer to start **repository\n indexing**: ask ONE consent question (\"Start repository indexing now? It runs as a background job\n and makes the codebase searchable for Bridge API's agents.\"). On yes, call the `parse_repository`\n MCP tool once. Report that the job was QUEUED and that progress can be checked with\n `get_parse_status` (or `/check-parse-status`) — do NOT poll it to completion in this session.\n Indexing is deliberately last so the applied `exclude_directories` / `custom_directories` values\n scope it correctly. If no human response can be obtained (non-interactive session), skip indexing\n and list it as a pending next step in the final report — never start it without consent.\n\n## Stage 9 — Offer CI follow-up configuration (only when CI is detected)\n\n**Skip this entire stage in the install-spawn context** (the spawning instruction identifies it), so\nthe index-consent question the spawn prompt owns remains the sole closing interaction. On direct\nmanual `/install-bridge` invocation, run it normally.\n\nThis stage is separate from the single apply call: it writes at most ONE field via the\n`config_field` MCP tool. `ci_followup_config` is REPLACE-NOT-AUGMENT — leaving it NULL already means\nsafe poll-only defaults, so \"skip\" is always a correct answer.\n\n1. Gate: only offer this when Stage 3 found real CI evidence (e.g. `.github/workflows/*.yml`,\n `bitbucket-pipelines.yml`). With no CI detected, skip this stage and note \"no CI detected — CI\n follow-up not offered\" in the final report.\n2. Ask ONE question, referencing the discovered CI by name, with exactly these preset profiles\n (default: skip):\n - **observe** — poll CI results only, never attempt fixes:\n `{\"strategy\": \"poll_only\", \"max_iterations\": 1, \"max_minutes\": 10, \"instructions\": \"\"}`\n - **self-heal** — bounded fix-and-iterate loop on the automation's own PRs:\n `{\"strategy\": \"fix_and_iterate\", \"max_iterations\": 3, \"max_minutes\": 45, \"instructions\": \"\"}`\n - **skip** (default) — leave `ci_followup_config` unset (NULL = baseline poll-only behavior).\n3. Do NOT offer or compose a `custom` strategy during install — free-form CI follow-up instructions\n are a deliberate later act (setup UI or a direct `config_field` update).\n4. When the human picks a profile, call the `config_field` MCP tool once (operation `\"update\"`,\n `field_name: \"ci_followup_config\"`, `value`: the profile's JSON object). If the server rejects the\n value, retry ONCE with a corrected payload; if it is rejected again, stop, show the proposed JSON\n to the human, and leave the field unset.\n5. Never select a profile in a non-interactive session — skip and list it as a pending next step. An\n unattended install must never enable fix-and-iterate behavior.\n\n## Stage 10 — Offer the speed-vs-quality repository preference\n\n**Skip this entire stage in the install-spawn context** (the spawning instruction identifies it), so\nthe index-consent question the spawn prompt owns remains the sole closing interaction. On direct\nmanual `/install-bridge` invocation, run it normally.\n\nThis stage is separate from the single apply call: it writes at most ONE field via the `config_field`\nMCP tool, `field_name: \"speed_vs_quality\"`. The column defaults to `5` (max quality) for every\nrepository, so \"skip\" always leaves a safe, valid value in place.\n\n1. Never ask this question in a non-interactive session — skip and list it as a pending next step. An\n unattended install must never block on or silently answer this preference.\n2. Read the current value first via the `config_field` MCP tool (operation `\"get\"`,\n `field_name: \"speed_vs_quality\"`) so a reinstall can show the stored value — not always `5` — as\n the displayed default rather than silently re-asking from scratch.\n3. Ask ONE question with exactly these five numbered presets (default: the value from step 2, or `5`\n if this is the first install):\n - **1** — Max speed\n - **2** — Prefer speed\n - **3** — Balanced\n - **4** — Prefer quality\n - **5** — Max quality (default)\n4. Persist ONLY on an explicit answer — including an explicitly accepted default — by calling the\n `config_field` MCP tool once (operation `\"update\"`, `field_name: \"speed_vs_quality\"`,\n `value`: the selected integer 1-5). Do not overwrite an existing value when the human gives no\n answer at all (e.g. the session cannot obtain one) — leave the stored/default value untouched in\n that case, distinct from an explicit accepted-default selection of `5`.\n5. After a successful persist, emit one additive structured log line (mirroring the codebase's\n `logging.info(msg, extra={...})` convention for non-response-body observability signals) with\n fields: `event=\"install.speed_vs_quality\"`, `repo_name`, `field_name=\"speed_vs_quality\"`,\n `selected_preset` (the persisted integer), `outcome=\"persisted\"`. Never log this event before the\n `config_field` tool call has confirmed the write.\n6. When skipped, emit the corresponding structured event with `outcome` set to one of\n `\"skipped_non_interactive\"` (non-interactive session) or `\"skipped_no_answer\"` (interactive session,\n no explicit answer obtained) — omit `selected_preset` and any prompt text from this event.\n7. If the `config_field` call is rejected, retry ONCE with a corrected payload; if it is rejected\n again, stop, show the proposed value to the human, and leave the field at its current stored value.\n Do not log a persistence-success event for a rejected or failed write.\n\n## Return\n\nReport the admin check result, the \"Applied N of M\" count, the\napplied/skipped/conflict/needs_confirmation/rejected field names, any fields omitted for\nnested-repository or ambiguity reasons, the approval outcome for `project_description` — install's\nonly confirmation-requiring field — (approved / declined / pending human input), the fact that\n`selected_mcp_slugs` is deferred to `/learn-repository` rather than decided here, whether a\nstale-command warning was raised (manifest\n`command_contract_version` higher than this command's), whether the routing credential was persisted\n(the returned `target` and `path`, or the non-blocking failure remediation), the five-section\ncapability report (Connected ✓ / Not yet connected ✗ / Tools you can use now / Tools you'll unlock /\nRecommended next step + why) from the post-apply read-after-write manifest read, the CI follow-up\noutcome (profile written / skipped / no CI detected / pending / skipped in install-spawn context), the\nspeed-vs-quality preference outcome (persisted with its preset / skipped_non_interactive /\nskipped_no_answer / pending / skipped in install-spawn context), and the recommended next step.\n",
|
|
18
|
-
"learn-repository.md": "Learn and document all configuration fields for the repository by running parallel research agents.\n\n$ARGUMENTS\n\n---\n\n# Instructions\n\nThis command is recipe-driven. Do not call MCP tools directly -- the recipe determines which tools to call and with what parameters.\n\n1. This command takes no arguments.\n\n2. **Before executing any recipe step**, tell the user what they are about to sit through and why it\n is worth it:\n\n ```\n Learning this repository. This takes a while — the research agents read the actual codebase, and\n all the unlearned fields are researched in parallel, so the wait is roughly the slowest single\n field rather than the sum of all of them. Fields that are already populated are skipped entirely.\n\n What this buys you: these fields are what ground Bridge's agents in THIS codebase. Planning,\n reviewing, and code generation all read them, so they follow your repository's actual\n architecture, testing, documentation, and correctness conventions instead of generic defaults.\n\n It runs unattended — there are no approval prompts during the run. You may be asked one batched\n question at the very end.\n ```\n\n Do not invent a specific number of minutes; the honest statement is the parallel-wait shape above.\n\n3. Call the `get_pipeline_recipe` MCP tool with:\n - `pipeline`: `\"learn-repository\"`\n\n If the tool returns an error, stop and report the failure.\n\n4. Read and strictly obey the `agent_instructions` field in the response. Execute each step in order, announcing each as **Step N of M: <description>**.\n\n Retain, for the closing summary: the consolidated research task's structured result (its per-field\n `status`, `character_count`, `condensed`, and `condensation_reason`), each upload step's result,\n and the final confirmation task's structured result. You will need all three — do not discard them\n as you go.\n\n5. After all steps complete, display a summary built from the results you retained:\n\n ```\n ## Learn Complete\n\n **Status**: Success / Completed with gaps / Failed at step N\n\n **Learned and applied**: <fields drafted this run and written to config>\n **Already populated (skipped)**: <fields skipped because they already had a value>\n **Condensed to fit the field limit**: <field — reason it was condensed, per field>\n **Gaps**: <fields whose research or upload failed, each named with its reason>\n **Confirmation**: <approved / applied / declined / pending human input / not applicable, per field>\n\n Review or edit any of these on the **Project Configuration** page, under **Code Writer Settings**\n for the learned instructions and **MCP Validation Manuals** for the manual selection. Bridge's\n agents read whatever is stored there, so correcting a wrong conclusion there changes their\n behavior.\n ```\n\n Rules for the summary:\n\n - A run where some fields failed but others applied is **`Completed with gaps`**, not `Failed`.\n Name every gap explicitly — an unnamed gap is worse than a failed run, because the user believes\n the field was learned.\n - Report confirmation candidates that could not be presented in a headless session with the exact\n phrase `pending human input`.\n - Every field that was condensed must appear with the reason it was condensed.\n",
|
|
17
|
+
"install-bridge.md": "Bootstrap Bridge API project configuration from the local codebase via the easy-install manifest.\n\n$ARGUMENTS\n\n---\n\n# Instructions\n\nThis command takes no arguments; any `$ARGUMENTS` text is ignored.\n\nCommand contract version: **5**. The install manifest returns a `command_contract_version`; Stage 2\ncompares it to this number to detect a stale scaffolded copy of this command.\n\nThis command performs a one-time \"easy install\" of Bridge API project configuration. It reads the\nserver-owned install manifest once, derives values for the **unset** bootstrap fields from the local\ncodebase, asks for human approval of the project description, applies everything in a single atomic\ncall, and then presents a **concise capability report** derived from a fresh read-after-write manifest\nread. The server owns all skip-if-set, conflict, and confirmation semantics — this command never makes\nits own skip-if-set decisions — and the server owns the complete tool catalog and the bounded concise\nprojection over it, their grouping and ordering, and every gate and dependency relationship; this\ncommand formats the server's contract and never recomputes it from prose. Indexing is never a decision\nthis command makes or asks about: it starts automatically, gated entirely by server-side readiness (see\nStage 8).\n\nThis command is portable across Claude Code, Cursor, and Copilot: it only drives the Bridge API MCP\ntools (`get_my_role`, `get_install_manifest`, `apply_install_manifest`).\n\nIf any stage fails, stop immediately and report which stage failed and why.\n\n**Install-spawn context.** When the spawning instruction explicitly says this `/install-bridge` run is\nin the \"install-spawn context\" (it was launched by the `install-bridge` CLI's fresh agent session),\nStage 8, Stage 9, and Stage 10 are SKIPPED and the single closing interaction is the concise capability\nreport plus a `/learn-repository` recommendation that the spawn prompt owns. When you invoke\n`/install-bridge` directly (manual invocation), Stages 8, 9, and 10 run normally.\n\n## Stage 1 — Admin preflight\n\n1. Call the `get_my_role` MCP tool (no parameters). Inspect the response:\n - If `source` is `\"legacy\"`: proceed (legacy keys are permitted).\n - Else if `role` is `\"admin\"`: proceed.\n - Otherwise (a non-admin `user_access` key): stop immediately and display:\n ```\n Admin role required to apply install configuration. Ask a project admin to run /install-bridge,\n or use an admin API key.\n ```\n\n## Stage 2 — Read the manifest (once, pre-apply)\n\n1. Call the `get_install_manifest` MCP tool exactly once here.\n2. Keep the returned `snapshot_token` verbatim — you must pass the SAME token to the apply call in\n Stage 5. Do not call `get_install_manifest` again before applying; a fresh read would invalidate\n the token you are about to use. (Stage 7 performs a SEPARATE, later read-after-write manifest read\n for the current capability status — that is deliberate and does not reuse this token.)\n3. The manifest contains ordered `groups` of bootstrap fields (each with `field_name`, `is_set`,\n `current_value`, `guidance`, `examples`, `validation_summary`, `requires_confirmation`), a list of\n `deferred_fields`, a `next_step`, `done_criteria`, a `command_contract_version`, and the\n `snapshot_token`. It also carries the additive capability fields (`integrations`, `configured`,\n `learned`, `indexed`, `tool_capabilities`, `concise_tool_capabilities`, `locked_tools`,\n `unlocked_tools`) — but ignore those here; the accurate capability status is the post-apply read in\n Stage 7. `tool_capabilities` is the COMPLETE catalog-backed report field (one entry per registered\n MCP tool, grouped and ordered by the server); `concise_tool_capabilities` is the ADDITIVE, bounded\n projection Stage 7 actually renders (see Stage 7); `locked_tools` / `unlocked_tools` are LEGACY\n compatibility data covering only the VCS/index policy cases and are NOT the tool inventory.\n4. Compare the manifest's `command_contract_version` to this command's contract version (5, stated at\n the top of this file). If the manifest's version is HIGHER, this scaffolded command copy is stale:\n warn the user to refresh it by running `npx -y @bridge_gpt/mcp-server --init` in the project root,\n then proceed conservatively — wherever the manifest's `guidance`, `next_step`, or `done_criteria`\n differ from these instructions, follow the manifest.\n\n## Stage 3 — Derive values for UNSET bootstrap fields only\n\n1. Consider ONLY fields whose `is_set` is `false`. Never re-derive or overwrite a field that is\n already set — the server will also skip already-set fields, but you should not even propose them.\n2. For each unset field, derive a value from the local codebase following that field's `guidance`\n (e.g. infer `working_in` from frameworks/manifests, `version_control_system` from the git remote,\n testing stacks from test config and dependencies, directory fields from the repo layout).\n3. Omit any field you are uncertain about or that does not apply — leaving it unset is always\n acceptable. Do NOT guess.\n4. Specifically, do NOT invent a `version` when the platform has no meaningful version concept; leave\n it unset.\n5. Before deriving `version_control_system` or `base_branch`, confirm the git repository root is THIS\n project's root: `git rev-parse --show-toplevel` must resolve to the project directory itself. If\n the git root is an ancestor directory (the project is nested inside another repository), OMIT both\n fields and note why in the final report — deriving them would describe the parent repository, not\n this project.\n6. If you find real evidence for TWO OR MORE plausible values for a field (e.g. configs for multiple\n test frameworks, multiple frameworks in a monorepo), treat the value as ambiguous and omit it\n rather than picking one.\n7. Do NOT derive `selected_mcp_slugs`, even though it appears as an unset bootstrap-eligible field in\n the manifest. MCP validation manual selection is deferred to `/learn-repository`, which derives and\n confirms it with the codebase already researched. Install neither proposes nor applies this field.\n\n## Stage 4 — Human approval for confirmation-requiring fields\n\n`project_description` is the ONLY confirmation-requiring field install proposes. It carries\n`requires_confirmation: true` in the manifest, so it is never applied on derivation alone — it needs\nexplicit human approval. (`selected_mcp_slugs` also requires confirmation, but install does not\nderive it at all; `/learn-repository` asks for it. See Stage 3 step 7.)\n\n1. If `project_description` is unset and you can draft a concise description from the codebase,\n present the proposed `project_description` text to the human and ask for explicit approval before\n including it.\n2. Include `project_description` in the apply payload ONLY as\n `{ \"value\": <approved value>, \"confirmed\": true }`, and only after the human approves it. If the\n human does not approve it, omit the field entirely.\n3. If no human response can be obtained (e.g. a non-interactive / headless session), do NOT stall and\n do NOT abandon the install: proceed to Stage 5 with `project_description` omitted, and report it\n as \"pending human input\" in the final summary. The other derived fields must still be applied — an\n unapproved description never blocks them.\n\n## Stage 5 — Apply (one call)\n\n1. Make exactly ONE call to the `apply_install_manifest` MCP tool, passing:\n - `snapshot_token`: the exact token from Stage 2.\n - `fields`: a map of the derived fields. Scalar fields may be passed directly\n (e.g. `\"base_branch\": \"main\"`); an approved `project_description` must use the\n `{ \"value\": ..., \"confirmed\": true }` object form from Stage 4. Never include\n `selected_mcp_slugs` in this payload — install does not derive or apply it (Stage 3 step 7).\n2. Do not implement any client-side skip-if-set, conflict, or confirmation logic — the server owns\n all of that and returns the outcome buckets.\n3. The apply is partial-tolerant: fields that fail server-side validation are returned in the\n `rejected` bucket while the valid fields still commit. A rejected field is therefore reported,\n not fatal — do NOT retry the whole apply just because one field was rejected.\n\n## Stage 6 — Persist the routing credential\n\nThe Bash-spawned `start-tickets` CLI runs in a DIFFERENT runtime surface than this MCP server, so a\nkey that lives only in `.mcp.json` / `.cursor/mcp.json` is invisible to it and difficulty→model\nrouting silently degrades. This final stage persists the already-validated key into the user-scoped\ncredential store (`~/.config/bridge/credentials.json`) so shell-spawned CLI commands can resolve it.\n\n1. Call the `persist_routing_credential` MCP tool with `repo_name` set to the repository name for\n this install (the configured `BAPI_REPO_NAME`). Pass `repo_name` ONLY — this tool resolves the\n API key inside the MCP server process. NEVER pass or display `BAPI_API_KEY` (nor `api_key`,\n `apiKey`, `secret`, or `token`); the key value must not appear in the tool call or in any output.\n2. On success, tell the user the credential was stored, echoing the returned `target` and `path`,\n e.g. `Stored routing credential for bapi:<repo_name> at <path>`.\n3. On failure, do NOT block the install — show the rest of the install summary anyway, then tell the\n user the routing credential could not be stored. They can rerun `/install-bridge` after setting\n `BAPI_API_KEY` in the environment, or run\n `npx -y @bridge_gpt/mcp-server credentials migrate-agent-config --write-credentials` to migrate an\n existing key. Until then, `start-tickets` model routing fails open (assumes a hard ticket and\n defaults to premium/Opus when available) — this is the MOST EXPENSIVE model tier, so unexplained\n cost is the symptom of leaving this unfixed. Recommend verifying credential resolution afterwards\n with `npx -y @bridge_gpt/mcp-server doctor`.\n\n## Stage 7 — Summarize the outcome, then present the concise capability report\n\nFirst, begin with an explicit applied count: \"Applied N of M derivable fields\" (M = the unset\nbootstrap fields you derived values for). An install that applied 0 fields must say so loudly — the\ninstall is NOT complete until the apply call reports applied fields. This applied-count line and the\nsix-bucket summary below remain the PRIMARY install result — the capability report that follows is a\nsecondary close, not a replacement for it.\n\nThen summarize the six buckets from the apply response for the user:\n- `applied` — fields written this run.\n- `skipped` — fields already set (left untouched).\n- `conflict` — fields that changed since the manifest was read (re-run /install-bridge to retry).\n- `needs_confirmation` — fields awaiting explicit confirmation (e.g. project_description).\n- `rejected` — fields that failed validation or were not bootstrap-eligible (the other fields still\n applied; fix or drop the rejected values before any retry).\n- `deferred` — fields intentionally not auto-written by install (owned by /learn-repository or set\n deliberately). `selected_mcp_slugs` belongs here: report it as deferred to `/learn-repository`,\n never as approved, declined, or pending an install-time decision.\n\n### Read-after-write: fetch the concise capability report\n\nAfter the bucket summary, call the `get_install_manifest` MCP tool ONCE MORE. This post-apply read\nreflects the configuration you just wrote (the Stage-2 read was pre-apply and is stale for this\npurpose). This read does not need the snapshot token. Use ONLY this post-write response for the\nreport below.\n\nThe response carries `concise_tool_capabilities` — an ADDITIVE, bounded projection over the complete\n`tool_capabilities` catalog (which the response still carries unchanged; this stage simply does not\nrender it). It is a server-ordered array of at most two tiers, each\n`{id, name, tools: [{tool, display_name}], more_count}`, covering only \"Regularly useful\" and\n\"Occasionally useful\", available-now tools only, with everything else in those two tiers collapsed\ninto that tier's `more_count`.\n\nServer authority: the server computed this projection's tier selection, availability filter, and\n`more_count` arithmetic. Never recompute, re-filter, re-count, or re-derive it from `tool_capabilities`,\n`docs/mcp-tool-integrations.md`, or any other documentation — render exactly what the server sent.\n\nIf the post-write response has no `concise_tool_capabilities` field at all, or it is present but\nmalformed (not the `{id, name, tools, more_count}` tier shape described above), print exactly:\n`No capability status is available yet; configuration can still continue.` and skip the section below\nentirely — never fall back to rendering the complete `tool_capabilities` catalog or a remembered/\nhallucinated capability list.\n\nOtherwise, render exactly one section, with this exact heading:\n\n**What Bridge can help with**\n\n- Render each tier from `concise_tool_capabilities` in the server's given order: \"Regularly useful\"\n first, then \"Occasionally useful\". Do not reorder, filter, re-tier, or drop a tier the server\n included, even if its `tools` array is empty.\n- Within a tier, list each tool's `display_name` only, in server order — no description,\n `availability_text`, effect, dependency explanation, or variant detail; those live on the complete\n `tool_capabilities` field, which this section does not touch.\n- Render the tier's `more_count` as plain, muted-style summary text (\"+N more\") — never as an\n expansion prompt, a link, or something requiring further action. Omit the \"+N more\" line entirely\n when `more_count` is `0`.\n- Do not locally filter, count, regroup, infer availability, or fall back to the complete\n `tool_capabilities` collection for this section under any circumstance.\n\n## Stage 8 — Offer the next step\n\n**Skip this entire stage in the install-spawn context** (the spawning instruction identifies it); the\nspawn prompt owns the single closing concise-report-plus-learn-recommendation there. On direct manual\n`/install-bridge` invocation, run it normally:\n\n1. Offer to continue with `/learn-repository` to populate the deeper instruction-tier configuration\n (architecture, review, testing, and correctness standards), matching the manifest's `next_step`.\n Mention that it also derives and confirms the applicable MCP validation manuals\n (`selected_mcp_slugs`) — it researches the codebase first, so it can propose them with evidence\n and explain what they do, which is why install leaves that field alone.\n2. Do NOT ask about repository indexing in any form. There is no consent question, no\n `parse_repository` tool call, and no `/parse-repository` continuation here — indexing starts\n automatically once the repository reaches full parse readiness (VCS credentials, the Pinecone\n index, `working_in` / `project_description`, and SFCC prerequisites where applicable), via the\n same readiness-gated funnel the GitHub connection-confirm endpoints and the scheduled sweep already\n use. Do not claim indexing has already started — this command has no visibility into that funnel's\n outcome.\n\n## Stage 9 — Offer CI follow-up configuration (only when CI is detected)\n\n**Skip this entire stage in the install-spawn context** (the spawning instruction identifies it), so\nthe concise report and learn recommendation the spawn prompt owns remain the sole closing interaction. On direct\nmanual `/install-bridge` invocation, run it normally.\n\nThis stage is separate from the single apply call: it writes at most ONE field via the\n`config_field` MCP tool. `ci_followup_config` is REPLACE-NOT-AUGMENT — leaving it NULL already means\nsafe poll-only defaults, so \"skip\" is always a correct answer.\n\n1. Gate: only offer this when Stage 3 found real CI evidence (e.g. `.github/workflows/*.yml`,\n `bitbucket-pipelines.yml`). With no CI detected, skip this stage and note \"no CI detected — CI\n follow-up not offered\" in the final report.\n2. Ask ONE question, referencing the discovered CI by name, with exactly these preset profiles\n (default: skip):\n - **observe** — poll CI results only, never attempt fixes:\n `{\"strategy\": \"poll_only\", \"max_iterations\": 1, \"max_minutes\": 10, \"instructions\": \"\"}`\n - **self-heal** — bounded fix-and-iterate loop on the automation's own PRs:\n `{\"strategy\": \"fix_and_iterate\", \"max_iterations\": 3, \"max_minutes\": 45, \"instructions\": \"\"}`\n - **skip** (default) — leave `ci_followup_config` unset (NULL = baseline poll-only behavior).\n3. Do NOT offer or compose a `custom` strategy during install — free-form CI follow-up instructions\n are a deliberate later act (setup UI or a direct `config_field` update).\n4. When the human picks a profile, call the `config_field` MCP tool once (operation `\"update\"`,\n `field_name: \"ci_followup_config\"`, `value`: the profile's JSON object). If the server rejects the\n value, retry ONCE with a corrected payload; if it is rejected again, stop, show the proposed JSON\n to the human, and leave the field unset.\n5. Never select a profile in a non-interactive session — skip and list it as a pending next step. An\n unattended install must never enable fix-and-iterate behavior.\n\n## Stage 10 — Offer the speed-vs-quality repository preference\n\n**Skip this entire stage in the install-spawn context** (the spawning instruction identifies it), so\nthe concise report and learn recommendation the spawn prompt owns remain the sole closing interaction. On direct\nmanual `/install-bridge` invocation, run it normally.\n\nThis stage is separate from the single apply call: it writes at most ONE field via the `config_field`\nMCP tool, `field_name: \"speed_vs_quality\"`. The column defaults to `5` (max quality) for every\nrepository, so \"skip\" always leaves a safe, valid value in place.\n\n1. Never ask this question in a non-interactive session — skip and list it as a pending next step. An\n unattended install must never block on or silently answer this preference.\n2. Read the current value first via the `config_field` MCP tool (operation `\"get\"`,\n `field_name: \"speed_vs_quality\"`) so a reinstall can show the stored value — not always `5` — as\n the displayed default rather than silently re-asking from scratch.\n3. Ask ONE question with exactly these five numbered presets (default: the value from step 2, or `5`\n if this is the first install):\n - **1** — Max speed\n - **2** — Prefer speed\n - **3** — Balanced\n - **4** — Prefer quality\n - **5** — Max quality (default)\n4. Persist ONLY on an explicit answer — including an explicitly accepted default — by calling the\n `config_field` MCP tool once (operation `\"update\"`, `field_name: \"speed_vs_quality\"`,\n `value`: the selected integer 1-5). Do not overwrite an existing value when the human gives no\n answer at all (e.g. the session cannot obtain one) — leave the stored/default value untouched in\n that case, distinct from an explicit accepted-default selection of `5`.\n5. After a successful persist, emit one additive structured log line (mirroring the codebase's\n `logging.info(msg, extra={...})` convention for non-response-body observability signals) with\n fields: `event=\"install.speed_vs_quality\"`, `repo_name`, `field_name=\"speed_vs_quality\"`,\n `selected_preset` (the persisted integer), `outcome=\"persisted\"`. Never log this event before the\n `config_field` tool call has confirmed the write.\n6. When skipped, emit the corresponding structured event with `outcome` set to one of\n `\"skipped_non_interactive\"` (non-interactive session) or `\"skipped_no_answer\"` (interactive session,\n no explicit answer obtained) — omit `selected_preset` and any prompt text from this event.\n7. If the `config_field` call is rejected, retry ONCE with a corrected payload; if it is rejected\n again, stop, show the proposed value to the human, and leave the field at its current stored value.\n Do not log a persistence-success event for a rejected or failed write.\n\n## Return\n\nReport the admin check result, the \"Applied N of M\" count, the\napplied/skipped/conflict/needs_confirmation/rejected field names, any fields omitted for\nnested-repository or ambiguity reasons, the approval outcome for `project_description` — install's\nonly confirmation-requiring field — (approved / declined / pending human input), the fact that\n`selected_mcp_slugs` is deferred to `/learn-repository` rather than decided here, whether a\nstale-command warning was raised (manifest\n`command_contract_version` higher than this command's), whether the routing credential was persisted\n(the returned `target` and `path`, or the non-blocking failure remediation), the \"What Bridge can help\nwith\" concise capability report from the post-apply read-after-write manifest read, the CI follow-up\noutcome (profile written / skipped / no CI detected / pending / skipped in install-spawn context), the\nspeed-vs-quality preference outcome (persisted with its preset / skipped_non_interactive /\nskipped_no_answer / pending / skipped in install-spawn context), and the `/learn-repository`\nrecommendation.\n",
|
|
18
|
+
"learn-repository.md": "Learn and document all configuration fields for the repository by running parallel research agents.\n\n$ARGUMENTS\n\n---\n\n# Instructions\n\nThis command is recipe-driven. Do not call MCP tools directly -- the recipe determines which tools to call and with what parameters.\n\n1. This command takes no arguments.\n\n2. **Before executing any recipe step**, tell the user what they are about to sit through and why it\n is worth it:\n\n ```\n Learning this repository. This takes a while — the research agents read the actual codebase, and\n all the unlearned fields are researched in parallel, so the wait is roughly the slowest single\n field rather than the sum of all of them. Fields that are already populated are skipped entirely.\n\n What this buys you: these fields are what ground Bridge's agents in THIS codebase. Planning,\n reviewing, and code generation all read them, so they follow your repository's actual\n architecture, testing, documentation, and correctness conventions instead of generic defaults.\n\n It runs unattended — there are no approval prompts during the run. You may be asked one batched\n question at the very end.\n ```\n\n Do not invent a specific number of minutes; the honest statement is the parallel-wait shape above.\n\n3. Call the `get_pipeline_recipe` MCP tool with:\n - `pipeline`: `\"learn-repository\"`\n\n If the tool returns an error, stop and report the failure.\n\n4. Read and strictly obey the `agent_instructions` field in the response. Execute each step in order, announcing each as **Step N of M: <description>**.\n\n Retain, for the closing summary: the consolidated research task's structured result (its per-field\n `status`, `character_count`, `condensed`, and `condensation_reason`), each upload step's result,\n and the final confirmation task's structured result. You will need all three — do not discard them\n as you go.\n\n5. After all steps complete, display a summary built from the results you retained:\n\n ```\n ## Learn Complete\n\n **Status**: Success / Completed with gaps / Failed at step N\n\n **Learned and applied**: <fields drafted this run and written to config>\n **Already populated (skipped)**: <fields skipped because they already had a value>\n **Condensed to fit the field limit**: <field — reason it was condensed, per field>\n **Gaps**: <fields whose research or upload failed, each named with its reason>\n **Confirmation**: <approved / applied / declined / pending human input / not applicable, per field>\n\n Review or edit any of these on the **Project Configuration** page, under **Code Writer Settings**\n for the learned instructions and **MCP Validation Manuals** for the manual selection. Bridge's\n agents read whatever is stored there, so correcting a wrong conclusion there changes their\n behavior.\n ```\n\n Rules for the summary:\n\n - A run where some fields failed but others applied is **`Completed with gaps`**, not `Failed`.\n Name every gap explicitly — an unnamed gap is worse than a failed run, because the user believes\n the field was learned.\n - Report confirmation candidates that could not be presented in a headless session with the exact\n phrase `pending human input`.\n - Every field that was condensed must appear with the reason it was condensed.\n\n6. **After** the `## Learn Complete` summary above is fully displayed, close with the same concise\n capability report `/install-bridge` renders (BAPI-658, AC-9). This is the recipe's ONE exception to\n \"do not call MCP tools directly\": call the `get_install_manifest` MCP tool EXACTLY ONCE here,\n directly, with no arguments beyond what it requires — never through the recipe, never a second time,\n and never to apply or change any configuration.\n\n The report is structurally and visually SUBORDINATE to `## Learn Complete` above it — it is a\n closing addendum, not a replacement for or a distraction from the learn summary's own status,\n fields, gaps, and confirmation outcome.\n\n If the `get_install_manifest` call errors, or its response has no `concise_tool_capabilities` field,\n or that field is present but malformed (not the server's `{id, name, tools, more_count}` tier\n shape), print exactly this line and stop — do not attempt the report in any other form:\n\n ```\n capability report unavailable — run /install-bridge to see it\n ```\n\n Never substitute documentation, the complete `tool_capabilities` catalog, a remembered tool list, or\n an inferred capability category for a missing or malformed concise field — a hallucinated report\n during this trust-critical first run is worse than no report at all.\n\n Otherwise, render exactly one section, with this exact heading, from `concise_tool_capabilities`\n only:\n\n **What Bridge can help with**\n\n - Render each tier in the server's given order: \"Regularly useful\" first, then \"Occasionally\n useful\". Do not reorder, filter, re-tier, or drop a tier the server included, even if its `tools`\n array is empty.\n - Within a tier, list each tool's `display_name` only, in server order — no description,\n availability text, effect, dependency explanation, or variant detail.\n - Render the tier's `more_count` as plain, muted-style summary text (\"+N more\") — never as an\n expansion prompt, a link, or something requiring further action (it is not interactive or\n expandable). Omit the \"+N more\" line entirely when `more_count` is `0`.\n - Do not locally filter, count, regroup, infer availability, write configuration, or fall back to\n the complete `tool_capabilities` collection for this section under any circumstance.\n",
|
|
19
19
|
"parse-repository.md": "Queue a background job to parse and index the repository for Bridge API's AI agents.\n\n$ARGUMENTS\n\n---\n\n# Instructions\n\nExecute all steps in this command as a simple linear sequence of MCP tool calls.\n\n## Step 1 — Parse Arguments\n\nParse `$ARGUMENTS` for an optional `directory_path` argument (a subdirectory path to scope the parse to, e.g., `src/python`). If no argument is provided, the entire repository will be parsed. If `$ARGUMENTS` is provided but invalid (e.g., contains special characters that suggest it's not a path), report an error.\n\n## Step 2 — Queue Parse Job\n\nCall the `parse_repository` MCP tool with:\n- `directory_path`: set to the parsed `directory_path` from Step 1 if provided, otherwise omit the parameter\n\nIf the response indicates parsing is already in progress, display:\n\n```\nRepository parsing is already in progress. A previous parse job has not yet completed.\n\nRun `/check-parse-status` to monitor progress, or wait a few minutes and try again.\n```\n\nStop and do not proceed to the summary.\n\nIf the call fails or returns an error, stop immediately and display:\n\n```\nFailed to queue parse job: <error message from the tool>\n```\n\n## Summary\n\nOn successful queuing, display:\n\n```\nRepository parse job queued successfully.\n\nScope: <entire repository or directory_path if provided>\n\nProcessing typically takes several minutes for large repositories.\nRun `/check-parse-status` to monitor progress.\n```\n\nAfter the parse completes, AI-generated plans and clarifying questions will reflect the latest code changes.\n",
|
|
20
20
|
"plan-epic.md": "Plan an epic by decomposing it into sub-tasks with structured exploration documents.\n\n$ARGUMENTS\n\n---\n\n# Instructions\n\nThis command is recipe-driven. Do not call MCP tools directly -- the recipe determines which tools to call and with what parameters.\n\n## Stage 0 — Setup\n\n1. **Parse arguments**: Extract the input from `$ARGUMENTS`. Trim any surrounding whitespace. If the input is empty or whitespace-only, stop immediately and display:\n ```\n Usage: /plan-epic <description of the epic or Jira key>\n ```\n\n2. **Jira key detection**: If the input matches a Jira key pattern (`[A-Z]+-\\d+`), call the `get_ticket` MCP tool with that key to fetch the epic description. Use the ticket's description as the `epic_description`, and set `epic_key` to that Jira key. If the input does not match a Jira key, use the free-form text directly as the `epic_description` and set `epic_key` to an empty string `\"\"` (there is no Jira epic to update). The recipe uses `epic_key` to decide whether to post the goals/NFRs + recommended implementation order as a comment on the epic.\n\n3. **Generate slug**: Create a kebab-case slug from the epic description — take the first 6-8 meaningful words, strip non-alphanumeric characters (except hyphens), lowercase, and truncate to 60 characters. This becomes the `epic_slug`.\n\n4. **Directory existence check**: Call the `get_docs_dir` MCP tool (no parameters) to get the docs directory path. Then run a terminal command to check if the directory `{docs_dir}/epic-plans/{epic_slug}` already exists:\n ```\n test -d {docs_dir}/epic-plans/{epic_slug} && echo \"exists\" || echo \"not_found\"\n ```\n If the directory exists, append `-{unix_timestamp}` to the `epic_slug` (e.g., `add-auth-provider-support-1710000000`).\n\n## Stage 1 — Execution\n\n5. Call the `get_pipeline_recipe` MCP tool with:\n - `pipeline`: `\"plan-epic\"`\n - `variables`: `{ \"epic_description\": \"<resolved_description>\", \"epic_slug\": \"<slug>\", \"epic_key\": \"<jira_key_or_empty_string>\" }`\n\n Note: Do NOT pass `docs_dir` in variables — it is auto-injected by the pipeline system.\n\n If the tool returns an error, stop and report the failure.\n\n6. Read and strictly obey the `agent_instructions` field in the response. Execute each step in order, announcing each as **Step N of M: <description>**.\n\n7. After all steps complete, display a summary:\n ```\n ## Pipeline Complete\n\n **Epic**: <first 80 characters of epic_description>...\n **Slug**: <epic_slug>\n **Output**: <docs_dir>/epic-plans/<epic_slug>/overview.md\n **Steps executed**: N of M\n **Status**: Success / Failed at step N\n ```\n",
|
|
21
21
|
"plan-ticket.md": "Generate an implementation plan for a Jira ticket, wait for the result, and save it locally.\n\n$ARGUMENTS\n\n---\n\n# Instructions\n\nExecute all steps in this command as a simple linear sequence of MCP tool calls.\n\n## Step 1 — Parse Arguments\n\n1. **Parse `$ARGUMENTS`**: Extract a required `ticket_key`, an optional `--second-opinion` flag, and an optional `--provider` flag.\n - Split `$ARGUMENTS` on whitespace.\n - If `--second-opinion` appears followed by a provider name (one of `openai`, `anthropic`, `gemini`), capture that provider as `second_opinion_value`.\n - If `--second-opinion` appears without a provider name following it (or is the last token), set `second_opinion_value = \"auto\"`.\n - If `--second-opinion` is absent, set `second_opinion_value = null`.\n - If `--provider` appears followed by a provider name (one of `openai`, `anthropic`, `gemini`), capture that provider as `provider_value`.\n - If `--provider` appears without a valid provider name following it (or is the last token), stop immediately and report: \"Usage error: --provider requires a provider name (openai, anthropic, or gemini).\"\n - If `--provider` is absent, set `provider_value = null`.\n - If both `--second-opinion` and `--provider` are present, `--second-opinion` takes precedence (set `provider_value = null`).\n - The remaining token (after removing flags and their arguments) is the `ticket_key`.\n - If `ticket_key` is empty or missing, stop immediately and display:\n\n ```\n Usage: /plan-ticket <ticket_key> [--second-opinion [provider]] [--provider <name>] (e.g., /plan-ticket BAPI-150)\n ```\n\n## Step 2 — Resolve Docs Directory\n\nCall the `get_docs_dir` MCP tool (no parameters). Store the returned path as `docs_dir`.\n\n## Step 3 — Generate Plan\n\nCall the `request_plan_generation` MCP tool with:\n- `ticket_number`: the parsed `ticket_key`\n- `wait_for_result`: `true`\n- `save_locally`: `true`\n- `second_opinion`: set to `second_opinion_value` if non-null; omit entirely if null\n- `provider`: set to `provider_value` if non-null; omit entirely if null\n\nThis step may take 1-5 minutes while the backend processes the plan.\n\nIf the tool returns an error, stop immediately and display:\n\n```\nPlan generation failed: <error message from the tool>\n```\n\n## Step 4 — Confirm Success\n\nDisplay a confirmation message:\n\n```\nPlan generated successfully for <ticket_key>\nSaved to: {docs_dir}/plans/<ticket_key>-plan.md\n```\n\n## Final Summary\n\nDisplay a summary block:\n\n```\n## Plan Generation Report\n\n- **Ticket**: <ticket_key>\n- **Plan Status**: Generated successfully\n- **Local File**: {docs_dir}/plans/<ticket_key>-plan.md\n```\n\nOn failure at any step, stop immediately, display which step failed and the error details, and do not proceed.\n",
|
|
@@ -896,6 +896,71 @@ export async function transitionEpicDispatch(access, request, fetchImpl = global
|
|
|
896
896
|
const parsed = await fetchConductorJsonPostWithTimeout(url, conductorPostHeaders(access), body, CONDUCTOR_FETCH_TIMEOUT_MS, fetchImpl);
|
|
897
897
|
return parsed;
|
|
898
898
|
}
|
|
899
|
+
const SHADOW_FRESHNESS_VERDICTS = new Set([
|
|
900
|
+
"not_applicable",
|
|
901
|
+
"covered",
|
|
902
|
+
"stale",
|
|
903
|
+
"failed",
|
|
904
|
+
]);
|
|
905
|
+
function parseShadowMergeReconcileResult(parsed) {
|
|
906
|
+
const obj = (parsed ?? {});
|
|
907
|
+
return {
|
|
908
|
+
applies: obj.applies === true,
|
|
909
|
+
scheduled: obj.scheduled === true,
|
|
910
|
+
reasonCode: typeof obj.reason_code === "string" ? obj.reason_code : "unknown",
|
|
911
|
+
shadowRepoName: typeof obj.shadow_repo_name === "string" ? obj.shadow_repo_name : null,
|
|
912
|
+
requiredCommitSha: typeof obj.required_commit_sha === "string" ? obj.required_commit_sha : null,
|
|
913
|
+
lifecycleState: typeof obj.lifecycle_state === "string" ? obj.lifecycle_state : null,
|
|
914
|
+
};
|
|
915
|
+
}
|
|
916
|
+
function parseShadowDispatchFreshnessResult(parsed) {
|
|
917
|
+
const obj = (parsed ?? {});
|
|
918
|
+
// Fail closed: any unrecognized/malformed verdict is treated as `stale` so a
|
|
919
|
+
// malformed control-plane response never opens the dispatch gate.
|
|
920
|
+
const rawVerdict = typeof obj.verdict === "string" ? obj.verdict : "";
|
|
921
|
+
const verdict = SHADOW_FRESHNESS_VERDICTS.has(rawVerdict)
|
|
922
|
+
? rawVerdict
|
|
923
|
+
: "stale";
|
|
924
|
+
return {
|
|
925
|
+
verdict,
|
|
926
|
+
reasonCode: typeof obj.reason_code === "string" ? obj.reason_code : verdict,
|
|
927
|
+
lifecycleState: typeof obj.lifecycle_state === "string" ? obj.lifecycle_state : null,
|
|
928
|
+
indexedCommitSha: typeof obj.indexed_commit_sha === "string" ? obj.indexed_commit_sha : null,
|
|
929
|
+
shadowRepoName: typeof obj.shadow_repo_name === "string" ? obj.shadow_repo_name : null,
|
|
930
|
+
lastError: typeof obj.last_error === "string" ? obj.last_error : null,
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
/**
|
|
934
|
+
* POST the shadow merge-reconcile endpoint: ask the backend to record the current
|
|
935
|
+
* feature-branch head and schedule a shadow re-parse after an observed merge. All
|
|
936
|
+
* DB/VCS work happens server-side; the caller passes only the epic run and (for
|
|
937
|
+
* correlation) the merged ticket key.
|
|
938
|
+
*/
|
|
939
|
+
export async function reconcileShadowMerge(access, request, fetchImpl = globalThis.fetch) {
|
|
940
|
+
requireNonEmptyString(request.epicKey);
|
|
941
|
+
const url = buildConductorJiraUrl(access.baseUrl, `${epicRunApiPath(request.epicKey)}/shadow/merge-reconcile`);
|
|
942
|
+
const body = JSON.stringify({
|
|
943
|
+
repo_name: access.repoName,
|
|
944
|
+
merged_ticket_key: request.mergedTicketKey ?? null,
|
|
945
|
+
});
|
|
946
|
+
const parsed = await fetchConductorJsonPostWithTimeout(url, conductorPostHeaders(access), body, CONDUCTOR_FETCH_TIMEOUT_MS, fetchImpl);
|
|
947
|
+
return parseShadowMergeReconcileResult(parsed);
|
|
948
|
+
}
|
|
949
|
+
/**
|
|
950
|
+
* POST the per-ticket shadow dispatch-freshness endpoint. Returns the backend's
|
|
951
|
+
* authoritative commit-watermark verdict for one ready ticket. The caller treats
|
|
952
|
+
* `covered`/`not_applicable` as dispatchable, `stale` as hold, and `failed` as a
|
|
953
|
+
* terminal shadow failure (the backend has already persisted the blocked state).
|
|
954
|
+
*/
|
|
955
|
+
export async function fetchShadowDispatchFreshness(access, request, fetchImpl = globalThis.fetch) {
|
|
956
|
+
requireNonEmptyString(request.epicKey);
|
|
957
|
+
requireNonEmptyString(request.ticketKey);
|
|
958
|
+
requireNoSlashPathSegment(request.ticketKey);
|
|
959
|
+
const url = buildConductorJiraUrl(access.baseUrl, `${epicRunApiPath(request.epicKey)}/tickets/${encodeURIComponent(request.ticketKey)}/shadow-freshness`);
|
|
960
|
+
const body = JSON.stringify({ repo_name: access.repoName });
|
|
961
|
+
const parsed = await fetchConductorJsonPostWithTimeout(url, conductorPostHeaders(access), body, CONDUCTOR_FETCH_TIMEOUT_MS, fetchImpl);
|
|
962
|
+
return parseShadowDispatchFreshnessResult(parsed);
|
|
963
|
+
}
|
|
899
964
|
/**
|
|
900
965
|
* POST the immutable plan blob to the durable-store endpoint. The blob is
|
|
901
966
|
* written once per `(epic_run_id, plan_version)` and never mutated — a
|
|
@@ -928,6 +993,38 @@ export async function storeEpicPlan(access, request, fetchImpl = globalThis.fetc
|
|
|
928
993
|
* monotonic constraint violation). Other errors throw a sanitized
|
|
929
994
|
* {@link ConductorBridgeApiError}.
|
|
930
995
|
*/
|
|
996
|
+
/**
|
|
997
|
+
* Parse a successful approve-plan body into an {@link ApproveEpicPlanResult}.
|
|
998
|
+
* The optional `feature_branch_provisioning` object is carried through verbatim
|
|
999
|
+
* (server snake_case fields) only when present and well-shaped; a missing or
|
|
1000
|
+
* malformed object simply yields the base `{ ok, plan_hash }` result, never a
|
|
1001
|
+
* throw — the no-feature and feature paths share one parser.
|
|
1002
|
+
*/
|
|
1003
|
+
function parseApproveEpicPlanSuccess(parsed) {
|
|
1004
|
+
const record = (parsed ?? {});
|
|
1005
|
+
const result = {
|
|
1006
|
+
ok: true,
|
|
1007
|
+
plan_hash: record["plan_hash"],
|
|
1008
|
+
};
|
|
1009
|
+
const prov = record["feature_branch_provisioning"];
|
|
1010
|
+
if (prov && typeof prov === "object" && !Array.isArray(prov)) {
|
|
1011
|
+
const p = prov;
|
|
1012
|
+
if ((p["status"] === "created" || p["status"] === "already_exists") &&
|
|
1013
|
+
typeof p["feature_branch"] === "string" &&
|
|
1014
|
+
typeof p["source_branch"] === "string" &&
|
|
1015
|
+
typeof p["source_sha"] === "string" &&
|
|
1016
|
+
typeof p["remote_head_sha"] === "string") {
|
|
1017
|
+
result.featureBranchProvisioning = {
|
|
1018
|
+
status: p["status"],
|
|
1019
|
+
feature_branch: p["feature_branch"],
|
|
1020
|
+
source_branch: p["source_branch"],
|
|
1021
|
+
source_sha: p["source_sha"],
|
|
1022
|
+
remote_head_sha: p["remote_head_sha"],
|
|
1023
|
+
};
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
return result;
|
|
1027
|
+
}
|
|
931
1028
|
export async function approveEpicPlan(access, request, fetchImpl = globalThis.fetch) {
|
|
932
1029
|
requireNonEmptyString(request.epicKey);
|
|
933
1030
|
requirePositiveSafeInteger(request.planVersion);
|
|
@@ -938,7 +1035,7 @@ export async function approveEpicPlan(access, request, fetchImpl = globalThis.fe
|
|
|
938
1035
|
});
|
|
939
1036
|
try {
|
|
940
1037
|
const parsed = await fetchConductorJsonPostWithTimeout(url, conductorPostHeaders(access), body, CONDUCTOR_FETCH_TIMEOUT_MS, fetchImpl);
|
|
941
|
-
return parsed;
|
|
1038
|
+
return parseApproveEpicPlanSuccess(parsed);
|
|
942
1039
|
}
|
|
943
1040
|
catch (error) {
|
|
944
1041
|
if (error instanceof ConductorBridgeApiError && error.status === 409) {
|
|
@@ -154,6 +154,34 @@ export async function reconcileEpic(access, observed, plan, deps, supervisorConf
|
|
|
154
154
|
// in every case implementation must NOT dispatch this tick.
|
|
155
155
|
continue;
|
|
156
156
|
}
|
|
157
|
+
// BAPI-654 (T2): commit-watermark dispatch gate. A DAG-DEPENDENT ticket must
|
|
158
|
+
// not claim a dispatch key until the backend proves the run's shadow index
|
|
159
|
+
// covers every declared predecessor's merge watermark. Dependency-free nodes
|
|
160
|
+
// bypass the gate entirely so independent tickets keep their parallel behavior.
|
|
161
|
+
const declaredDeps = plan.tickets.find((t) => t.ticket_key === ticketKey)?.depends_on ?? [];
|
|
162
|
+
if (declaredDeps.length > 0 && deps.checkShadowFreshness) {
|
|
163
|
+
let verdict;
|
|
164
|
+
try {
|
|
165
|
+
verdict = await deps.checkShadowFreshness(ticketKey);
|
|
166
|
+
}
|
|
167
|
+
catch (err) {
|
|
168
|
+
// Fail closed: a freshness API error / malformed response never opens the
|
|
169
|
+
// gate. Hold this tick without claiming a key or consuming any counter.
|
|
170
|
+
verdict = "stale";
|
|
171
|
+
deps.log(`[epic-reconcile] shadow-freshness error for ${ticketKey}: ${safeDiagnosticMessage(err, "freshness error")}; holding`);
|
|
172
|
+
}
|
|
173
|
+
if (verdict === "stale") {
|
|
174
|
+
deps.log(`[epic-reconcile] holding ${ticketKey}: shadow watermark stale`);
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
if (verdict === "failed") {
|
|
178
|
+
// Terminal shadow failure — the backend has already persisted the run's
|
|
179
|
+
// blocked state; emit a diagnostic and skip dispatch (no key claimed).
|
|
180
|
+
deps.log(`[epic-reconcile] shadow index failed for ${ticketKey}; run blocked, skipping dispatch`);
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
// "covered" | "not_applicable" fall through to the normal claim/dispatch.
|
|
184
|
+
}
|
|
157
185
|
let claimResult;
|
|
158
186
|
try {
|
|
159
187
|
claimResult = await deps.claimDispatchKey(observed.epic_key, ticketKey, observed.plan_version);
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* Epic Run TS client (already available in bridge-api-client.ts as of BAPI-407).
|
|
19
19
|
*/
|
|
20
20
|
import { spawnSync } from "child_process";
|
|
21
|
-
import { resolveConductorBridgeApiAccess, claimEpicSupervisionLease, fetchEpicRunState, advanceEpicTicketStatus, createEpicTicketStatus, updateEpicRunStatus, recordEpicDispatch, transitionEpicDispatch, fetchParseStatus, triggerRepositoryParse, getEpicPlan, buildEpicDispatchKey, fetchEffectiveSupervisorConfig, fetchEffectiveSupervisorSetup, remediateEpicTicket, deletePullRequestBranch, transitionJiraStatus, safeDiagnosticMessage, } from "./bridge-api-client.js";
|
|
21
|
+
import { resolveConductorBridgeApiAccess, claimEpicSupervisionLease, fetchEpicRunState, advanceEpicTicketStatus, createEpicTicketStatus, updateEpicRunStatus, recordEpicDispatch, transitionEpicDispatch, fetchParseStatus, triggerRepositoryParse, getEpicPlan, buildEpicDispatchKey, fetchEffectiveSupervisorConfig, fetchEffectiveSupervisorSetup, remediateEpicTicket, deletePullRequestBranch, transitionJiraStatus, reconcileShadowMerge, fetchShadowDispatchFreshness, safeDiagnosticMessage, } from "./bridge-api-client.js";
|
|
22
22
|
import { processGateMetMerge } from "./supervisor-merge.js";
|
|
23
23
|
import { observePrCiOnce } from "./pr-ci-producer.js";
|
|
24
24
|
import { runGhCommand } from "./pr-discovery.js";
|
|
@@ -764,6 +764,23 @@ export async function runEpicTick(options, deps = {}) {
|
|
|
764
764
|
// in the SAME tick as the trigger, so parse_pending is always observable
|
|
765
765
|
// after merge before any success/failure fold.
|
|
766
766
|
try {
|
|
767
|
+
// BAPI-654 (T2): additively reconcile the run's shadow index for this
|
|
768
|
+
// observed feature-branch merge. The backend records the current
|
|
769
|
+
// feature-branch head and schedules a shadow re-parse; when no shadow
|
|
770
|
+
// applies it is a benign no-op and the canonical parse-after-merge path
|
|
771
|
+
// below runs unchanged (Step 9.10). Fail-open: a reconcile error never
|
|
772
|
+
// blocks the canonical trigger.
|
|
773
|
+
if (deps.reconcileShadowMerge) {
|
|
774
|
+
try {
|
|
775
|
+
const shadowResult = await deps.reconcileShadowMerge(access, epic_key, ticketKey);
|
|
776
|
+
if (shadowResult.applies) {
|
|
777
|
+
log(`[epic-runtime] shadow merge-reconcile for ${ticketKey}: scheduled=${shadowResult.scheduled} reason=${shadowResult.reasonCode}`);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
catch (err) {
|
|
781
|
+
errorLog(`[epic-runtime] shadow merge-reconcile failed for ${ticketKey}: ${safeDiagnosticMessage(err, "reconcile error")}`);
|
|
782
|
+
}
|
|
783
|
+
}
|
|
767
784
|
await triggerParseFn(access);
|
|
768
785
|
emitConductorEventFn({
|
|
769
786
|
source: PARSE_WAIT_EVENT_SOURCE,
|
|
@@ -1446,6 +1463,11 @@ export async function runEpicTick(options, deps = {}) {
|
|
|
1446
1463
|
// (built from the dispatch ledger) tracks the fresh worker.
|
|
1447
1464
|
await dispatchSeam(ek, tk, attempt);
|
|
1448
1465
|
},
|
|
1466
|
+
// BAPI-654 (T2): wire the backend per-ticket commit-watermark gate into the
|
|
1467
|
+
// reconcile pass. Absent runtime seam ⇒ undefined ⇒ gate disabled (legacy).
|
|
1468
|
+
checkShadowFreshness: deps.checkShadowFreshness
|
|
1469
|
+
? (tk) => deps.checkShadowFreshness(access, observed.epic_key, tk)
|
|
1470
|
+
: undefined,
|
|
1449
1471
|
};
|
|
1450
1472
|
const reconcileResult = await reconcileEpic(access, observed, plan, reconcileDeps, remediationConfig);
|
|
1451
1473
|
log(`[epic-tick] reconcile done: epic=${epic_key} ` +
|
|
@@ -1928,6 +1950,11 @@ export async function buildProductionEpicRuntimeDeps(epicKey) {
|
|
|
1928
1950
|
fetchLocalEvents,
|
|
1929
1951
|
escalateOnce,
|
|
1930
1952
|
postActionWaitSeam,
|
|
1953
|
+
// BAPI-654 (T2): backend-authoritative shadow ops. Like fetchPlan, they
|
|
1954
|
+
// receive `access` as a parameter, so they close over no credentials.
|
|
1955
|
+
reconcileShadowMerge: (acc, ek, mergedTicketKey) => reconcileShadowMerge(acc, { epicKey: ek, mergedTicketKey }),
|
|
1956
|
+
checkShadowFreshness: async (acc, ek, tk) => (await fetchShadowDispatchFreshness(acc, { epicKey: ek, ticketKey: tk }))
|
|
1957
|
+
.verdict,
|
|
1931
1958
|
// BAPI-442 seams are wired at the reconcileDeps level inside runEpicTick
|
|
1932
1959
|
// (they need the per-tick `access` and `prBindings` closure). The factory
|
|
1933
1960
|
// returns the dispatchSeam with isReReview support; the other two seams are
|