@bridge_gpt/mcp-server 0.2.54 → 0.2.55
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 +38 -54
- package/build/agent-guidance.generated.js +3 -0
- package/build/agent-launchers/claude-executor-adapter.js +3 -0
- package/build/agent-notes.js +178 -0
- package/build/agent-registry.js +5 -2
- package/build/agent-utils.js +58 -0
- package/build/agents.generated.js +1 -1
- package/build/codex-skill-adapter.js +55 -0
- package/build/commands.generated.js +5 -4
- package/build/conductor/bridge-api-client.js +199 -6
- package/build/conductor/bring-up-facts.js +187 -0
- package/build/conductor/claude-hook.js +7 -5
- package/build/conductor/cli.js +28 -0
- package/build/conductor/doctor.js +80 -9
- package/build/conductor/epic-implementer-cli.js +1298 -0
- package/build/conductor/epic-runtime.js +1 -1
- package/build/conductor/errors.js +2 -2
- package/build/conductor/git-ci-types.js +1 -1
- package/build/conductor/git-hooks.js +28 -14
- package/build/conductor/install-doctor.js +11 -5
- package/build/conductor/readiness-cli.js +10 -10
- package/build/conductor/readiness-sections.js +58 -9
- package/build/conductor/readiness.js +37 -6
- package/build/conductor/recovery-cli.js +289 -10
- package/build/conductor/recovery-operations.js +125 -2
- package/build/conductor/repair-contract.js +58 -0
- package/build/conductor/store.js +2 -2
- package/build/conductor/supervisor-runtime.js +1 -1
- package/build/conductor-bin.js +2 -139
- package/build/conductor-claude-hook-bin.js +2 -2
- package/build/conductor-claude-hook-removed-stub-bin.js +31 -0
- package/build/conductor-removed-stub-bin.js +30 -0
- package/build/docs.generated.js +1 -1
- package/build/doctor.js +77 -17
- package/build/drive-epic.js +541 -115
- package/build/epic-implementer-bin.js +145 -0
- package/build/epic-implementer-bundle-cli.js +264 -0
- package/build/epic-implementer-claude-hook-bin.js +3 -0
- package/build/epic-integration-pr.js +5 -3
- package/build/executor/env.js +6 -0
- package/build/executor/interrupted-worktree.js +60 -0
- package/build/executor/job-errors.js +45 -0
- package/build/executor/job-runner.js +274 -9
- package/build/executor/job-types.js +25 -9
- package/build/executor/merge-tree-classifier.js +171 -0
- package/build/executor/reconcile-overlap-governance.js +129 -0
- package/build/executor/reconcile-overlap-job.js +989 -0
- package/build/executor/reconcile-overlap-types.js +14 -0
- package/build/executor/spawn-job-driver.js +1 -0
- package/build/executor/types.js +2 -0
- package/build/executor/worker-finalization.js +25 -2
- package/build/executor/worker-guard-hook.js +15 -7
- package/build/implement-epic/bridge-client.js +773 -0
- package/build/implement-epic/checkpoint-store.js +542 -0
- package/build/implement-epic/cli.js +3158 -0
- package/build/implement-epic/cut-protocol.js +392 -0
- package/build/implement-epic/lock.js +302 -0
- package/build/implement-epic/pr-state.js +286 -0
- package/build/implement-epic/spawn.js +113 -0
- package/build/index.js +586 -138
- package/build/init.js +72 -8
- package/build/install-bridge-conductor.js +5 -5
- package/build/install-bridge.js +403 -70
- package/build/mcp-host-config.js +22 -60
- package/build/mcp-host-entry-adapter.js +18 -0
- package/build/mcp-host-targets.js +1 -21
- package/build/merge-pull-request.js +1 -1
- package/build/pipelines.generated.js +7 -7
- package/build/plan-epic-conductor-eligibility.js +1 -1
- package/build/plane/cli.js +36 -5
- package/build/plane/preflight.js +128 -12
- package/build/plane/shutdown.js +4 -4
- package/build/readiness-check.js +3 -3
- package/build/readme.generated.js +1 -1
- package/build/run-unit-tests-launcher.js +1 -1
- package/build/setup-epic.js +69 -31
- package/build/start-tickets-conductor.js +8 -7
- package/build/version.generated.js +3 -3
- package/build/worker-guard-hook-bin.js +1 -1
- package/docs/CONDUCTOR.md +8 -6
- package/package.json +5 -3
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Epic Supervisor stateless reconciliation tick (BAPI-408).
|
|
3
3
|
*
|
|
4
|
-
* `
|
|
4
|
+
* `epic-implementer epic-tick --epic-key <key>` runs one stateless pass:
|
|
5
5
|
* 1. Claim the epic supervision lease (single-writer guard)
|
|
6
6
|
* 2. Drift check (log if fired materially late)
|
|
7
7
|
* 3. Rebuild observed state from Postgres + local ledger
|
|
@@ -62,7 +62,7 @@ export class ConductorStoreError extends Error {
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
/**
|
|
65
|
-
* Raised when the frozen v1 `
|
|
65
|
+
* Raised when the frozen v1 `epic-implementer epic-tick` entrypoint is invoked
|
|
66
66
|
* (BAPI-534, Epic Conductor v2 slice 0). The v1 local reconciler/ledger tick is
|
|
67
67
|
* frozen: Epic Conductor v2 replaces it with a server-side reconciler plus the
|
|
68
68
|
* local executor (`mcp-server executor`). The message points operators at the v2
|
|
@@ -70,7 +70,7 @@ export class ConductorStoreError extends Error {
|
|
|
70
70
|
*/
|
|
71
71
|
export class ConductorEpicTickV1FrozenError extends Error {
|
|
72
72
|
constructor() {
|
|
73
|
-
super("The v1 `
|
|
73
|
+
super("The v1 `epic-implementer epic-tick` path is frozen (EPIC_TICK_V1_FROZEN). Epic " +
|
|
74
74
|
"Conductor v2 replaces the local epic tick with a server-side reconciler " +
|
|
75
75
|
"plus the local executor — run `mcp-server executor` instead.");
|
|
76
76
|
this.name = "ConductorEpicTickV1FrozenError";
|
|
@@ -155,7 +155,7 @@ export function stableJsonHash(value) {
|
|
|
155
155
|
* The two literals are exported as named constants so no consumer spells them
|
|
156
156
|
* inline — a typo in one consumer would otherwise silently disagree with the
|
|
157
157
|
* parser about which disposition is configured. The Python mirror lives in
|
|
158
|
-
* `api/library/epic_conductor/
|
|
158
|
+
* `api/library/epic_conductor/epic_implementer_readiness.py` and must carry the same
|
|
159
159
|
* closed set.
|
|
160
160
|
*/
|
|
161
161
|
export const VERDICTLESS_DISPOSITION_PARK = "park";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Managed, non-blocking local git hook installation and verification (BAPI-395).
|
|
3
3
|
*
|
|
4
|
-
* `
|
|
4
|
+
* `epic-implementer install-git-hooks` installs opportunistic `post-commit` and
|
|
5
5
|
* `reference-transaction` hooks that launch the conductor producer in the
|
|
6
6
|
* background and never block the user's git workflow. Hooks are LOCAL and
|
|
7
7
|
* unversioned: only a clearly-delimited managed block is inserted/replaced, all
|
|
@@ -36,8 +36,13 @@ export function resolveGitHooksDirectory(deps = {}) {
|
|
|
36
36
|
const absoluteCommonDir = isAbsolute(commonDir) ? commonDir : resolve(cwd, commonDir);
|
|
37
37
|
return { is_worktree: true, hooks_dir: resolve(absoluteCommonDir, "hooks") };
|
|
38
38
|
}
|
|
39
|
-
/**
|
|
40
|
-
|
|
39
|
+
/**
|
|
40
|
+
* The file name every managed hook body must invoke. S4 (BAPI-1080): this is
|
|
41
|
+
* the renamed REAL event-ledger bin output; the retained `conductor-bin.js`
|
|
42
|
+
* output name is now a fixed, failing migration stub and must never be
|
|
43
|
+
* resolved here.
|
|
44
|
+
*/
|
|
45
|
+
export const CONDUCTOR_BIN_FILENAME = "epic-implementer-bin.js";
|
|
41
46
|
/**
|
|
42
47
|
* The sanitized failure reason. Names the layout categories that were searched
|
|
43
48
|
* so the operator can act, and nothing else — no probed path, no caught error
|
|
@@ -48,18 +53,19 @@ export const CONDUCTOR_HOOK_BIN_UNRESOLVED_REASON = `no usable ${CONDUCTOR_BIN_F
|
|
|
48
53
|
`the invocation target when it is named ${CONDUCTOR_BIN_FILENAME}). ` +
|
|
49
54
|
"Reinstall or rebuild the package so a conductor binary ships beside the executing module.";
|
|
50
55
|
/**
|
|
51
|
-
* Absolute, canonical path of the `
|
|
52
|
-
* that is currently executing (BAPI-772, the BAPI-768 pattern applied
|
|
56
|
+
* Absolute, canonical path of the `epic-implementer-bin.js` belonging to the
|
|
57
|
+
* artifact that is currently executing (BAPI-772, the BAPI-768 pattern applied
|
|
58
|
+
* to hooks; renamed from `conductor-bin.js` by S4/BAPI-1080).
|
|
53
59
|
*
|
|
54
60
|
* ## Why this cannot be path arithmetic
|
|
55
61
|
*
|
|
56
62
|
* The old implementation hard-coded ONE layout — `dirname(import.meta.url)/../
|
|
57
63
|
* conductor-bin.js`, the tsc emit — and returned it unverified. In the shipped
|
|
58
|
-
* esbuild bundle this module IS `build/
|
|
59
|
-
* landed on `<pkg>/
|
|
64
|
+
* esbuild bundle this module IS `build/epic-implementer-bin.js`, so the directory hop
|
|
65
|
+
* landed on `<pkg>/epic-implementer-bin.js`, a path that has never existed. Every hook
|
|
60
66
|
* installed from the published package embedded that dead path, and because hook
|
|
61
67
|
* bodies discard output and end in `|| true`, each commit ENOENTed in silence
|
|
62
|
-
* while `
|
|
68
|
+
* while `epic-implementer doctor` still called the hooks healthy.
|
|
63
69
|
*
|
|
64
70
|
* So this resolver PROBES. Candidates are drawn only from the executing module's
|
|
65
71
|
* own URL and Node's actual invocation target — never `process.cwd()` or an
|
|
@@ -82,13 +88,13 @@ export function resolveConductorHookBin(overrides = {}) {
|
|
|
82
88
|
// Bundled layout: the bin sits beside the executing module (and in the
|
|
83
89
|
// single-file bundle it IS the executing module).
|
|
84
90
|
candidates.push(join(dir, CONDUCTOR_BIN_FILENAME));
|
|
85
|
-
// tsc layout: `build/conductor/git-hooks.js` → `build/
|
|
91
|
+
// tsc layout: `build/conductor/git-hooks.js` → `build/epic-implementer-bin.js`.
|
|
86
92
|
candidates.push(join(dir, "..", CONDUCTOR_BIN_FILENAME));
|
|
87
93
|
}
|
|
88
94
|
// Last resort, and only ever a validated one: a packaged install launches
|
|
89
95
|
// through a `node_modules/.bin` symlink. Accepted only when the invocation
|
|
90
|
-
// target is exactly the
|
|
91
|
-
// must never become a hook target.
|
|
96
|
+
// target is exactly the epic-implementer bin — `index.js` or
|
|
97
|
+
// `epic-implementer-bin.js.bak` must never become a hook target.
|
|
92
98
|
if (typeof deps.argv1 === "string" && deps.argv1.length > 0) {
|
|
93
99
|
if (basename(deps.argv1) === CONDUCTOR_BIN_FILENAME)
|
|
94
100
|
candidates.push(deps.argv1);
|
|
@@ -379,14 +385,22 @@ export function inspectConductorGitHooks(deps = {}) {
|
|
|
379
385
|
// embedded target READ-ONLY so a dead hook is visible instead of
|
|
380
386
|
// reported healthy. Nothing here rewrites or canonicalizes the hook.
|
|
381
387
|
embeddedBinPath = extractManagedHookBinPath(content);
|
|
382
|
-
|
|
388
|
+
// S4 (BAPI-1080): a hook installed before the rename still embeds
|
|
389
|
+
// the old `conductor-bin.js` path, which still exists post-upgrade
|
|
390
|
+
// as the migration stub — stat-true but dead. Require the embedded
|
|
391
|
+
// path's basename to be the current real bin so a stale hook is
|
|
392
|
+
// correctly flagged degraded instead of reported healthy.
|
|
393
|
+
embeddedBinIsFile =
|
|
394
|
+
embeddedBinPath !== null &&
|
|
395
|
+
basename(embeddedBinPath) === CONDUCTOR_BIN_FILENAME &&
|
|
396
|
+
defaultIsFile(embeddedBinPath);
|
|
383
397
|
if (embeddedBinPath === null) {
|
|
384
398
|
warnings.push(`${name} hook has a managed block with no recognizable conductor command; ` +
|
|
385
|
-
"reinstall with `
|
|
399
|
+
"reinstall with `epic-implementer install-git-hooks`");
|
|
386
400
|
}
|
|
387
401
|
else if (!embeddedBinIsFile) {
|
|
388
402
|
warnings.push(`${name} hook points at a missing or invalid conductor binary (${embeddedBinPath}); ` +
|
|
389
|
-
"the hook is dead — reinstall with `
|
|
403
|
+
"the hook is dead — reinstall with `epic-implementer install-git-hooks`");
|
|
390
404
|
}
|
|
391
405
|
}
|
|
392
406
|
}
|
|
@@ -36,7 +36,7 @@ import { buildConductorDoctorReport, formatConductorDoctorReport, } from "./doct
|
|
|
36
36
|
import { inspectBridgeApiProfileToken, } from "../mcp-host-config.js";
|
|
37
37
|
import { collectExecutorServiceDiagnostics, formatExecutorServiceDiagnosticsReport, } from "../doctor.js";
|
|
38
38
|
import { collectInstallStatusChecks, formatInstallStatusReport, } from "../install-doctor.js";
|
|
39
|
-
import { conductorHookTargetsSection, conductorLedgerLoadabilitySection, conductorProfileTokenSection, CONDUCTOR_PROFILE_TOKEN, detectConductorContext, executorObservationSection, executorProvisioningSection, executorServiceStateSection, githubActionsSection, githubCredentialsSection, inspectWorkflowPresence, reconcilerSection, supervisorSection, workflowSection, } from "./readiness-sections.js";
|
|
39
|
+
import { bringUpSafetySections, conductorHookTargetsSection, conductorLedgerLoadabilitySection, conductorProfileTokenSection, CONDUCTOR_PROFILE_TOKEN, detectConductorContext, executorObservationSection, executorProvisioningSection, executorServiceStateSection, githubActionsSection, githubCredentialsSection, inspectWorkflowPresence, reconcilerSection, supervisorSection, workflowSection, } from "./readiness-sections.js";
|
|
40
40
|
import { ConductorBridgeApiError, fetchConductorReadiness, } from "./bridge-api-client.js";
|
|
41
41
|
/**
|
|
42
42
|
* Run one optional collector, containing any throw.
|
|
@@ -89,8 +89,8 @@ export async function runConductorInstallDoctor(deps) {
|
|
|
89
89
|
id: "conductor-ledger",
|
|
90
90
|
label: "Conductor ledger doctor",
|
|
91
91
|
status: "degraded",
|
|
92
|
-
detail: "the local
|
|
93
|
-
remediation: "run `
|
|
92
|
+
detail: "the local epic-implementer doctor could not be built",
|
|
93
|
+
remediation: "run `epic-implementer doctor` directly to see why the local ledger report failed.",
|
|
94
94
|
});
|
|
95
95
|
}
|
|
96
96
|
const legacyExecutorPreflight = executorPreflightSettled.value ?? null;
|
|
@@ -161,6 +161,10 @@ export async function runConductorInstallDoctor(deps) {
|
|
|
161
161
|
const workflow = await inspectWorkflowPresence(deps.readWorkflowFile, deps.classifyWorkflowDrift);
|
|
162
162
|
sections.push(workflowSection(workflow.presence, deps.reviewPolicySource, workflow.driftDetail));
|
|
163
163
|
if (readiness) {
|
|
164
|
+
// BAPI-1153 — the "Bring-up safety" group, ahead of the repository-scoped
|
|
165
|
+
// reconciler/executor story: these three decide whether a plane can drive a
|
|
166
|
+
// run at all, and `setup-epic` refuses on them before a run exists.
|
|
167
|
+
sections.push(...bringUpSafetySections(readiness));
|
|
164
168
|
sections.push(reconcilerSection(readiness));
|
|
165
169
|
}
|
|
166
170
|
// --- Executor provisioning (BAPI-688): real, read-only unit detection -----
|
|
@@ -326,9 +330,11 @@ export function conductorInstallDoctorHasFatal(report) {
|
|
|
326
330
|
//
|
|
327
331
|
// The section contract and every pure section builder now live in
|
|
328
332
|
// `readiness-sections.ts`, so the install-time doctor and the advisory
|
|
329
|
-
// `
|
|
333
|
+
// `epic-implementer readiness` gate share ONE adapter set. They are re-exported from
|
|
330
334
|
// here unchanged: every existing import of `ConductorInstallSection`, the
|
|
331
335
|
// remediation constants, or the installer reporting APIs from
|
|
332
336
|
// `conductor/install-doctor.js` keeps resolving exactly as before.
|
|
333
337
|
// ---------------------------------------------------------------------------
|
|
334
|
-
export { CONDUCTOR_OPERATOR_RUNBOOK_POINTER, EXECUTOR_PROVISIONING_GUIDANCE, CONDUCTOR_PROFILE_TOKEN, CONDUCTOR_PROFILE_TOKEN_NO_ENTRY_REMEDIATION, CONDUCTOR_PROFILE_TOKEN_REMEDIATION, CONDUCTOR_HOOK_TARGET_REMEDIATION, CONDUCTOR_LEDGER_LOADABILITY_REMEDIATION, EXECUTOR_INSTALL_SERVICE_REMEDIATION, EXECUTOR_SERVICE_START_REMEDIATION, } from "./readiness-sections.js";
|
|
338
|
+
export { BRING_UP_SECTION_LABELS, bringUpSafetySections, databaseMigrationCurrentSection, singleTransitionAuthoritySection, workerPresentSection, CONDUCTOR_OPERATOR_RUNBOOK_POINTER, EXECUTOR_PROVISIONING_GUIDANCE, CONDUCTOR_PROFILE_TOKEN, CONDUCTOR_PROFILE_TOKEN_NO_ENTRY_REMEDIATION, CONDUCTOR_PROFILE_TOKEN_REMEDIATION, CONDUCTOR_HOOK_TARGET_REMEDIATION, CONDUCTOR_LEDGER_LOADABILITY_REMEDIATION, EXECUTOR_INSTALL_SERVICE_REMEDIATION, EXECUTOR_SERVICE_START_REMEDIATION, } from "./readiness-sections.js";
|
|
339
|
+
// BAPI-1153 — the bring-up remediations the three sections above render.
|
|
340
|
+
export { BRING_UP_RUNBOOK, BRING_UP_UNREPORTED_REMEDIATION, DATABASE_MIGRATION_BEHIND_REMEDIATION, DATABASE_MIGRATION_UNREADABLE_REMEDIATION, SECOND_RECONCILER_REMEDIATION, WORKER_HEARTBEAT_UNREADABLE_REMEDIATION, WORKER_NOT_PRESENT_REMEDIATION, } from "./bring-up-facts.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The `
|
|
2
|
+
* The `epic-implementer readiness` command surface (BAPI-1055, AC-6).
|
|
3
3
|
*
|
|
4
4
|
* A CLI subcommand, deliberately, and NOT an MCP tool. Every peer diagnostic in
|
|
5
5
|
* this package — `doctor`, `plane`, `drive-epic`, `executor`, `install` — is an
|
|
@@ -40,13 +40,13 @@ import { claudeReviewWorkflowPath } from "../claude-review-workflow.js";
|
|
|
40
40
|
import { runDefaultPlanePreflight } from "../plane/cli.js";
|
|
41
41
|
/** Fixed notice emitted to stderr when a source could not be collected. */
|
|
42
42
|
export const READINESS_COLLECTION_NOTICE = "one or more readiness sources could not be collected; the affected prerequisites are reported as failures.";
|
|
43
|
-
/** Usage text for `
|
|
43
|
+
/** Usage text for `epic-implementer readiness`. */
|
|
44
44
|
export function getReadinessUsage() {
|
|
45
45
|
return [
|
|
46
|
-
"Usage:
|
|
46
|
+
"Usage: epic-implementer readiness [--json] [--no-deny-probe]",
|
|
47
47
|
"",
|
|
48
48
|
"Read-only ADVISORY report of every conductor prerequisite, consolidated from the",
|
|
49
|
-
"Bridge install checklist, the
|
|
49
|
+
"Bridge install checklist, the epic-implementer doctor, the plane preflight, and the",
|
|
50
50
|
"server conductor-readiness collector. Each prerequisite reports pass/warn/fail/skip",
|
|
51
51
|
"and, on a failure, exactly ONE named remediation.",
|
|
52
52
|
"",
|
|
@@ -56,12 +56,12 @@ export function getReadinessUsage() {
|
|
|
56
56
|
"",
|
|
57
57
|
"Options:",
|
|
58
58
|
" --json Print the versioned structured report as JSON",
|
|
59
|
-
" --no-deny-probe Accepted for parity with `
|
|
59
|
+
" --no-deny-probe Accepted for parity with `epic-implementer doctor`; inert here, because",
|
|
60
60
|
" this command never spawns the deny-enforcement probe",
|
|
61
61
|
" --help Print this usage message",
|
|
62
62
|
].join("\n");
|
|
63
63
|
}
|
|
64
|
-
/** Parse `
|
|
64
|
+
/** Parse `epic-implementer readiness` argv. Unknown flags and positionals are errors. */
|
|
65
65
|
export function parseReadinessArgs(argv) {
|
|
66
66
|
const parsed = { json: false, help: false, noDenyProbe: false };
|
|
67
67
|
for (const token of argv) {
|
|
@@ -72,7 +72,7 @@ export function parseReadinessArgs(argv) {
|
|
|
72
72
|
else if (token === "--help" || token === "-h")
|
|
73
73
|
parsed.help = true;
|
|
74
74
|
else
|
|
75
|
-
return { error: `Unknown argument "${token}". Run "
|
|
75
|
+
return { error: `Unknown argument "${token}". Run "epic-implementer readiness --help" for usage.` };
|
|
76
76
|
}
|
|
77
77
|
return parsed;
|
|
78
78
|
}
|
|
@@ -80,7 +80,7 @@ export function parseReadinessArgs(argv) {
|
|
|
80
80
|
* Build the production report for `repoRoot`.
|
|
81
81
|
*
|
|
82
82
|
* Every seam is a real, READ-ONLY one: the install doctor's own GET, the
|
|
83
|
-
*
|
|
83
|
+
* epic-implementer doctor's local inspections, and the same plane preflight
|
|
84
84
|
* `plane up` and `drive-epic` run. Nothing is re-implemented here.
|
|
85
85
|
*/
|
|
86
86
|
async function collectProductionReport(repoRoot, env) {
|
|
@@ -111,13 +111,13 @@ async function collectProductionReport(repoRoot, env) {
|
|
|
111
111
|
},
|
|
112
112
|
},
|
|
113
113
|
// The deny-enforcement probe is never spawned from this command; the check
|
|
114
|
-
// is reported as an explicit skip naming `
|
|
114
|
+
// is reported as an explicit skip naming `epic-implementer doctor` instead.
|
|
115
115
|
conductorDoctorDeps: { env, skipDenyProbe: true },
|
|
116
116
|
runPlanePreflight: () => runDefaultPlanePreflight(repoRoot, env),
|
|
117
117
|
});
|
|
118
118
|
}
|
|
119
119
|
/**
|
|
120
|
-
* Run `
|
|
120
|
+
* Run `epic-implementer readiness`.
|
|
121
121
|
*
|
|
122
122
|
* Returns 0 for every readiness outcome; see the module docstring. The only
|
|
123
123
|
* non-zero return is a usage error.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The shared
|
|
2
|
+
* The shared epic-implementer readiness SECTION layer (BAPI-1055).
|
|
3
3
|
*
|
|
4
4
|
* Extracted verbatim from `conductor/install-doctor.ts`, where these builders
|
|
5
5
|
* had already composed four of the five prerequisite loci. Nothing about a
|
|
6
6
|
* section changed in the move: same ids, same labels, same statuses, same
|
|
7
7
|
* detail text, same installer remediations. What changed is who may consume
|
|
8
|
-
* them — the install-time doctor and the advisory `
|
|
8
|
+
* them — the install-time doctor and the advisory `epic-implementer readiness` gate now
|
|
9
9
|
* read ONE adapter set instead of interpreting the same probes twice.
|
|
10
10
|
*
|
|
11
11
|
* ## Why extraction rather than a second aggregator
|
|
@@ -37,6 +37,7 @@ import { MANAGED_HOOK_NAMES } from "./git-hooks.js";
|
|
|
37
37
|
import { MCP_PACKAGE_NAME } from "../mcp-identity.js";
|
|
38
38
|
import { CLAUDE_REVIEW_WORKFLOW_DRIFT_REMEDIATION, summarizeClaudeReviewWorkflowDrift, } from "../claude-review-workflow-drift.js";
|
|
39
39
|
import { describeNativeLedgerAvailability } from "./doctor.js";
|
|
40
|
+
import { classifyDatabaseMigration, classifySingleTransitionAuthority, classifyWorkerPresent, } from "./bring-up-facts.js";
|
|
40
41
|
/** Operator runbook pointer surfaced beside the executor gap. */
|
|
41
42
|
export const CONDUCTOR_OPERATOR_RUNBOOK_POINTER = "docs/claude/epic-conductor-v2-operator-runbook.md";
|
|
42
43
|
/**
|
|
@@ -65,7 +66,7 @@ export const CONDUCTOR_PROFILE_TOKEN_REMEDIATION = "re-run `install conductor` a
|
|
|
65
66
|
"tools become visible after the MCP client is restarted.";
|
|
66
67
|
/** Remediation surfaced whenever the managed conductor hooks are not usable. */
|
|
67
68
|
export const CONDUCTOR_HOOK_TARGET_REMEDIATION = "re-run `install conductor` and accept the local-observability consent, or run " +
|
|
68
|
-
"`
|
|
69
|
+
"`epic-implementer install-git-hooks` directly.";
|
|
69
70
|
/** Remediation surfaced when the ledger's native binding does not load. */
|
|
70
71
|
export const CONDUCTOR_LEDGER_LOADABILITY_REMEDIATION = "`better-sqlite3` is an optionalDependency npm silently skips on a build failure — " +
|
|
71
72
|
"reinstall it for this Node runtime to restore local conductor observability.";
|
|
@@ -244,9 +245,57 @@ export function reconcilerSection(readiness) {
|
|
|
244
245
|
detail: r.last_tick_at === null
|
|
245
246
|
? "no reconciler tick recorded for this repository"
|
|
246
247
|
: `last tick ${r.last_tick_age_seconds}s ago (threshold ${readiness.thresholds.reconciler_stale_after_seconds}s)`,
|
|
247
|
-
remediation: "start the reconciler (`
|
|
248
|
+
remediation: "start the reconciler (`epic-implementer epic-tick` schedule) for this repository.",
|
|
248
249
|
};
|
|
249
250
|
}
|
|
251
|
+
// ---------------------------------------------------------------------------
|
|
252
|
+
// Bring-up safety (BAPI-1153)
|
|
253
|
+
// ---------------------------------------------------------------------------
|
|
254
|
+
// Three deployment-wide facts, rendered as their own group. Each builder is a
|
|
255
|
+
// thin projection of the ONE classification in `bring-up-facts.ts`, which the
|
|
256
|
+
// advisory gate and the `setup-epic` refusal read too — so the doctor cannot
|
|
257
|
+
// call a database current that setup-epic refuses as behind.
|
|
258
|
+
//
|
|
259
|
+
// Never `fatal`: the installer's fatal state refuses to WRITE, and none of these
|
|
260
|
+
// is a reason not to install. They refuse at `setup-epic`, before a run exists.
|
|
261
|
+
/** Section labels, in render order. Ids come from the shared classification. */
|
|
262
|
+
export const BRING_UP_SECTION_LABELS = {
|
|
263
|
+
"database-migration-current": "Database migration",
|
|
264
|
+
"worker-present": "Worker present",
|
|
265
|
+
"single-transition-authority": "Single transition authority",
|
|
266
|
+
};
|
|
267
|
+
function bringUpSection(verdict) {
|
|
268
|
+
const base = { id: verdict.id, label: BRING_UP_SECTION_LABELS[verdict.id] };
|
|
269
|
+
if (verdict.state === "ok") {
|
|
270
|
+
return { ...base, status: "ok", detail: verdict.detail };
|
|
271
|
+
}
|
|
272
|
+
return {
|
|
273
|
+
...base,
|
|
274
|
+
status: "degraded",
|
|
275
|
+
detail: verdict.detail,
|
|
276
|
+
...(verdict.remediation ? { remediation: verdict.remediation } : {}),
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
/** Whether the shared database is at (or ahead of) the server's code heads. */
|
|
280
|
+
export function databaseMigrationCurrentSection(readiness) {
|
|
281
|
+
return bringUpSection(classifyDatabaseMigration(readiness.bring_up ?? null));
|
|
282
|
+
}
|
|
283
|
+
/** Whether any reconciler (worker) heartbeat is fresh across the deployment. */
|
|
284
|
+
export function workerPresentSection(readiness) {
|
|
285
|
+
return bringUpSection(classifyWorkerPresent(readiness.bring_up ?? null));
|
|
286
|
+
}
|
|
287
|
+
/** Whether exactly one reconciler holds transition authority. */
|
|
288
|
+
export function singleTransitionAuthoritySection(readiness) {
|
|
289
|
+
return bringUpSection(classifySingleTransitionAuthority(readiness.bring_up ?? null));
|
|
290
|
+
}
|
|
291
|
+
/** The "Bring-up safety" group, in render order. */
|
|
292
|
+
export function bringUpSafetySections(readiness) {
|
|
293
|
+
return [
|
|
294
|
+
databaseMigrationCurrentSection(readiness),
|
|
295
|
+
workerPresentSection(readiness),
|
|
296
|
+
singleTransitionAuthoritySection(readiness),
|
|
297
|
+
];
|
|
298
|
+
}
|
|
250
299
|
export function workflowSection(presence, reviewPolicySource, driftDetail) {
|
|
251
300
|
if (presence === "present") {
|
|
252
301
|
return {
|
|
@@ -527,7 +576,7 @@ export function conductorHookTargetsSection(legacyConductor, conductorContext) {
|
|
|
527
576
|
return {
|
|
528
577
|
...base,
|
|
529
578
|
status: "degraded",
|
|
530
|
-
detail: "hook targets could not be derived (the
|
|
579
|
+
detail: "hook targets could not be derived (the epic-implementer doctor was not collected)",
|
|
531
580
|
remediation: CONDUCTOR_HOOK_TARGET_REMEDIATION,
|
|
532
581
|
};
|
|
533
582
|
}
|
|
@@ -538,7 +587,7 @@ export function conductorHookTargetsSection(legacyConductor, conductorContext) {
|
|
|
538
587
|
return {
|
|
539
588
|
...base,
|
|
540
589
|
status: "degraded",
|
|
541
|
-
detail: "hook targets could not be derived (the
|
|
590
|
+
detail: "hook targets could not be derived (the epic-implementer doctor reported no hook state)",
|
|
542
591
|
remediation: CONDUCTOR_HOOK_TARGET_REMEDIATION,
|
|
543
592
|
};
|
|
544
593
|
}
|
|
@@ -576,7 +625,7 @@ export function conductorHookTargetsSection(legacyConductor, conductorContext) {
|
|
|
576
625
|
*
|
|
577
626
|
* No parallel `better-sqlite3` check: the mapping from an inspection to
|
|
578
627
|
* available/degraded lives in one place ({@link describeNativeLedgerAvailability}),
|
|
579
|
-
* so this section and the
|
|
628
|
+
* so this section and the epic-implementer doctor can never disagree about whether the
|
|
580
629
|
* binding loads. A degraded result carries the module name, the ABI, and the
|
|
581
630
|
* sanitized failure kind, because the common cause is npm silently skipping an
|
|
582
631
|
* optionalDependency build and that is undiagnosable without the ABI.
|
|
@@ -590,7 +639,7 @@ export function conductorLedgerLoadabilitySection(legacyConductor) {
|
|
|
590
639
|
return {
|
|
591
640
|
...base,
|
|
592
641
|
status: "degraded",
|
|
593
|
-
detail: "ledger loadability could not be derived (the
|
|
642
|
+
detail: "ledger loadability could not be derived (the epic-implementer doctor was not collected)",
|
|
594
643
|
remediation: CONDUCTOR_LEDGER_LOADABILITY_REMEDIATION,
|
|
595
644
|
};
|
|
596
645
|
}
|
|
@@ -599,7 +648,7 @@ export function conductorLedgerLoadabilitySection(legacyConductor) {
|
|
|
599
648
|
return {
|
|
600
649
|
...base,
|
|
601
650
|
status: "degraded",
|
|
602
|
-
detail: "ledger loadability could not be derived (the
|
|
651
|
+
detail: "ledger loadability could not be derived (the epic-implementer doctor reported no native-ledger state)",
|
|
603
652
|
remediation: CONDUCTOR_LEDGER_LOADABILITY_REMEDIATION,
|
|
604
653
|
};
|
|
605
654
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The consolidated ADVISORY
|
|
2
|
+
* The consolidated ADVISORY epic-implementer readiness gate (BAPI-1055, AC-6/AC-7).
|
|
3
3
|
*
|
|
4
4
|
* One report that answers "is this host and repository ready to conduct an
|
|
5
5
|
* epic", assembled from the five loci that each answered part of it before:
|
|
6
|
-
* `bridge doctor`'s install checklist, `
|
|
6
|
+
* `bridge doctor`'s install checklist, `epic-implementer doctor`'s local inspections,
|
|
7
7
|
* `plane` preflight, the server `conductor-readiness` collector, and the
|
|
8
8
|
* install-time doctor that already composed four of them. Every prerequisite
|
|
9
9
|
* comes back as a canonical {@link ReadinessCheck}: a stable id, a
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
* ## Schema versioning
|
|
78
78
|
*
|
|
79
79
|
* {@link CONDUCTOR_READINESS_GATE_SCHEMA_VERSION} is INDEPENDENT of the install
|
|
80
|
-
* doctor's section vocabulary, the
|
|
80
|
+
* doctor's section vocabulary, the epic-implementer doctor's report, the plane
|
|
81
81
|
* diagnostic shape, and the Python wire response. Each of those evolves for its
|
|
82
82
|
* own reasons; binding this report's version to any of them would make an
|
|
83
83
|
* unrelated additive change look like a breaking one here, or worse, hide a real
|
|
@@ -87,6 +87,8 @@ import { mapConductorDoctorReportToReadinessChecks, } from "./doctor.js";
|
|
|
87
87
|
import { mapInstallStatusChecksToReadinessChecks, } from "../install-doctor.js";
|
|
88
88
|
import { mapPlanePreflightToReadinessChecks, } from "../plane/preflight.js";
|
|
89
89
|
import { runConductorInstallDoctor, } from "./install-doctor.js";
|
|
90
|
+
import { BRING_UP_SECTION_LABELS } from "./readiness-sections.js";
|
|
91
|
+
import { classifyBringUpFacts } from "./bring-up-facts.js";
|
|
90
92
|
import { createReadinessCheck, createReadinessCheckSafely, summarizeReadinessChecks, validateReadinessChecks, } from "../readiness-check.js";
|
|
91
93
|
/** This report's own schema version. See the module docstring. */
|
|
92
94
|
export const CONDUCTOR_READINESS_GATE_SCHEMA_VERSION = 1;
|
|
@@ -108,6 +110,15 @@ export const SERVER_READINESS_DESCRIPTORS = [
|
|
|
108
110
|
{ id: "review-policy", label: "Review policy" },
|
|
109
111
|
{ id: "github-credentials", label: "GitHub App credentials" },
|
|
110
112
|
{ id: "github-actions-permission", label: "GitHub App actions permission" },
|
|
113
|
+
// BAPI-1153 — the "Bring-up safety" group: deployment-wide facts that decide
|
|
114
|
+
// whether a plane can drive a run at all, ahead of the repository-scoped
|
|
115
|
+
// reconciler/executor rows. Labels come from the shared section layer.
|
|
116
|
+
{ id: "database-migration-current", label: BRING_UP_SECTION_LABELS["database-migration-current"] },
|
|
117
|
+
{ id: "worker-present", label: BRING_UP_SECTION_LABELS["worker-present"] },
|
|
118
|
+
{
|
|
119
|
+
id: "single-transition-authority",
|
|
120
|
+
label: BRING_UP_SECTION_LABELS["single-transition-authority"],
|
|
121
|
+
},
|
|
111
122
|
{ id: "reconciler-liveness", label: "Reconciler tick liveness" },
|
|
112
123
|
{ id: "executor-readiness", label: "Executor server observation" },
|
|
113
124
|
{ id: "review-workflow", label: "Review workflow protocol" },
|
|
@@ -150,7 +161,7 @@ const SERVER_REMEDIATIONS = {
|
|
|
150
161
|
actionsProbeFailed: "re-run once credentials resolve; the permission could not be checked, which is not the same as confirmed-missing.",
|
|
151
162
|
actionsMissing: "grant the GitHub App `actions: write`; without it the conductor's workflow rerun lane fails open on 403.",
|
|
152
163
|
reconcilerUnreadable: "retry once the Bridge API is reachable; reconciler liveness is unknown here, not confirmed stale.",
|
|
153
|
-
reconcilerStale: "start the reconciler for this repository (the `
|
|
164
|
+
reconcilerStale: "start the reconciler for this repository (the `epic-implementer epic-tick` schedule) so ticks resume.",
|
|
154
165
|
executorUnreadable: "retry once the Bridge API is reachable; no executor readiness claim is ever made from local state.",
|
|
155
166
|
executorNeverSeen: "provision and start an executor (`install conductor --executor-id <id>` or `executor install-service`); readiness comes only from a live server observation.",
|
|
156
167
|
executorStale: "restart the executor service; it was observed once and has gone quiet past the server's staleness threshold.",
|
|
@@ -161,7 +172,7 @@ const SERVER_REMEDIATIONS = {
|
|
|
161
172
|
olderServer: "upgrade the Bridge API deploy so it reports this fact; its state is unknown here, not healthy.",
|
|
162
173
|
// BAPI-1102. Each names the ONE action that clears it, and none of them
|
|
163
174
|
// mentions a webhook URL, a destination, or any configuration value.
|
|
164
|
-
unattendedConsent: "set `
|
|
175
|
+
unattendedConsent: "set `unattended_epic_implementer_allowed` for this repository (docs/claude/account-settings-operator-runbook.md), or run setup with --attended.",
|
|
165
176
|
unattendedNotifyDefault: "supply a --policy-file declaring notify.webhook_url, or run setup with --attended; an unattended run must have a working escalation channel, and notify.local_sink is not one.",
|
|
166
177
|
repositoryWorkflowReadiness: "run `install-bridge conductor` so the review and conductor-CI workflows are confirmed on the default branch, or run setup with --attended.",
|
|
167
178
|
};
|
|
@@ -268,6 +279,7 @@ export function mapServerReadinessToChecks(readiness, failure = "unreachable") {
|
|
|
268
279
|
remediation: SERVER_REMEDIATIONS.githubCredentials,
|
|
269
280
|
});
|
|
270
281
|
outcomes.push(githubActionsOutcome(readiness));
|
|
282
|
+
outcomes.push(...bringUpOutcomes(readiness));
|
|
271
283
|
outcomes.push(reconcilerOutcome(readiness));
|
|
272
284
|
outcomes.push(executorOutcome(readiness));
|
|
273
285
|
outcomes.push(reviewWorkflowOutcome(readiness));
|
|
@@ -298,6 +310,25 @@ export function mapServerReadinessToChecks(readiness, failure = "unreachable") {
|
|
|
298
310
|
});
|
|
299
311
|
});
|
|
300
312
|
}
|
|
313
|
+
/**
|
|
314
|
+
* The three BAPI-1153 bring-up safety rows.
|
|
315
|
+
*
|
|
316
|
+
* Read from the SAME classification the install doctor's sections and the
|
|
317
|
+
* `setup-epic` refusal read (`bring-up-facts.ts`), so the advisory gate cannot
|
|
318
|
+
* call a fact healthy that setup-epic refuses. Every non-`ok` verdict is `fail`:
|
|
319
|
+
* unreported and unreadable are never healthy, and the remediation already
|
|
320
|
+
* separates "retry — unknown" from "confirmed bad — do this".
|
|
321
|
+
*/
|
|
322
|
+
function bringUpOutcomes(readiness) {
|
|
323
|
+
return classifyBringUpFacts(readiness.bring_up ?? null).map((verdict) => verdict.state === "ok"
|
|
324
|
+
? { id: verdict.id, status: "pass", detail: verdict.detail }
|
|
325
|
+
: {
|
|
326
|
+
id: verdict.id,
|
|
327
|
+
status: "fail",
|
|
328
|
+
detail: verdict.detail,
|
|
329
|
+
remediation: verdict.remediation ?? SERVER_REMEDIATIONS.olderServer,
|
|
330
|
+
});
|
|
331
|
+
}
|
|
301
332
|
/**
|
|
302
333
|
* The three BAPI-1102 unattended prerequisite rows.
|
|
303
334
|
*
|
|
@@ -338,7 +369,7 @@ function unattendedOutcomes(readiness) {
|
|
|
338
369
|
: {
|
|
339
370
|
id: "unattended-consent",
|
|
340
371
|
status: "warn",
|
|
341
|
-
detail: "
|
|
372
|
+
detail: "unattended_epic_implementer_allowed is NOT set — an unattended run will be refused before it is created",
|
|
342
373
|
remediation: SERVER_REMEDIATIONS.unattendedConsent,
|
|
343
374
|
},
|
|
344
375
|
u.notify_webhook_default_declared
|