@bridge_gpt/mcp-server 0.2.69 → 0.2.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/README.md +140 -292
  2. package/build/agent-launchers/claude-executor-adapter.js +17 -4
  3. package/build/agents.generated.js +4 -4
  4. package/build/artifact-diff.js +110 -0
  5. package/build/bin-entrypoint.js +36 -0
  6. package/build/bridge-permissions.js +23 -4
  7. package/build/claude-review-workflow-drift.js +3 -3
  8. package/build/codex-bridge-profile.js +1 -1
  9. package/build/commands.generated.js +6 -6
  10. package/build/conductor/bridge-api-client.js +598 -19
  11. package/build/conductor/bring-up-facts.js +9 -9
  12. package/build/conductor/doctor.js +466 -18
  13. package/build/conductor/epic-child-continuation.js +54 -3
  14. package/build/conductor/epic-implementer-cli.js +132 -34
  15. package/build/conductor/epic-runtime.js +2 -2
  16. package/build/conductor/epic-state.js +1 -1
  17. package/build/conductor/hook-runtime-qualification.js +1 -1
  18. package/build/conductor/install-doctor.js +4 -2
  19. package/build/conductor/launch-need-probes.js +337 -0
  20. package/build/conductor/local-merge.js +241 -56
  21. package/build/conductor/merge-credential.js +172 -0
  22. package/build/conductor/merge-execution-eligibility.js +1 -1
  23. package/build/conductor/merged-child-check.js +95 -0
  24. package/build/conductor/plan-trim.js +30 -0
  25. package/build/conductor/pr-ci-producer.js +1 -1
  26. package/build/conductor/readiness-cli.js +11 -6
  27. package/build/conductor/readiness-prerequisites.js +41 -0
  28. package/build/conductor/readiness-sections.js +345 -55
  29. package/build/conductor/readiness.js +99 -87
  30. package/build/conductor/recovery-cli.js +606 -21
  31. package/build/conductor/recovery-operations.js +306 -7
  32. package/build/conductor/repair-contract.js +47 -2
  33. package/build/conductor/repository-workflow-remedy.js +8 -6
  34. package/build/conductor/stored-merge-credential.js +76 -0
  35. package/build/conductor/stored-merge-token-probe.js +28 -0
  36. package/build/conductor/temporary-plan.js +50 -0
  37. package/build/conductor/wait-run-cli.js +759 -0
  38. package/build/conductor/worker-database-doctor-inspector.js +49 -0
  39. package/build/conductor-bin.js +2 -2
  40. package/build/conductor-claude-hook-bin.js +2 -2
  41. package/build/conductor-claude-hook-removed-stub-bin.js +2 -5
  42. package/build/conductor-removed-stub-bin.js +2 -5
  43. package/build/credential-store.js +356 -13
  44. package/build/docs.generated.js +26 -13
  45. package/build/doctor.js +94 -32
  46. package/build/drive-epic-help.js +212 -0
  47. package/build/drive-epic.js +1840 -373
  48. package/build/epic-ci-workflow-drift.js +77 -0
  49. package/build/epic-ci-workflow-install.js +183 -0
  50. package/build/epic-ci-workflow.js +157 -0
  51. package/build/epic-contract-context.js +326 -0
  52. package/build/epic-implementer-bin.js +57 -41
  53. package/build/epic-implementer-claude-hook-bin.js +2 -2
  54. package/build/escalation-reachability.js +134 -0
  55. package/build/executor/cleanup-report.js +99 -0
  56. package/build/executor/cli.js +12 -1
  57. package/build/executor/completion-contract.js +66 -11
  58. package/build/executor/completion-supervision.js +99 -0
  59. package/build/executor/delivery-outcome.js +179 -0
  60. package/build/executor/deps.js +3 -0
  61. package/build/executor/env.js +71 -0
  62. package/build/executor/failure-reason-codes.js +126 -0
  63. package/build/executor/heartbeat.js +38 -0
  64. package/build/executor/http-client.js +72 -0
  65. package/build/executor/integration-job.js +298 -5
  66. package/build/executor/job-errors.js +36 -6
  67. package/build/executor/job-runner.js +720 -36
  68. package/build/executor/likely-tests.js +36 -2
  69. package/build/executor/merge-job.js +145 -2
  70. package/build/executor/observation.js +73 -4
  71. package/build/executor/permissions.js +193 -97
  72. package/build/executor/preflight.js +14 -3
  73. package/build/executor/process-containment.js +28 -2
  74. package/build/executor/prompt-spec.js +50 -5
  75. package/build/executor/rebase-finalization.js +38 -3
  76. package/build/executor/reconcile-overlap-job.js +35 -7
  77. package/build/executor/required-work-ledger.js +236 -81
  78. package/build/executor/resume-pre-spawn.js +38 -1
  79. package/build/executor/runner.js +91 -3
  80. package/build/executor/test-clock.js +8 -0
  81. package/build/executor/types.js +37 -2
  82. package/build/executor/worker-cli-launcher.js +259 -0
  83. package/build/executor/worker-command.js +41 -4
  84. package/build/executor/worker-database-target.js +91 -0
  85. package/build/executor/worker-database.js +908 -0
  86. package/build/executor/worker-finalization.js +106 -0
  87. package/build/executor/worker-guard-hook.js +203 -0
  88. package/build/executor/worker-resources.js +47 -0
  89. package/build/executor/worker-verification-guidance.js +15 -2
  90. package/build/executor/worktree.js +303 -15
  91. package/build/executor-launch.js +11 -1
  92. package/build/generated-assets-doctor.js +9 -1
  93. package/build/generated-assets-health.js +13 -4
  94. package/build/generated-assets-legacy.js +39 -0
  95. package/build/generated-assets-paths.js +12 -1
  96. package/build/generated-assets-repair.js +4 -1
  97. package/build/generated-assets-test-support.js +12 -0
  98. package/build/github-remote.js +33 -0
  99. package/build/github-sign-in.js +62 -0
  100. package/build/index.js +1067 -536
  101. package/build/init.js +39 -1
  102. package/build/install-bridge-conductor-gate.js +78 -0
  103. package/build/install-bridge-conductor-setup.js +413 -0
  104. package/build/install-bridge-conductor.js +1226 -267
  105. package/build/install-bridge.js +376 -60
  106. package/build/mcp-install-state.js +8 -2
  107. package/build/merge-pull-request.js +6 -0
  108. package/build/merge-workflow-install.js +763 -345
  109. package/build/phase-result-artifacts.js +75 -20
  110. package/build/pipeline-orchestrator.js +247 -4
  111. package/build/pipelines.generated.js +35 -35
  112. package/build/plan-discovery.js +15 -2
  113. package/build/plan-execution-ledger.js +306 -17
  114. package/build/plan-phase-routing.js +48 -13
  115. package/build/plane/cli.js +105 -51
  116. package/build/plane/defaults.js +3 -0
  117. package/build/plane/plane-freshness.js +294 -0
  118. package/build/plane/schema-compatibility.js +137 -0
  119. package/build/plane/shutdown-deps.js +38 -0
  120. package/build/plane/shutdown.js +83 -8
  121. package/build/plane/terminal-run-cleanup.js +303 -0
  122. package/build/printed-remedy-commands.js +26 -4
  123. package/build/readiness-check.js +6 -2
  124. package/build/readiness-prerequisites.generated.js +532 -0
  125. package/build/readme.generated.js +1 -1
  126. package/build/setup-epic.js +400 -30
  127. package/build/shared-resources.generated.js +5 -0
  128. package/build/start-tickets.js +28 -15
  129. package/build/test-command-detection.js +247 -0
  130. package/build/test-command-validation.js +73 -0
  131. package/build/testing-config-client.js +150 -0
  132. package/build/ticket-delivery-artifacts.js +258 -0
  133. package/build/ticket-delivery-enrollment.js +306 -0
  134. package/build/ticket-delivery.js +205 -0
  135. package/build/ticket-review-summary.js +14 -0
  136. package/build/upgrade-cli.js +13 -0
  137. package/build/v2-readiness-requirements.js +552 -0
  138. package/build/version.generated.js +3 -3
  139. package/build/worker-guard-hook-bin.js +2 -2
  140. package/build/worktree-core.js +22 -7
  141. package/docs/BRIDGE-DIRECTORY-README.md +48 -0
  142. package/docs/EPIC-IMPLEMENTER-INSTALL.md +12 -867
  143. package/docs/EPIC-IMPLEMENTER.md +104 -63
  144. package/docs/epic-implementer/getting-started.md +131 -0
  145. package/docs/epic-implementer/self-hosting.md +133 -0
  146. package/package.json +3 -3
  147. package/pipelines/idea-to-ticket.json +1 -1
  148. package/pipelines/implement-ticket.json +8 -8
  149. package/pipelines/review-ticket.json +2 -2
  150. package/design-assets/favicon/favicon-32x32.png +0 -0
  151. package/design-assets/just-logo-rough-draft.png +0 -0
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Fail-closed byte identity for the managed CI workflow (BAPI-1460).
3
+ *
4
+ * Unlike the merge workflow, the CI workflow's bytes depend on one input — the
5
+ * stored test command — so recognition cannot be a fixed SHA list. Instead a
6
+ * file is recognized as Bridge-rendered only when its ENTIRE bytes equal a
7
+ * re-render: of the current proposal, or of the command extracted from the
8
+ * file's own fixed `run:` line by a renderer version Bridge actually published.
9
+ * A version marker alone proves nothing; a hand-edited or hostile file can claim
10
+ * any version it likes.
11
+ *
12
+ * Leaf module: no filesystem, network, or subprocess access, and no YAML parser.
13
+ * Nothing here logs file content.
14
+ */
15
+ import { EPIC_CI_COMMAND_LINE_PREFIX, EPIC_CI_WORKFLOW_PATH, renderEpicImplementerCiWorkflow, } from "./epic-ci-workflow.js";
16
+ import { TestCommandValidationError, validateTestCommand } from "./test-command-validation.js";
17
+ /**
18
+ * Renderers for every OLDER published version, keyed by version.
19
+ *
20
+ * Empty on purpose: version 1 is the first release, and inventing a historical
21
+ * version would let a file that never shipped be "upgraded". When version 2
22
+ * ships, freeze version 1's renderer as a function here, unchanged, so its exact
23
+ * bytes stay recognizable.
24
+ */
25
+ export const EPIC_CI_WORKFLOW_HISTORICAL_RENDERERS = new Map();
26
+ /** Extract and validate the command from the fixed `run:` line; `null` if absent or bad. */
27
+ export function extractEpicCiWorkflowCommand(content) {
28
+ const lines = content.split("\n");
29
+ const commandLines = lines.filter((line) => line.startsWith(EPIC_CI_COMMAND_LINE_PREFIX + "\""));
30
+ if (commandLines.length !== 1)
31
+ return null;
32
+ const scalar = commandLines[0].slice(EPIC_CI_COMMAND_LINE_PREFIX.length);
33
+ let decoded;
34
+ try {
35
+ decoded = JSON.parse(scalar);
36
+ }
37
+ catch {
38
+ return null;
39
+ }
40
+ try {
41
+ return validateTestCommand(decoded);
42
+ }
43
+ catch (err) {
44
+ if (err instanceof TestCommandValidationError)
45
+ return null;
46
+ throw err;
47
+ }
48
+ }
49
+ /**
50
+ * Classify `existing` (`null` when absent) against the proposal for
51
+ * `proposedCommand`. Comparison is on raw bytes; line-ending differences are
52
+ * differences.
53
+ */
54
+ export function classifyEpicCiWorkflowContent(existing, proposedCommand) {
55
+ if (existing === null)
56
+ return "not_installed";
57
+ if (existing === renderEpicImplementerCiWorkflow(proposedCommand))
58
+ return "identical";
59
+ const extracted = extractEpicCiWorkflowCommand(existing);
60
+ if (extracted === null)
61
+ return "modified_or_unrecognized";
62
+ for (const render of EPIC_CI_WORKFLOW_HISTORICAL_RENDERERS.values()) {
63
+ if (existing === render(extracted))
64
+ return "managed_version_older";
65
+ }
66
+ if (existing === renderEpicImplementerCiWorkflow(extracted))
67
+ return "managed_command_differs";
68
+ return "modified_or_unrecognized";
69
+ }
70
+ /** Operator-facing gloss for each classification. Path-only; never the command. */
71
+ export const EPIC_CI_WORKFLOW_CLASSIFICATION_TEXT = {
72
+ not_installed: `${EPIC_CI_WORKFLOW_PATH} is not installed`,
73
+ identical: `${EPIC_CI_WORKFLOW_PATH} already matches the stored test command`,
74
+ managed_version_older: `${EPIC_CI_WORKFLOW_PATH} is an older Bridge-managed version and can be upgraded`,
75
+ managed_command_differs: `${EPIC_CI_WORKFLOW_PATH} was rendered by Bridge for a different test command and can be updated`,
76
+ modified_or_unrecognized: `${EPIC_CI_WORKFLOW_PATH} has been modified or is not a file Bridge rendered`,
77
+ };
@@ -0,0 +1,183 @@
1
+ /**
2
+ * The Epic Implementer CI workflow as an installable artifact set (BAPI-1460).
3
+ *
4
+ * One managed file, `.github/workflows/epic-implementer-ci.yml`, rendered from
5
+ * the repository's stored test command and delivered through the shared
6
+ * {@link installArtifactSet} engine: read-only preflight, consent, a temporary
7
+ * worktree, a managed branch, and one pull request into the default branch.
8
+ *
9
+ * ## The setup caller's sequence (BAPI-1461)
10
+ *
11
+ * 1. Detect or select a command (`detectTestCommand`), asking at most once.
12
+ * 2. Get consent to save the command, then save it with `PUT /setup/testing-config`
13
+ * (`writeStoredTestCommand`).
14
+ * 3. Read the authoritative command back with `GET /setup/testing-config`
15
+ * (`readStoredTestCommand`) and build the descriptor from THAT value with
16
+ * {@link buildEpicCiWorkflowArtifactSet} — never from the typed answer.
17
+ * 4. Get separate consent for the pull request and call {@link installEpicCiWorkflow}.
18
+ *
19
+ * A failed command read or write stops before any installation. Saving the
20
+ * command and installing the workflow are reported separately
21
+ * ({@link describeEpicCiSetupStatus}): a refused or failed install never undoes
22
+ * a stored command.
23
+ *
24
+ * The backend integration point for readiness (BAPI-1459) is
25
+ * `get_test_command(repo_name)` in `api/library/db/postgres_functions.py`. A
26
+ * runnable command is never derived from the worker prose fields.
27
+ */
28
+ import { EPIC_CI_GATE_JOB_TIMEOUT_MINUTES, EPIC_CI_TEST_JOB_TIMEOUT_MINUTES, EPIC_CI_WORKFLOW_PATH, EPIC_CI_WORKFLOW_RENDERER_VERSION, EPIC_IMPLEMENTER_CI_CHECK_NAME, renderEpicImplementerCiWorkflow, } from "./epic-ci-workflow.js";
29
+ import { EPIC_CI_WORKFLOW_CLASSIFICATION_TEXT, classifyEpicCiWorkflowContent, } from "./epic-ci-workflow-drift.js";
30
+ import { installArtifactSet, } from "./merge-workflow-install.js";
31
+ import { TestCommandValidationError, validateTestCommand } from "./test-command-validation.js";
32
+ /** Managed branch for a fresh installation. */
33
+ export const EPIC_CI_WORKFLOW_INSTALL_BRANCH = "bridge/epic-implementer-ci-v" + String(EPIC_CI_WORKFLOW_RENDERER_VERSION);
34
+ /**
35
+ * Managed branch for replacing an existing Bridge rendering: an older renderer
36
+ * version, or the current version rendered for a different stored command.
37
+ */
38
+ export const EPIC_CI_WORKFLOW_UPGRADE_BRANCH = "bridge/epic-implementer-ci-upgrade-v" + String(EPIC_CI_WORKFLOW_RENDERER_VERSION);
39
+ export const EPIC_CI_WORKFLOW_INSTALL_COMMIT_SUBJECT = "Add the Bridge Epic Implementer CI workflow";
40
+ export const EPIC_CI_WORKFLOW_UPGRADE_COMMIT_SUBJECT = "Update the Bridge Epic Implementer CI workflow";
41
+ /**
42
+ * Render the CI pull request body. Deterministic and secret-free. It describes
43
+ * the command contract but never embeds the command; the diff shows it.
44
+ */
45
+ export function renderEpicCiWorkflowPrBody(upgrade) {
46
+ return [
47
+ upgrade
48
+ ? "This pull request updates the Bridge Epic Implementer CI workflow to renderer version " +
49
+ String(EPIC_CI_WORKFLOW_RENDERER_VERSION) + " and the repository's current stored test command."
50
+ : "This pull request adds the Bridge Epic Implementer CI workflow, renderer version " +
51
+ String(EPIC_CI_WORKFLOW_RENDERER_VERSION) + ".",
52
+ "",
53
+ "The workflow publishes one required check, `" + EPIC_IMPLEMENTER_CI_CHECK_NAME + "`. The Bridge",
54
+ "Epic Implementer reads it before it merges a child pull request.",
55
+ "",
56
+ "## How it runs",
57
+ "",
58
+ "- On every pull request, it runs the repository's stored test command with bash from the",
59
+ " repository root, on `ubuntu-latest`, with a " + String(EPIC_CI_TEST_JOB_TIMEOUT_MINUTES) + "-minute limit.",
60
+ "- The command must be self-contained: the workflow installs no runtime or dependencies.",
61
+ "- It requests only `contents: read` and uses no secrets.",
62
+ "- A pull request from a fork never checks out or runs repository code. The gate passes for",
63
+ " it immediately and says so. The gate job itself has a " + String(EPIC_CI_GATE_JOB_TIMEOUT_MINUTES) + "-minute limit.",
64
+ "",
65
+ "## Files",
66
+ "",
67
+ "- `" + EPIC_CI_WORKFLOW_PATH + "`",
68
+ "",
69
+ "## What you must do",
70
+ "",
71
+ "Review the command in the diff, then merge this pull request yourself. Bridge never merges",
72
+ "it. The branch, the commit, and this pull request were created locally with your own git",
73
+ "and `gh`; Bridge performed no write to this repository.",
74
+ "",
75
+ ].join("\n");
76
+ }
77
+ /**
78
+ * The CI artifact set for one stored command. Build it from the command read
79
+ * back through `GET /setup/testing-config`, never from unsaved input.
80
+ */
81
+ export function buildEpicCiWorkflowArtifactSet(testCommand) {
82
+ return {
83
+ setName: "epic-ci-workflow",
84
+ installBranch: EPIC_CI_WORKFLOW_INSTALL_BRANCH,
85
+ upgradeBranch: EPIC_CI_WORKFLOW_UPGRADE_BRANCH,
86
+ installCommitSubject: EPIC_CI_WORKFLOW_INSTALL_COMMIT_SUBJECT,
87
+ upgradeCommitSubject: EPIC_CI_WORKFLOW_UPGRADE_COMMIT_SUBJECT,
88
+ installPrTitle: EPIC_CI_WORKFLOW_INSTALL_COMMIT_SUBJECT,
89
+ upgradePrTitle: EPIC_CI_WORKFLOW_UPGRADE_COMMIT_SUBJECT,
90
+ renderPrBody: renderEpicCiWorkflowPrBody,
91
+ validateOptions: () => {
92
+ try {
93
+ validateTestCommand(testCommand);
94
+ }
95
+ catch (err) {
96
+ if (err instanceof TestCommandValidationError)
97
+ return { ok: false, reason: "test_command_invalid" };
98
+ throw err;
99
+ }
100
+ return { ok: true, progress: [] };
101
+ },
102
+ artifacts: [
103
+ {
104
+ relPath: EPIC_CI_WORKFLOW_PATH,
105
+ render: () => renderEpicImplementerCiWorkflow(testCommand),
106
+ progressLabel: "workflow",
107
+ classify: (existing) => {
108
+ const classification = classifyEpicCiWorkflowContent(existing, testCommand);
109
+ const text = EPIC_CI_WORKFLOW_CLASSIFICATION_TEXT[classification];
110
+ if (classification === "modified_or_unrecognized") {
111
+ return { state: "conflict", reason: "workflow_modified_or_unrecognized", text };
112
+ }
113
+ // A complete Bridge rendering of a different command is not a hand
114
+ // edit: the stored command changed. Replace it through the same
115
+ // reviewed update PR as an older renderer version, so the workflow
116
+ // header's "save a new command and re-run the installer" holds.
117
+ if (classification === "managed_command_differs")
118
+ return { state: "managed_version_older", text };
119
+ return { state: classification, text };
120
+ },
121
+ },
122
+ ],
123
+ };
124
+ }
125
+ /** Install the CI workflow for `testCommand`. Returns the typed result; prints progress only. */
126
+ export async function installEpicCiWorkflow(testCommand, options, deps) {
127
+ return await installArtifactSet(buildEpicCiWorkflowArtifactSet(testCommand), options, deps);
128
+ }
129
+ const REFUSALS = new Set([
130
+ "workflow_modified_or_unrecognized",
131
+ "consent_declined",
132
+ "worktree_not_clean",
133
+ "test_command_invalid",
134
+ ]);
135
+ /** Map an install result to its status. `null` means the install was not attempted. */
136
+ export function epicCiInstallStatus(result) {
137
+ if (result === null)
138
+ return "not_attempted";
139
+ switch (result.outcome) {
140
+ case "installed-pr-opened":
141
+ case "upgrade-pr-opened":
142
+ return "pr_opened";
143
+ case "dry-run":
144
+ return "proposal_ready";
145
+ case "identical":
146
+ return "already_installed";
147
+ case "existing-pr":
148
+ return "setup_pr_already_open";
149
+ case "failed":
150
+ if (result.reason === "existing_pr_differs")
151
+ return "proposal_differs";
152
+ if (result.reason === "managed_branch_without_pr" || result.reason === "pr_open_failed") {
153
+ return "managed_branch_needs_recovery";
154
+ }
155
+ return REFUSALS.has(result.reason) ? "install_refused" : "install_failed";
156
+ }
157
+ }
158
+ /**
159
+ * Combine the command outcome and the install outcome into one status, keeping
160
+ * them separate: the command can be saved while the install is refused.
161
+ */
162
+ export function describeEpicCiSetupStatus(command, provenance, result) {
163
+ const status = {
164
+ command,
165
+ install: epicCiInstallStatus(result),
166
+ provenance,
167
+ branch: null,
168
+ prUrl: null,
169
+ compareUrl: null,
170
+ reason: null,
171
+ };
172
+ if (result === null)
173
+ return status;
174
+ if ("branch" in result && typeof result.branch === "string")
175
+ status.branch = result.branch;
176
+ if ("prUrl" in result && typeof result.prUrl === "string")
177
+ status.prUrl = result.prUrl;
178
+ if (result.outcome === "failed") {
179
+ status.reason = result.reason;
180
+ status.compareUrl = result.compareUrl ?? null;
181
+ }
182
+ return status;
183
+ }
@@ -0,0 +1,157 @@
1
+ /**
2
+ * The customer Epic Implementer CI workflow renderer (BAPI-1460).
3
+ *
4
+ * Renders `.github/workflows/epic-implementer-ci.yml` from the repository's ONE
5
+ * stored test command. The workflow publishes the single required context the
6
+ * Epic Implementer `ci` gate observes, `epic-implementer-ci / gate`: green when
7
+ * the command passes on a same-repository pull request, and green immediately,
8
+ * without running anything, for a fork pull request.
9
+ *
10
+ * ## Why a TypeScript renderer
11
+ *
12
+ * Same precedent as `merge-workflow.ts`: the compiled renderer is the file's
13
+ * only byte source, so "did Bridge render this?" has one answer.
14
+ *
15
+ * ## The command is data, never code in this process
16
+ *
17
+ * The command is written as ONE double-quoted YAML scalar produced by
18
+ * `JSON.stringify` (a JSON string is a valid YAML double-quoted scalar), on one
19
+ * fixed `run:` line. It is never placed in this process's own shell or git
20
+ * arguments. The renderer re-checks the stored-command rules and throws on a bad
21
+ * command; `${{` in particular is refused because Actions would evaluate an
22
+ * expression inside a `run:` value before the shell sees it.
23
+ *
24
+ * ## Safe by default
25
+ *
26
+ * `on: pull_request` only, `permissions: contents: read`, no secrets, no
27
+ * path filters, `ubuntu-latest`. The execution job runs only when the pull
28
+ * request head is this repository; a fork pull request never checks out or runs
29
+ * repository code. The checkout does not persist its token. The command must be
30
+ * self-contained: it installs whatever it needs, because the workflow provisions
31
+ * no runtime.
32
+ */
33
+ import { validateTestCommand } from "./test-command-validation.js";
34
+ /** Repository-relative path of the managed workflow. */
35
+ export const EPIC_CI_WORKFLOW_PATH = ".github/workflows/epic-implementer-ci.yml";
36
+ /** The renderer version written into the managed header. */
37
+ export const EPIC_CI_WORKFLOW_RENDERER_VERSION = 1;
38
+ /** The header marker key; the line is `# <key>: <version>`. */
39
+ export const EPIC_CI_WORKFLOW_VERSION_MARKER_KEY = "x-bridge-ci-renderer-version";
40
+ /** The workflow `name:`. */
41
+ export const EPIC_CI_WORKFLOW_NAME = "epic-implementer-ci";
42
+ /**
43
+ * The required check context, and the aggregator job's display `name:`.
44
+ *
45
+ * Must equal `EPIC_IMPLEMENTER_GATE_CHECK_NAME` in
46
+ * `api/library/github/workflow_inspection.py` and `CURRENT_GATE_NAME` in
47
+ * `mcp_server/src/conductor/readiness.ts`; a test asserts all three agree.
48
+ */
49
+ export const EPIC_IMPLEMENTER_CI_CHECK_NAME = "epic-implementer-ci / gate";
50
+ /** Bound on the execution job, in minutes. */
51
+ export const EPIC_CI_TEST_JOB_TIMEOUT_MINUTES = 30;
52
+ /** Bound on the aggregator job, in minutes. */
53
+ export const EPIC_CI_GATE_JOB_TIMEOUT_MINUTES = 5;
54
+ /** The fixed indentation and key that precede the command scalar. */
55
+ export const EPIC_CI_COMMAND_LINE_PREFIX = " run: ";
56
+ /**
57
+ * Characters `JSON.stringify` leaves raw that a YAML parser may read as a line
58
+ * break or a byte-order mark (YAML 1.1 treats U+0085, U+2028 and U+2029 as
59
+ * breaks), plus DEL and the C1 controls. They are escaped as `\uXXXX`, which is
60
+ * still valid JSON and a valid YAML double-quoted escape.
61
+ */
62
+ const YAML_UNSAFE_RAW = /[\u007f-\u009f\u2028\u2029\ufeff]/g;
63
+ /** Encode the command as one JSON string that is also a safe YAML scalar. */
64
+ export function encodeCommandScalar(testCommand) {
65
+ return JSON.stringify(testCommand).replace(YAML_UNSAFE_RAW, (ch) => "\\u" + ch.charCodeAt(0).toString(16).padStart(4, "0"));
66
+ }
67
+ /**
68
+ * Render the workflow for `testCommand`. Deterministic: the same command always
69
+ * gives the same bytes. Throws `TestCommandValidationError` for a bad command.
70
+ */
71
+ export function renderEpicImplementerCiWorkflow(testCommand) {
72
+ const command = validateTestCommand(testCommand);
73
+ const lines = [
74
+ "# ===========================================================================",
75
+ "# Epic Implementer CI gate — MANAGED FILE",
76
+ "#",
77
+ "# Generated by Bridge (`" + EPIC_CI_WORKFLOW_PATH + "`). Do not hand-edit: the",
78
+ "# installer compares this file byte for byte against what it renders and",
79
+ "# refuses to overwrite a modified or unrecognized copy.",
80
+ "#",
81
+ "# " + EPIC_CI_WORKFLOW_VERSION_MARKER_KEY + ": " + String(EPIC_CI_WORKFLOW_RENDERER_VERSION),
82
+ "#",
83
+ "# WHAT THIS WORKFLOW IS",
84
+ "#",
85
+ "# It publishes one required check, `" + EPIC_IMPLEMENTER_CI_CHECK_NAME + "`, that the",
86
+ "# Bridge Epic Implementer reads before it merges a child pull request. The",
87
+ "# check is green only when the repository's stored test command passes.",
88
+ "#",
89
+ "# SCOPE: SAME-REPOSITORY PULL REQUESTS ONLY",
90
+ "#",
91
+ "# The `tests` job runs only when the pull request head is this repository.",
92
+ "# A fork pull request never checks out or runs repository code; the gate",
93
+ "# passes for it immediately and says so. Nothing here uses a secret, and the",
94
+ "# token is read-only.",
95
+ "#",
96
+ "# THE TEST COMMAND",
97
+ "#",
98
+ "# The command below is the repository's stored test command, run with bash",
99
+ "# from the repository root. It must be self-contained: this workflow installs",
100
+ "# no language runtime or dependencies, so the command must install what it",
101
+ "# needs. To change it, save a new command through Bridge setup and re-run the",
102
+ "# installer; do not edit it here.",
103
+ "# ===========================================================================",
104
+ "name: " + EPIC_CI_WORKFLOW_NAME,
105
+ "",
106
+ "on:",
107
+ " pull_request:",
108
+ "",
109
+ "permissions:",
110
+ " contents: read",
111
+ "",
112
+ "jobs:",
113
+ " tests:",
114
+ " name: tests",
115
+ " if: ${{ github.event.pull_request.head.repo.full_name == github.repository }}",
116
+ " runs-on: ubuntu-latest",
117
+ " timeout-minutes: " + String(EPIC_CI_TEST_JOB_TIMEOUT_MINUTES),
118
+ " steps:",
119
+ " - name: Check out the pull request",
120
+ " uses: actions/checkout@v7",
121
+ " with:",
122
+ " persist-credentials: false",
123
+ " - name: Run the stored test command",
124
+ " shell: bash",
125
+ EPIC_CI_COMMAND_LINE_PREFIX + encodeCommandScalar(command),
126
+ "",
127
+ " gate:",
128
+ " name: " + EPIC_IMPLEMENTER_CI_CHECK_NAME,
129
+ " needs: [tests]",
130
+ " if: ${{ always() }}",
131
+ " runs-on: ubuntu-latest",
132
+ " timeout-minutes: " + String(EPIC_CI_GATE_JOB_TIMEOUT_MINUTES),
133
+ " steps:",
134
+ " - name: Aggregate the Epic Implementer CI result",
135
+ " env:",
136
+ " HEAD_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name }}",
137
+ " THIS_REPOSITORY: ${{ github.repository }}",
138
+ " TESTS_RESULT: ${{ needs.tests.result }}",
139
+ " run: |",
140
+ " if [ -z \"$HEAD_REPOSITORY\" ] || [ -z \"$THIS_REPOSITORY\" ]; then",
141
+ " echo \"The pull request head repository could not be identified; the gate fails.\"",
142
+ " exit 1",
143
+ " fi",
144
+ " if [ \"$HEAD_REPOSITORY\" != \"$THIS_REPOSITORY\" ]; then",
145
+ " echo \"Fork pull request: repository code is never run for a fork, so the gate passes without running tests.\"",
146
+ " exit 0",
147
+ " fi",
148
+ " echo \"Tests: $TESTS_RESULT\"",
149
+ " if [ \"$TESTS_RESULT\" = \"success\" ]; then",
150
+ " exit 0",
151
+ " fi",
152
+ " echo \"The stored test command did not conclude 'success'; the gate fails.\"",
153
+ " exit 1",
154
+ "",
155
+ ];
156
+ return lines.join("\n");
157
+ }