@bridge_gpt/mcp-server 0.2.60 → 0.2.65
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 +58 -11
- package/build/agent-launchers/claude-executor-adapter.js +134 -3
- package/build/agent-launchers/executor-adapter.js +54 -2
- package/build/agents.generated.js +10 -1
- package/build/bounded-wait.js +32 -1
- package/build/commands.generated.js +8 -7
- package/build/conductor/bridge-api-client.js +678 -14
- package/build/conductor/bring-up-facts.js +3 -1
- package/build/conductor/doctor.js +23 -14
- package/build/conductor/epic-child-continuation.js +324 -0
- package/build/conductor/epic-implementer-cli.js +142 -17
- package/build/conductor/git-hooks.js +529 -118
- package/build/conductor/hook-runtime-qualification.js +340 -0
- package/build/conductor/merge-execution-contract.js +30 -5
- package/build/conductor/merge-execution-eligibility.js +10 -9
- package/build/conductor/readiness-sections.js +104 -20
- package/build/conductor/readiness.js +129 -25
- package/build/conductor/recovery-cli.js +354 -13
- package/build/conductor/recovery-operations.js +251 -5
- package/build/conductor/repair-contract.js +8 -0
- package/build/conductor-bundle-artifacts.js +1 -0
- package/build/credential-store.js +194 -0
- package/build/docs.generated.js +2 -1
- package/build/doctor.js +161 -7
- package/build/drive-epic-key.js +12 -0
- package/build/drive-epic.js +1318 -111
- package/build/epic-coherence-input-cli.js +137 -0
- package/build/epic-coherence-input.js +920 -0
- package/build/epic-coherence-input.test-fixtures.js +175 -0
- package/build/epic-implementer-bin.js +35 -29
- package/build/epic-implementer-bundle-cli.js +14 -1
- package/build/epic-integration-pr.js +61 -15
- package/build/epic-launch-profile.js +464 -0
- package/build/epic-run-status.js +5 -0
- package/build/epic-shared/bridge-client.js +46 -4
- package/build/executor/cli.js +48 -0
- package/build/executor/completion-contract.js +423 -0
- package/build/executor/completion-supervision.js +286 -0
- package/build/executor/credentials.js +19 -1
- package/build/executor/deps.js +65 -1
- package/build/executor/env.js +6 -0
- package/build/executor/heartbeat.js +13 -3
- package/build/executor/http-client.js +117 -1
- package/build/executor/integration-job.js +759 -0
- package/build/executor/job-errors.js +48 -0
- package/build/executor/job-runner.js +1386 -35
- package/build/executor/job-types.js +10 -2
- package/build/executor/observation.js +114 -7
- package/build/executor/preflight.js +99 -0
- package/build/executor/process-containment.js +756 -0
- package/build/executor/process.js +235 -5
- package/build/executor/prompt-spec.js +262 -1
- package/build/executor/provider-checks.js +69 -0
- package/build/executor/rebase-finalization.js +351 -0
- package/build/executor/reconcile-overlap-job.js +77 -84
- package/build/executor/required-verification.js +313 -0
- package/build/executor/required-work-ledger.js +1121 -0
- package/build/executor/results.js +142 -0
- package/build/executor/resume-pre-spawn.js +87 -4
- package/build/executor/runner.js +246 -1
- package/build/executor/test-declaration.js +20 -0
- package/build/executor/types.js +55 -0
- package/build/executor/worker-command.js +49 -13
- package/build/executor/worker-finalization.js +88 -11
- package/build/executor/worker-guard-hook.js +29 -4
- package/build/executor/worktree.js +13 -0
- package/build/executor-launch.js +658 -0
- package/build/index.js +772 -954
- package/build/install-bridge-conductor.js +622 -40
- package/build/install-bridge.js +92 -16
- package/build/merge-pull-request.js +1 -1
- package/build/merge-workflow-config.js +54 -0
- package/build/merge-workflow-drift.js +35 -4
- package/build/merge-workflow.js +386 -2
- package/build/pipelines.generated.js +9 -8
- package/build/plan-discovery.js +137 -0
- package/build/plan-epic-implementer-eligibility.js +2 -2
- package/build/plane/cli.js +37 -3
- package/build/plane/member-logs.js +3 -0
- package/build/plane/member-roster.js +7 -1
- package/build/plane/shutdown.js +59 -1
- package/build/plane/supervisor.js +148 -0
- package/build/readme.generated.js +1 -1
- package/build/scripts/executor-protocol-contract-driver.js +13 -3
- package/build/setup-epic.js +130 -48
- package/build/version.generated.js +3 -3
- package/build/worker-guard-hook-bin.js +2 -2
- package/docs/CONDUCTOR.md +116 -2
- package/package.json +2 -1
|
@@ -4,8 +4,10 @@
|
|
|
4
4
|
* Single source of truth for which claimed job types the executor spawns and
|
|
5
5
|
* which of those are RECOVERY jobs. Recovery jobs (`remediate`/`ci_fix`/`rebase`/
|
|
6
6
|
* `reconcile_overlap`) resume the ticket's existing branch/PR rather than cutting
|
|
7
|
-
* a fresh branch off base —
|
|
8
|
-
* --auto` correction loop, `
|
|
7
|
+
* a fresh branch off base — `remediate`/`rebase` through the `/implement-ticket
|
|
8
|
+
* <KEY> --auto` correction loop, `ci_fix` through its focused prompt spec
|
|
9
|
+
* (BAPI-1296: never the full recipe), `reconcile_overlap` through its own job
|
|
10
|
+
* body. Dispatch
|
|
9
11
|
* (`job-runner.ts` `runClaimedJob`), prompt synthesis (`worker-command.ts`
|
|
10
12
|
* `resolveExecutorPrompt`), and worktree reuse (`job-runner.ts` `prepareSpawn` →
|
|
11
13
|
* `worktree.ts` `ensureExecutorWorktree`) all read from here so the recovery /
|
|
@@ -22,6 +24,12 @@
|
|
|
22
24
|
* `reconcile_overlap`). They work on the ticket's EXISTING branch/PR, so the
|
|
23
25
|
* executor reuses that branch instead of cutting a fresh one off base.
|
|
24
26
|
*
|
|
27
|
+
* BAPI-1296: `ci_fix` stays a recovery job — and an implementation-style job
|
|
28
|
+
* below — for branch reuse, the worktree lock, and finalization, but prompt
|
|
29
|
+
* selection no longer follows recovery membership for it: `worker-command.ts`
|
|
30
|
+
* refuses to synthesize `/implement-ticket` for `ci_fix`, and `job-runner.ts`
|
|
31
|
+
* renders its server-authored focused prompt spec instead.
|
|
32
|
+
*
|
|
25
33
|
* BAPI-1152: `reconcile_overlap` is a recovery job for worktree reuse and prompt
|
|
26
34
|
* resolution, but it does NOT re-enter `/implement-ticket`: it always carries an
|
|
27
35
|
* explicit server-rendered `payload.prompt`, which wins over the synthesis in
|
|
@@ -288,8 +288,10 @@ function parseRateLimitEvent(obj) {
|
|
|
288
288
|
* Stated exactly once per line so an operator reading it cold understands what
|
|
289
289
|
* happens next without cross-referencing the runbook.
|
|
290
290
|
*/
|
|
291
|
-
const RATE_LIMIT_CONSEQUENCE_TEXT = "
|
|
292
|
-
"
|
|
291
|
+
const RATE_LIMIT_CONSEQUENCE_TEXT = "This warning is advisory only and triggers no waiting. Automatic capacity " +
|
|
292
|
+
"waiting happens only when the worker then fails with the supported terminal " +
|
|
293
|
+
"rejection and the server advertised capacity recovery for this job; otherwise " +
|
|
294
|
+
"the failure follows ordinary failure handling.";
|
|
293
295
|
/** Fixed runbook reference appended to every rate-limit diagnostic (BAPI-898). */
|
|
294
296
|
const RATE_LIMIT_RUNBOOK_REFERENCE = "See docs/claude/epic-implementer-v2-operator-runbook.md §8.";
|
|
295
297
|
/**
|
|
@@ -354,6 +356,67 @@ export function parseClaudeStreamJsonLine(line) {
|
|
|
354
356
|
function pickString(value) {
|
|
355
357
|
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
356
358
|
}
|
|
359
|
+
/**
|
|
360
|
+
* The exact status that marks a terminal rejection. `allowed_warning`,
|
|
361
|
+
* `blocked`, and any unknown status are NOT terminal capacity: the first is a
|
|
362
|
+
* warning, and the others are not part of the supported contract.
|
|
363
|
+
*/
|
|
364
|
+
const TERMINAL_CAPACITY_REJECTED_STATUS = "rejected";
|
|
365
|
+
/** Closed shape for a `rateLimitType` token the terminal parser will accept. */
|
|
366
|
+
const TERMINAL_CAPACITY_LIMIT_TYPE_PATTERN = /^[a-z0-9_]{1,64}$/;
|
|
367
|
+
/**
|
|
368
|
+
* Map the provider's limit window to a normalized cause. Long windows (the
|
|
369
|
+
* weekly ceilings) are account quota; shorter windows are rate limits.
|
|
370
|
+
*/
|
|
371
|
+
function terminalCapacityCauseFor(rateLimitType) {
|
|
372
|
+
return rateLimitType.startsWith("seven_day") ? "quota_exhausted" : "rate_limit_exhausted";
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Parse ONE stream-json line for the supported terminal-capacity event, or
|
|
376
|
+
* `undefined` for anything else (BAPI-1258).
|
|
377
|
+
*
|
|
378
|
+
* Accepts only `{"type":"rate_limit_event","rate_limit_info":{"status":"rejected",
|
|
379
|
+
* "rateLimitType":<closed token>, "resetsAt"?:<number>}}`. Unknown event types,
|
|
380
|
+
* unknown statuses, malformed tokens, and malformed reset values all yield
|
|
381
|
+
* `undefined` (a bad reset drops only the reset, not the rejection). Never
|
|
382
|
+
* throws: a failure here only means "no confirmed evidence".
|
|
383
|
+
*/
|
|
384
|
+
export function parseClaudeTerminalCapacityLine(line, nowMs) {
|
|
385
|
+
try {
|
|
386
|
+
const trimmed = typeof line === "string" ? line.trim() : "";
|
|
387
|
+
if (trimmed.length === 0)
|
|
388
|
+
return undefined;
|
|
389
|
+
const parsed = JSON.parse(trimmed);
|
|
390
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
391
|
+
return undefined;
|
|
392
|
+
const obj = parsed;
|
|
393
|
+
if (obj.type !== "rate_limit_event")
|
|
394
|
+
return undefined;
|
|
395
|
+
const info = obj.rate_limit_info;
|
|
396
|
+
if (!info || typeof info !== "object" || Array.isArray(info))
|
|
397
|
+
return undefined;
|
|
398
|
+
const record = info;
|
|
399
|
+
if (record.status !== TERMINAL_CAPACITY_REJECTED_STATUS)
|
|
400
|
+
return undefined;
|
|
401
|
+
const rateLimitType = record.rateLimitType;
|
|
402
|
+
if (typeof rateLimitType !== "string" || !TERMINAL_CAPACITY_LIMIT_TYPE_PATTERN.test(rateLimitType)) {
|
|
403
|
+
return undefined;
|
|
404
|
+
}
|
|
405
|
+
if (!Number.isFinite(nowMs))
|
|
406
|
+
return undefined;
|
|
407
|
+
const observation = {
|
|
408
|
+
cause: terminalCapacityCauseFor(rateLimitType),
|
|
409
|
+
observed_at: new Date(nowMs).toISOString(),
|
|
410
|
+
};
|
|
411
|
+
const resetAt = normalizeRateLimitResetAt(record.resetsAt, nowMs);
|
|
412
|
+
if (resetAt !== undefined)
|
|
413
|
+
observation.retry_not_before = resetAt;
|
|
414
|
+
return observation;
|
|
415
|
+
}
|
|
416
|
+
catch {
|
|
417
|
+
return undefined;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
357
420
|
// ---------------------------------------------------------------------------
|
|
358
421
|
// MCP surface assertion (BAPI-790)
|
|
359
422
|
// ---------------------------------------------------------------------------
|
|
@@ -528,8 +591,11 @@ export function createMcpSurfaceObserver(expectedServerNames, parseInitEvent) {
|
|
|
528
591
|
export const MAX_BUFFERED_ADVISORY_LINE_CHARS = 1_048_576;
|
|
529
592
|
/**
|
|
530
593
|
* Create the observation state. When `advisoryParserEnabled` is false, stdout is
|
|
531
|
-
* still timestamped (`last_stdout_at`) but never parsed
|
|
532
|
-
* parser is not load-bearing.
|
|
594
|
+
* still timestamped (`last_stdout_at`) but never parsed for advisory hints —
|
|
595
|
+
* proving the advisory parser is not load-bearing. The separate BAPI-1258
|
|
596
|
+
* terminal-capacity parser still reads lines unless
|
|
597
|
+
* `terminalCapacityParserEnabled` is `false`; it only ever yields "no evidence"
|
|
598
|
+
* on bad input, so ordinary success never depends on it either.
|
|
533
599
|
*/
|
|
534
600
|
export function createObservationState(deps, options) {
|
|
535
601
|
let git = {};
|
|
@@ -544,6 +610,15 @@ export function createObservationState(deps, options) {
|
|
|
544
610
|
* — leaves this unchanged.
|
|
545
611
|
*/
|
|
546
612
|
let rateLimit;
|
|
613
|
+
/**
|
|
614
|
+
* BAPI-1258: the terminal-capacity channel. The FIRST rejection fixes the
|
|
615
|
+
* cause and `observed_at`; a repeat of the same cause may only carry a later
|
|
616
|
+
* reset forward. A rejection with a DIFFERENT cause makes the evidence
|
|
617
|
+
* contradictory, and contradictory evidence is never reported.
|
|
618
|
+
*/
|
|
619
|
+
let terminalCapacity;
|
|
620
|
+
let terminalCapacityContradictory = false;
|
|
621
|
+
const terminalCapacityParserEnabled = options.terminalCapacityParserEnabled !== false;
|
|
547
622
|
/** Residual partial line carried across stdout chunks (BAPI-828). */
|
|
548
623
|
let buffer = "";
|
|
549
624
|
/** True while skipping the remainder of a line that blew the size cap. */
|
|
@@ -579,10 +654,37 @@ export function createObservationState(deps, options) {
|
|
|
579
654
|
}
|
|
580
655
|
}
|
|
581
656
|
};
|
|
657
|
+
const consumeTerminalCapacityLine = (line) => {
|
|
658
|
+
if (terminalCapacityContradictory)
|
|
659
|
+
return;
|
|
660
|
+
const observed = parseClaudeTerminalCapacityLine(line, deps.now());
|
|
661
|
+
if (observed === undefined)
|
|
662
|
+
return;
|
|
663
|
+
if (terminalCapacity === undefined) {
|
|
664
|
+
terminalCapacity = observed;
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
if (terminalCapacity.cause !== observed.cause) {
|
|
668
|
+
terminalCapacityContradictory = true;
|
|
669
|
+
terminalCapacity = undefined;
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
if (observed.retry_not_before !== undefined &&
|
|
673
|
+
(terminalCapacity.retry_not_before === undefined ||
|
|
674
|
+
observed.retry_not_before > terminalCapacity.retry_not_before)) {
|
|
675
|
+
terminalCapacity = { ...terminalCapacity, retry_not_before: observed.retry_not_before };
|
|
676
|
+
}
|
|
677
|
+
};
|
|
678
|
+
const consumeLine = (line) => {
|
|
679
|
+
if (options.advisoryParserEnabled)
|
|
680
|
+
consumeAdvisoryLine(line);
|
|
681
|
+
if (terminalCapacityParserEnabled)
|
|
682
|
+
consumeTerminalCapacityLine(line);
|
|
683
|
+
};
|
|
582
684
|
return {
|
|
583
685
|
recordStdout(chunk) {
|
|
584
686
|
advisory.last_stdout_at = new Date(deps.now()).toISOString();
|
|
585
|
-
if (!options.advisoryParserEnabled)
|
|
687
|
+
if (!options.advisoryParserEnabled && !terminalCapacityParserEnabled)
|
|
586
688
|
return;
|
|
587
689
|
if (typeof chunk !== "string" || chunk.length === 0)
|
|
588
690
|
return;
|
|
@@ -605,7 +707,7 @@ export function createObservationState(deps, options) {
|
|
|
605
707
|
buffer += input;
|
|
606
708
|
let newlineAt = buffer.indexOf("\n");
|
|
607
709
|
while (newlineAt !== -1) {
|
|
608
|
-
|
|
710
|
+
consumeLine(buffer.slice(0, newlineAt));
|
|
609
711
|
buffer = buffer.slice(newlineAt + 1);
|
|
610
712
|
newlineAt = buffer.indexOf("\n");
|
|
611
713
|
}
|
|
@@ -625,7 +727,7 @@ export function createObservationState(deps, options) {
|
|
|
625
727
|
// is harmless: a truncated line does not parse, and the first-wins guard
|
|
626
728
|
// above makes a repeat parse of a complete line a no-op.
|
|
627
729
|
if (buffer.length > 0)
|
|
628
|
-
|
|
730
|
+
consumeLine(buffer);
|
|
629
731
|
},
|
|
630
732
|
setGitTelemetry(telemetry) {
|
|
631
733
|
git = telemetry;
|
|
@@ -667,5 +769,10 @@ export function createObservationState(deps, options) {
|
|
|
667
769
|
residue.attempt_end_sha = attemptEndSha;
|
|
668
770
|
return residue;
|
|
669
771
|
},
|
|
772
|
+
terminalCapacity() {
|
|
773
|
+
if (terminalCapacityContradictory || terminalCapacity === undefined)
|
|
774
|
+
return undefined;
|
|
775
|
+
return { ...terminalCapacity };
|
|
776
|
+
},
|
|
670
777
|
};
|
|
671
778
|
}
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { DEFAULT_PROBE_TIMEOUT_MS } from "../agent-capabilities/types.js";
|
|
2
2
|
import { inspectExecutorAgentAdapter, inspectionForFailedResolution, } from "../agent-launchers/executor-adapter-inspection.js";
|
|
3
3
|
import { resolveExecutorAgentAdapter } from "../agent-launchers/executor-adapter-registry.js";
|
|
4
|
+
import { resolveCompletionEvidenceCapability } from "../agent-launchers/executor-adapter.js";
|
|
5
|
+
import { COMPLETION_EVIDENCE_CAPABILITY, REQUIRED_VERIFICATION_CAPABILITY, runtimeMatchesSupported, } from "./completion-contract.js";
|
|
4
6
|
import { resolveExecutorAgentId } from "./agent-identity.js";
|
|
5
7
|
import { resolveAllExecutorApiAccess, resolveBaseUrl, EXECUTOR_BASE_URL_REQUIRED_MESSAGE, } from "./credentials.js";
|
|
8
|
+
import { FOCUSED_REBASE_CAPABILITY } from "./types.js";
|
|
6
9
|
/**
|
|
7
10
|
* Environment override for the deny probe's headless timeout (BAPI-722).
|
|
8
11
|
*
|
|
@@ -80,6 +83,63 @@ export function createDenyProbeCache() {
|
|
|
80
83
|
},
|
|
81
84
|
};
|
|
82
85
|
}
|
|
86
|
+
/** Bound on a completion-evidence refusal reason (it is logged every cycle). */
|
|
87
|
+
const COMPLETION_REFUSAL_MAX_CHARS = 200;
|
|
88
|
+
/**
|
|
89
|
+
* BAPI-1301 (D4a) — decide whether this executor may advertise
|
|
90
|
+
* `completion_evidence.v1`.
|
|
91
|
+
*
|
|
92
|
+
* Probes the installed runtime through the adapter's declared probe and compares
|
|
93
|
+
* it with BOTH the adapter's declared identity and the approved identity the
|
|
94
|
+
* executor's lifecycle parser was measured against. Every mismatched field is
|
|
95
|
+
* named in the bounded reason; values are safe by construction (a parsed version
|
|
96
|
+
* or the `unrecognized` placeholder, closed platform ids, fixed schema names).
|
|
97
|
+
* Never throws and never produces a fatal finding.
|
|
98
|
+
*/
|
|
99
|
+
export async function evaluateCompletionEvidenceQualification(adapter, deps) {
|
|
100
|
+
const bounded = (text) => text.slice(0, COMPLETION_REFUSAL_MAX_CHARS);
|
|
101
|
+
const capability = resolveCompletionEvidenceCapability(adapter);
|
|
102
|
+
if (!capability.supported) {
|
|
103
|
+
return { qualified: false, refusal: bounded(capability.reason), runtime: null };
|
|
104
|
+
}
|
|
105
|
+
let probed;
|
|
106
|
+
try {
|
|
107
|
+
probed = await capability.value.probeRuntime({
|
|
108
|
+
runCommand: deps.runCommand,
|
|
109
|
+
platform: deps.platform,
|
|
110
|
+
// Absent → the adapter's default (`process.arch`); injectable for tests.
|
|
111
|
+
...(deps.arch !== undefined ? { arch: deps.arch } : {}),
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
probed = null;
|
|
116
|
+
}
|
|
117
|
+
if (probed === null) {
|
|
118
|
+
return { qualified: false, refusal: "runtime identity probe did not run", runtime: null };
|
|
119
|
+
}
|
|
120
|
+
const declared = capability.value.runtime;
|
|
121
|
+
const mismatches = [];
|
|
122
|
+
for (const key of ["adapter", "cli_version", "platform", "event_schema"]) {
|
|
123
|
+
if (probed[key] !== declared[key]) {
|
|
124
|
+
mismatches.push(`${key} ${String(probed[key]).slice(0, 40)} != ${declared[key]}`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
if (mismatches.length > 0) {
|
|
128
|
+
return {
|
|
129
|
+
qualified: false,
|
|
130
|
+
refusal: bounded(`probed runtime differs from the approved identity: ${mismatches.join("; ")}`),
|
|
131
|
+
runtime: probed,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
if (!runtimeMatchesSupported(probed)) {
|
|
135
|
+
return {
|
|
136
|
+
qualified: false,
|
|
137
|
+
refusal: "adapter-declared runtime is not the approved completion-evidence runtime",
|
|
138
|
+
runtime: probed,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
return { qualified: true, refusal: null, runtime: probed };
|
|
142
|
+
}
|
|
83
143
|
/** Fail-fast report shape for a preflight that refused before probing. */
|
|
84
144
|
function refusedPreflight(fatalFindings, warnings, adapter) {
|
|
85
145
|
return {
|
|
@@ -346,6 +406,16 @@ export async function collectExecutorPreflight(options, deps, seams = {}) {
|
|
|
346
406
|
}
|
|
347
407
|
}
|
|
348
408
|
warnings.push(...adapterInspection.inspection.warnings);
|
|
409
|
+
// --- Completion-evidence qualification (never fatal, BAPI-1301) --------
|
|
410
|
+
// Last, and outside `fatalFindings` on purpose: an executor that cannot
|
|
411
|
+
// supervise required work to completion is still a correct executor for every
|
|
412
|
+
// other job. It simply omits `completion_evidence.v1` from its claim manifest,
|
|
413
|
+
// and the server's claim filter keeps covered work away from it. One bounded
|
|
414
|
+
// line says why, so an operator expecting covered work can see the cause.
|
|
415
|
+
const completion = await evaluateCompletionEvidenceQualification(adapter, deps);
|
|
416
|
+
if (!completion.qualified && completion.refusal !== null) {
|
|
417
|
+
warnings.push(`completion evidence not advertised: ${completion.refusal}`);
|
|
418
|
+
}
|
|
349
419
|
return {
|
|
350
420
|
ok: fatalFindings.length === 0,
|
|
351
421
|
fatalFindings,
|
|
@@ -356,6 +426,9 @@ export async function collectExecutorPreflight(options, deps, seams = {}) {
|
|
|
356
426
|
ghAuthed,
|
|
357
427
|
denyEnforced,
|
|
358
428
|
adapter: adapterInspection.inspection,
|
|
429
|
+
completionEvidenceQualified: completion.qualified,
|
|
430
|
+
completionEvidenceRefusal: completion.refusal,
|
|
431
|
+
completionRuntime: completion.runtime,
|
|
359
432
|
};
|
|
360
433
|
}
|
|
361
434
|
/**
|
|
@@ -379,6 +452,15 @@ export async function collectExecutorPreflight(options, deps, seams = {}) {
|
|
|
379
452
|
* `undefined`-valued. For the same reason `repo_wide` is never serialized as
|
|
380
453
|
* `false`: `false` is the server's default and means "not an opt-out", so
|
|
381
454
|
* sending it would add a key that carries no information.
|
|
455
|
+
*
|
|
456
|
+
* BAPI-1301 (D4a) adds a third optional key, `capabilities`, under the same
|
|
457
|
+
* rule: `["completion_evidence.v1"]` only when preflight qualified the runtime.
|
|
458
|
+
*
|
|
459
|
+
* BAPI-1311 makes `capabilities` ADDITIVE: `focused_rebase.v1` is advertised by
|
|
460
|
+
* every executor this function builds a manifest for — a manifest is only built
|
|
461
|
+
* once ordinary preflight permits claiming — independently of completion-evidence
|
|
462
|
+
* qualification. The completion tokens keep their exact conditional behavior and
|
|
463
|
+
* order; the focused token is appended after them.
|
|
382
464
|
*/
|
|
383
465
|
export function buildClaimManifest(report, options, freeSlots) {
|
|
384
466
|
return {
|
|
@@ -392,5 +474,22 @@ export function buildClaimManifest(report, options, freeSlots) {
|
|
|
392
474
|
disk_free_gb: report.diskFreeGb ?? 0,
|
|
393
475
|
...(options.epicRunIds !== undefined ? { epic_run_ids: options.epicRunIds } : {}),
|
|
394
476
|
...(options.repoWide === true ? { repo_wide: true } : {}),
|
|
477
|
+
// BAPI-1301 (D4a): advertised ONLY when preflight qualified the exact
|
|
478
|
+
// runtime; otherwise the key is absent (never `[]`), keeping the manifest's
|
|
479
|
+
// `Object.keys()` shape for every unqualified executor and older server.
|
|
480
|
+
// BAPI-1302 (D4c): a qualified executor also runs declared checks itself
|
|
481
|
+
// (`required-verification.ts`), so it advertises that it can claim a job
|
|
482
|
+
// carrying a required-verification declaration. An older server drops the
|
|
483
|
+
// unknown token (`normalize_declared_capabilities`), so it is safe to send.
|
|
484
|
+
// BAPI-1311: the focused-rebase token rides every claiming manifest, after
|
|
485
|
+
// any completion tokens. It is NOT a completion-evidence statement — rebase is
|
|
486
|
+
// not a covered job type — so it never waits on that qualification. An older
|
|
487
|
+
// server drops the unknown token (`normalize_declared_capabilities`).
|
|
488
|
+
capabilities: [
|
|
489
|
+
...(report.completionEvidenceQualified === true
|
|
490
|
+
? [COMPLETION_EVIDENCE_CAPABILITY, REQUIRED_VERIFICATION_CAPABILITY]
|
|
491
|
+
: []),
|
|
492
|
+
FOCUSED_REBASE_CAPABILITY,
|
|
493
|
+
],
|
|
395
494
|
};
|
|
396
495
|
}
|