instar 1.3.511 → 1.3.513
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/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +168 -2
- package/dist/commands/server.js.map +1 -1
- package/dist/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +42 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/core/CoherenceJournal.d.ts +31 -1
- package/dist/core/CoherenceJournal.d.ts.map +1 -1
- package/dist/core/CoherenceJournal.js +49 -2
- package/dist/core/CoherenceJournal.js.map +1 -1
- package/dist/core/JournalSyncApplier.d.ts.map +1 -1
- package/dist/core/JournalSyncApplier.js +18 -0
- package/dist/core/JournalSyncApplier.js.map +1 -1
- package/dist/core/MeshRpc.d.ts +6 -0
- package/dist/core/MeshRpc.d.ts.map +1 -1
- package/dist/core/MeshRpc.js +1 -0
- package/dist/core/MeshRpc.js.map +1 -1
- package/dist/core/PeerPresencePuller.d.ts +19 -0
- package/dist/core/PeerPresencePuller.d.ts.map +1 -1
- package/dist/core/PeerPresencePuller.js +7 -0
- package/dist/core/PeerPresencePuller.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +57 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/PreferencesManager.d.ts +34 -0
- package/dist/core/PreferencesManager.d.ts.map +1 -1
- package/dist/core/PreferencesManager.js +74 -0
- package/dist/core/PreferencesManager.js.map +1 -1
- package/dist/core/PreferencesSync.d.ts +167 -0
- package/dist/core/PreferencesSync.d.ts.map +1 -0
- package/dist/core/PreferencesSync.js +307 -0
- package/dist/core/PreferencesSync.js.map +1 -0
- package/dist/core/devGatedFeatures.d.ts.map +1 -1
- package/dist/core/devGatedFeatures.js +5 -0
- package/dist/core/devGatedFeatures.js.map +1 -1
- package/dist/core/types.d.ts +34 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/monitoring/GreenPrAutoMerger.d.ts +188 -0
- package/dist/monitoring/GreenPrAutoMerger.d.ts.map +1 -0
- package/dist/monitoring/GreenPrAutoMerger.js +393 -0
- package/dist/monitoring/GreenPrAutoMerger.js.map +1 -0
- package/dist/monitoring/GuardLatchStore.d.ts +134 -0
- package/dist/monitoring/GuardLatchStore.d.ts.map +1 -0
- package/dist/monitoring/GuardLatchStore.js +255 -0
- package/dist/monitoring/GuardLatchStore.js.map +1 -0
- package/dist/monitoring/MergeRunner.d.ts +126 -0
- package/dist/monitoring/MergeRunner.d.ts.map +1 -0
- package/dist/monitoring/MergeRunner.js +296 -0
- package/dist/monitoring/MergeRunner.js.map +1 -0
- package/dist/monitoring/floorDriftCanary.d.ts +88 -0
- package/dist/monitoring/floorDriftCanary.d.ts.map +1 -0
- package/dist/monitoring/floorDriftCanary.js +89 -0
- package/dist/monitoring/floorDriftCanary.js.map +1 -0
- package/dist/monitoring/greenPrAutomergeWiring.d.ts +73 -0
- package/dist/monitoring/greenPrAutomergeWiring.d.ts.map +1 -0
- package/dist/monitoring/greenPrAutomergeWiring.js +273 -0
- package/dist/monitoring/greenPrAutomergeWiring.js.map +1 -0
- package/dist/monitoring/greenPrLogic.d.ts +121 -0
- package/dist/monitoring/greenPrLogic.d.ts.map +1 -0
- package/dist/monitoring/greenPrLogic.js +198 -0
- package/dist/monitoring/greenPrLogic.js.map +1 -0
- package/dist/monitoring/guardManifest.d.ts.map +1 -1
- package/dist/monitoring/guardManifest.js +12 -0
- package/dist/monitoring/guardManifest.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +1 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts +5 -0
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +3 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/CapabilityIndex.d.ts.map +1 -1
- package/dist/server/CapabilityIndex.js +16 -0
- package/dist/server/CapabilityIndex.js.map +1 -1
- package/dist/server/routes.d.ts +10 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +137 -1
- package/dist/server/routes.js.map +1 -1
- package/dist/server/stopGate.d.ts +45 -0
- package/dist/server/stopGate.d.ts.map +1 -1
- package/dist/server/stopGate.js +74 -0
- package/dist/server/stopGate.js.map +1 -1
- package/package.json +1 -1
- package/scripts/safe-merge.mjs +449 -59
- package/src/data/builtin-manifest.json +64 -64
- package/src/scaffold/templates.ts +1 -0
- package/upgrades/1.3.512.md +65 -0
- package/upgrades/1.3.513.md +61 -0
- package/upgrades/side-effects/green-pr-automerge-enforcement.md +171 -0
- package/upgrades/side-effects/multi-machine-seamlessness-ws21-preferences-pool.md +116 -0
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GreenPrAutoMerger — the background watcher that merges a green, mergeable,
|
|
3
|
+
* non-held PR this agent authored, surviving session death (Phase 7 becomes
|
|
4
|
+
* machinery, not memory). green-pr-automerge-enforcement R1–R11.
|
|
5
|
+
*
|
|
6
|
+
* Layer 1 (the guarantee). Ticks run ONLY on the multi-machine lease holder;
|
|
7
|
+
* the first tick after acquiring the lease is OBSERVE-ONLY (warm-up: seed hold
|
|
8
|
+
* memory, build the snapshot, read peer latches). At most one merge attempt per
|
|
9
|
+
* tick, single-flight with a liveness guarantee that survives a server restart
|
|
10
|
+
* (the MergeRunner owns the durable in-flight record + orphan reap). Everything
|
|
11
|
+
* that decides "is this change good" already happened upstream; safe-merge
|
|
12
|
+
* re-verifies at act time. Tier-0 supervision — the only discretionary call is
|
|
13
|
+
* hold/candidate status and its failure direction is fail-toward-skip.
|
|
14
|
+
*
|
|
15
|
+
* All I/O is injected (deps) so every decision path is unit-testable without gh,
|
|
16
|
+
* real processes, or a real lease.
|
|
17
|
+
*/
|
|
18
|
+
import { EventEmitter } from 'node:events';
|
|
19
|
+
import { classifyCandidate, holdReasonOf, selectOldest, debounceHoldRelease, applyOutcome, maybeRearm, episodeEligible, freshBreaker, feedBreaker, breakerBlocking, validateTimeoutInvariant, } from './greenPrLogic.js';
|
|
20
|
+
const DEFAULTS = {
|
|
21
|
+
enabled: false,
|
|
22
|
+
dryRun: false,
|
|
23
|
+
tickIntervalMs: 600_000,
|
|
24
|
+
maxAttempts: 3,
|
|
25
|
+
maxRearmEpisodes: 3,
|
|
26
|
+
breakerThreshold: 3,
|
|
27
|
+
deadlineKillBreakerThreshold: 3,
|
|
28
|
+
busySkipBreakerThreshold: 3,
|
|
29
|
+
breakerCooldownMin: 60,
|
|
30
|
+
mergeTimeoutMs: 1_500_000,
|
|
31
|
+
mergeKillGraceMs: 60_000,
|
|
32
|
+
expectedGhLogin: '',
|
|
33
|
+
identityRecheckTicks: 6,
|
|
34
|
+
holdReleaseTicks: 2,
|
|
35
|
+
staleHoldDays: 7,
|
|
36
|
+
backoffBaseMs: 30_000,
|
|
37
|
+
};
|
|
38
|
+
export function freshState() {
|
|
39
|
+
return {
|
|
40
|
+
consecutiveWarmupOnlyTenures: 0,
|
|
41
|
+
breaker: freshBreaker(),
|
|
42
|
+
episodes: {},
|
|
43
|
+
holdMemory: {},
|
|
44
|
+
snapshot: { at: 0, entries: [] },
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
export class GreenPrAutoMerger extends EventEmitter {
|
|
48
|
+
deps;
|
|
49
|
+
cfg;
|
|
50
|
+
tickHandle = null;
|
|
51
|
+
inFlight = false;
|
|
52
|
+
tickCount = 0;
|
|
53
|
+
/** Resolved at boot: does the timeout invariant hold? */
|
|
54
|
+
invariantOk;
|
|
55
|
+
invariantReason;
|
|
56
|
+
constructor(deps, cfg) {
|
|
57
|
+
super();
|
|
58
|
+
this.deps = deps;
|
|
59
|
+
this.cfg = {
|
|
60
|
+
...DEFAULTS,
|
|
61
|
+
...cfg,
|
|
62
|
+
agentNamespace: cfg.agentNamespace,
|
|
63
|
+
repo: cfg.repo,
|
|
64
|
+
expectedGhLogin: cfg.expectedGhLogin ?? '',
|
|
65
|
+
};
|
|
66
|
+
const inv = validateTimeoutInvariant(this.cfg.busySkipBreakerThreshold, this.cfg.tickIntervalMs, this.cfg.mergeTimeoutMs, this.cfg.mergeKillGraceMs);
|
|
67
|
+
this.invariantOk = inv.ok;
|
|
68
|
+
this.invariantReason = inv.reason;
|
|
69
|
+
}
|
|
70
|
+
start() {
|
|
71
|
+
if (!this.cfg.enabled || this.tickHandle)
|
|
72
|
+
return;
|
|
73
|
+
if (!this.invariantOk) {
|
|
74
|
+
// Boot refusal must be LOUD (B24) — never start with an inverted invariant.
|
|
75
|
+
this.deps.audit({ kind: 'green-pr-automerge', event: 'boot-refused-invariant', reason: this.invariantReason });
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
// Warm-up + orphan reap happens on the first acting tick.
|
|
79
|
+
this.tickHandle = setInterval(() => { void this.tick(); }, this.cfg.tickIntervalMs);
|
|
80
|
+
if (typeof this.tickHandle.unref === 'function')
|
|
81
|
+
this.tickHandle.unref();
|
|
82
|
+
}
|
|
83
|
+
stop() {
|
|
84
|
+
if (this.tickHandle) {
|
|
85
|
+
clearInterval(this.tickHandle);
|
|
86
|
+
this.tickHandle = null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
snapshot() { return this.deps.loadState(); }
|
|
90
|
+
/**
|
|
91
|
+
* The /hold route's handler (R3): validate the PR is open + in this agent's
|
|
92
|
+
* namespace, then apply the marker via gh. Lease-INDEPENDENT — a hold
|
|
93
|
+
* originates wherever the session lives. Honest non-2xx on failure (a hold
|
|
94
|
+
* that silently failed to apply would be merged ~10 minutes later).
|
|
95
|
+
*/
|
|
96
|
+
async applyHold(pr, reason) {
|
|
97
|
+
const elig = await this.deps.holdEligible(pr);
|
|
98
|
+
if (!elig.ok)
|
|
99
|
+
return { ok: false, status: elig.status ?? 404, detail: elig.detail ?? 'PR not eligible for hold', pr };
|
|
100
|
+
try {
|
|
101
|
+
const applied = await this.deps.applyHoldMarker(pr, reason);
|
|
102
|
+
if (!applied)
|
|
103
|
+
return { ok: false, status: 502, detail: 'gh failed to apply the hold marker', pr };
|
|
104
|
+
this.deps.audit({ kind: 'green-pr-automerge', event: 'hold-applied', pr });
|
|
105
|
+
return { ok: true, pr };
|
|
106
|
+
}
|
|
107
|
+
catch (e) { /* @silent-fallback-ok: green-pr-automerge fail-safe — skip/refuse, never over-merge; safe-merge is the act-time authority */
|
|
108
|
+
return { ok: false, status: 502, detail: String(e?.message).slice(0, 200), pr };
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/** The cron / manual-trigger entry point. Lease-gated; single-flight; warm-up. */
|
|
112
|
+
async tick(opts = {}) {
|
|
113
|
+
this.tickCount += 1;
|
|
114
|
+
const state = this.deps.loadState();
|
|
115
|
+
state.lastTickAt = this.deps.now();
|
|
116
|
+
// Lease gate (R10): ticks run only on the holder.
|
|
117
|
+
if (!safeBool(() => this.deps.holdsLease())) {
|
|
118
|
+
this.deps.saveState(state);
|
|
119
|
+
return { acted: false, reason: 'not-lease-holder' };
|
|
120
|
+
}
|
|
121
|
+
// Single-flight (R5): never overlap an in-flight attempt.
|
|
122
|
+
if (this.inFlight) {
|
|
123
|
+
state.breaker = feedBreaker(state.breaker, 'busy-skip', this.deps.now(), this.breakerCfg());
|
|
124
|
+
this.deps.audit({ kind: 'green-pr-automerge', event: 'tick-skipped-busy' });
|
|
125
|
+
this.deps.saveState(state);
|
|
126
|
+
return { acted: false, reason: 'busy' };
|
|
127
|
+
}
|
|
128
|
+
// Dual-latch gate (R9): rollback / emergency-pause / unreadable → no merge.
|
|
129
|
+
const gate = this.deps.latches.isMergeAllowed();
|
|
130
|
+
if (!gate.allowed) {
|
|
131
|
+
this.deps.audit({ kind: 'green-pr-automerge', event: 'tick-skipped-disabled', reason: gate.reason });
|
|
132
|
+
this.deps.saveState(state);
|
|
133
|
+
return { acted: false, reason: `disabled:${gate.reason}` };
|
|
134
|
+
}
|
|
135
|
+
// Breaker open within cooldown → skip.
|
|
136
|
+
if (breakerBlocking(state.breaker, this.deps.now(), this.cfg.breakerCooldownMin * 60_000)) {
|
|
137
|
+
this.deps.audit({ kind: 'green-pr-automerge', event: 'tick-skipped-breaker-open' });
|
|
138
|
+
this.deps.saveState(state);
|
|
139
|
+
return { acted: false, reason: 'breaker-open' };
|
|
140
|
+
}
|
|
141
|
+
// Warm-up (R10): the first tick of a new lease tenure is OBSERVE-ONLY.
|
|
142
|
+
const epoch = safeNum(() => this.deps.leaseEpoch());
|
|
143
|
+
const isWarmup = state.lastActingEpoch !== epoch;
|
|
144
|
+
let candidates;
|
|
145
|
+
try {
|
|
146
|
+
candidates = await this.deps.listOpenPrs();
|
|
147
|
+
state.lastSuccessfulListAt = this.deps.now();
|
|
148
|
+
state.breaker = feedBreaker(state.breaker, 'reset', this.deps.now(), this.breakerCfg());
|
|
149
|
+
}
|
|
150
|
+
catch (e) { /* @silent-fallback-ok: green-pr-automerge fail-safe — skip/refuse, never over-merge; safe-merge is the act-time authority */
|
|
151
|
+
// Tick-failed canary (L5(b)): a failed/unparseable list call feeds the breaker.
|
|
152
|
+
state.breaker = feedBreaker(state.breaker, 'tick-failed', this.deps.now(), this.breakerCfg());
|
|
153
|
+
this.deps.audit({ kind: 'green-pr-automerge', event: 'tick-failed', class: 'list', detail: String(e?.message).slice(0, 200) });
|
|
154
|
+
this.deps.saveState(state);
|
|
155
|
+
return { acted: false, reason: 'list-failed' };
|
|
156
|
+
}
|
|
157
|
+
// Build the candidate set (cheap fields) + the Layer-2 snapshot.
|
|
158
|
+
const { eligible, snapshotEntries } = await this.gather(candidates, state);
|
|
159
|
+
state.snapshot = { at: this.deps.now(), entries: snapshotEntries };
|
|
160
|
+
if (isWarmup) {
|
|
161
|
+
// Seed hold memory + snapshot + reap any orphan; begin merges next tick.
|
|
162
|
+
await this.warmupReap(state);
|
|
163
|
+
state.lastActingEpoch = epoch;
|
|
164
|
+
state.consecutiveWarmupOnlyTenures += 1;
|
|
165
|
+
if (state.consecutiveWarmupOnlyTenures >= 3) {
|
|
166
|
+
await this.refreshAggregate(state, [`waiting:lease-flap — ${state.consecutiveWarmupOnlyTenures} consecutive warm-up-only tenures (running, seeing PRs, never permitted to act)`]);
|
|
167
|
+
}
|
|
168
|
+
this.deps.audit({ kind: 'green-pr-automerge', event: 'tick-warm-up', candidates: eligible.length });
|
|
169
|
+
this.deps.saveState(state);
|
|
170
|
+
return { acted: false, reason: 'warm-up' };
|
|
171
|
+
}
|
|
172
|
+
state.lastActingEpoch = epoch;
|
|
173
|
+
state.consecutiveWarmupOnlyTenures = 0;
|
|
174
|
+
const target = selectOldest(eligible);
|
|
175
|
+
if (!target) {
|
|
176
|
+
this.deps.saveState(state);
|
|
177
|
+
return { acted: false, reason: 'no-candidate' };
|
|
178
|
+
}
|
|
179
|
+
// Act (Step 4 engine). dryRun observes only.
|
|
180
|
+
if (this.cfg.dryRun) {
|
|
181
|
+
this.deps.audit({ kind: 'green-pr-automerge', event: 'would-merge', pr: target.number, head: target.headRefOid });
|
|
182
|
+
this.deps.saveState(state);
|
|
183
|
+
return { acted: false, reason: 'dry-run' };
|
|
184
|
+
}
|
|
185
|
+
const acted = await this.act(target, state);
|
|
186
|
+
this.deps.saveState(state);
|
|
187
|
+
return { acted, reason: acted ? 'acted' : 'skipped' };
|
|
188
|
+
}
|
|
189
|
+
// ---- candidate gathering -----------------------------------------------
|
|
190
|
+
async gather(prs, state) {
|
|
191
|
+
const eligible = [];
|
|
192
|
+
const snapshotEntries = [];
|
|
193
|
+
const nowMs = this.deps.now();
|
|
194
|
+
for (const pr of prs) {
|
|
195
|
+
const verdict = classifyCandidate(pr, this.cfg.agentNamespace);
|
|
196
|
+
// Hold-release debounce (R3): a PR that WAS held resumes only after the
|
|
197
|
+
// marker is absent for holdReleaseTicks AND tickIntervalMs elapsed.
|
|
198
|
+
const heldNow = holdReasonOf(pr) !== null;
|
|
199
|
+
const deb = debounceHoldRelease(state.holdMemory[pr.number], heldNow, nowMs, this.cfg.holdReleaseTicks, this.cfg.tickIntervalMs);
|
|
200
|
+
if (deb.mem)
|
|
201
|
+
state.holdMemory[pr.number] = deb.mem;
|
|
202
|
+
else
|
|
203
|
+
delete state.holdMemory[pr.number];
|
|
204
|
+
if (!verdict.eligible) {
|
|
205
|
+
if (verdict.skip === 'held' && deb.transition === 'released') {
|
|
206
|
+
this.deps.audit({ kind: 'green-pr-automerge', event: 'hold-released', pr: pr.number });
|
|
207
|
+
}
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
// verdict.eligible but the hold debounce may still hold it back this tick.
|
|
211
|
+
if (heldNow || !deb.resumeEligible)
|
|
212
|
+
continue;
|
|
213
|
+
// Protected-paths gate (enumerated to exhaustion).
|
|
214
|
+
let pp;
|
|
215
|
+
try {
|
|
216
|
+
pp = await this.deps.protectedPaths(pr);
|
|
217
|
+
}
|
|
218
|
+
catch { /* @silent-fallback-ok: green-pr-automerge fail-safe — skip/refuse, never over-merge; safe-merge is the act-time authority */
|
|
219
|
+
pp = { touches: false, unverifiable: true };
|
|
220
|
+
}
|
|
221
|
+
if (pp.unverifiable) {
|
|
222
|
+
this.deps.audit({ kind: 'green-pr-automerge', event: 'waiting', class: 'protected-paths-unverifiable', pr: pr.number });
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
if (pp.touches) {
|
|
226
|
+
// Excluded from auto-merge; surfaced to the operator + included in the
|
|
227
|
+
// Layer-2 snapshot as the operator-routed variant (round-5/6).
|
|
228
|
+
snapshotEntries.push({ pr: pr.number, headRefName: pr.headRefName, headRefOid: pr.headRefOid, kind: 'protected-paths' });
|
|
229
|
+
await this.refreshAggregate(state, [`PR #${pr.number} is green but touches protected paths — it needs your manual review and merge`]);
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
eligible.push(pr);
|
|
233
|
+
snapshotEntries.push({ pr: pr.number, headRefName: pr.headRefName, headRefOid: pr.headRefOid, kind: 'mergeable' });
|
|
234
|
+
}
|
|
235
|
+
return { eligible, snapshotEntries };
|
|
236
|
+
}
|
|
237
|
+
// ---- act ----------------------------------------------------------------
|
|
238
|
+
async act(target, state) {
|
|
239
|
+
// Identity contract (R4): verify gh login matches expectedGhLogin (TTL cached).
|
|
240
|
+
const idOk = await this.identityOk(state);
|
|
241
|
+
if (idOk !== 'ok') {
|
|
242
|
+
this.deps.audit({ kind: 'green-pr-automerge', event: `skipped:${idOk}` });
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
// Episode ladder eligibility.
|
|
246
|
+
let ep = state.episodes[target.number];
|
|
247
|
+
if (ep && ep.headRefOid !== target.headRefOid) {
|
|
248
|
+
const re = maybeRearm(ep, target.headRefOid, this.ladderCfg());
|
|
249
|
+
if (re === null) {
|
|
250
|
+
this.deps.audit({ kind: 'green-pr-automerge', event: 'gave-up-rearm-exhausted', pr: target.number });
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
ep = re;
|
|
254
|
+
state.episodes[target.number] = ep;
|
|
255
|
+
}
|
|
256
|
+
if (!episodeEligible(ep, this.deps.now()))
|
|
257
|
+
return false;
|
|
258
|
+
// Contract probe BEFORE every attempt (round-3: mid-run checkout swap).
|
|
259
|
+
const probe = await this.deps.runner.probeContract();
|
|
260
|
+
if (!probe.ok) {
|
|
261
|
+
this.deps.audit({ kind: 'green-pr-automerge', event: 'skipped:safe-merge-contract' });
|
|
262
|
+
return false;
|
|
263
|
+
}
|
|
264
|
+
// Re-fetch hold/state immediately before acting (R3 residual window bound).
|
|
265
|
+
const live = await this.deps.refetchPr(target.number);
|
|
266
|
+
if (!live) {
|
|
267
|
+
this.deps.audit({ kind: 'green-pr-automerge', event: 'skipped:vanished', pr: target.number });
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
if (live.state !== 'OPEN') {
|
|
271
|
+
this.recordOutcome(state, target, live.state === 'MERGED' ? 'merged-by-other' : 'closed-by-other');
|
|
272
|
+
return false;
|
|
273
|
+
}
|
|
274
|
+
if (live.isDraft || holdReasonOf({ isDraft: live.isDraft, title: live.title, labels: live.labels })) {
|
|
275
|
+
this.deps.audit({ kind: 'green-pr-automerge', event: 'skipped:held-on-refetch', pr: target.number });
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
if (live.headRefOid !== target.headRefOid) {
|
|
279
|
+
this.deps.audit({ kind: 'green-pr-automerge', event: 'skipped:head-moved', pr: target.number });
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
// Single-flight + spawn (the MergeRunner owns the durable in-flight record).
|
|
283
|
+
this.inFlight = true;
|
|
284
|
+
let result;
|
|
285
|
+
try {
|
|
286
|
+
this.deps.audit({ kind: 'green-pr-automerge', event: 'merge-attempted', pr: target.number, head: target.headRefOid });
|
|
287
|
+
result = await this.deps.runner.run({ pr: target.number, headRefOid: target.headRefOid, repo: this.cfg.repo });
|
|
288
|
+
}
|
|
289
|
+
catch (e) { /* @silent-fallback-ok: green-pr-automerge fail-safe — skip/refuse, never over-merge; safe-merge is the act-time authority */
|
|
290
|
+
result = { outcome: `error:runner-${String(e?.message).slice(0, 40)}`, confirmedMerged: false };
|
|
291
|
+
}
|
|
292
|
+
finally {
|
|
293
|
+
this.inFlight = false;
|
|
294
|
+
}
|
|
295
|
+
if (result.deadlineKilled) {
|
|
296
|
+
state.breaker = feedBreaker(state.breaker, 'deadline-kill', this.deps.now(), this.breakerCfg());
|
|
297
|
+
}
|
|
298
|
+
// B10: classify "merged" ONLY on independent confirmation.
|
|
299
|
+
const outcome = result.outcome === 'merged' && !result.confirmedMerged ? 'error:merge-unconfirmed' : result.outcome;
|
|
300
|
+
this.recordOutcome(state, target, outcome);
|
|
301
|
+
return outcome === 'merged';
|
|
302
|
+
}
|
|
303
|
+
recordOutcome(state, target, outcome) {
|
|
304
|
+
const ep = state.episodes[target.number] ?? { pr: target.number, headRefOid: target.headRefOid, attempts: 0, rearmEpisodes: 0, state: 'active' };
|
|
305
|
+
const folded = applyOutcome(ep, outcome, this.deps.now(), this.ladderCfg());
|
|
306
|
+
state.episodes[target.number] = folded.ep;
|
|
307
|
+
if (folded.feedsBreaker) {
|
|
308
|
+
// Per-PR ladder failures do NOT feed the global breaker directly (that is
|
|
309
|
+
// for tick/deadline/busy classes); they advance the per-PR ladder only.
|
|
310
|
+
}
|
|
311
|
+
this.deps.audit({ kind: 'green-pr-automerge', event: outcome, pr: target.number });
|
|
312
|
+
if (outcome === 'merged') {
|
|
313
|
+
// Reap the episode on success.
|
|
314
|
+
delete state.episodes[target.number];
|
|
315
|
+
}
|
|
316
|
+
else if (folded.ep.state === 'gave-up' && outcome !== 'merged-by-other' && outcome !== 'closed-by-other') {
|
|
317
|
+
void this.refreshAggregate(state, [`PR #${target.number} could not be auto-merged after ${folded.ep.attempts} attempts (${outcome}) — needs a look`]);
|
|
318
|
+
}
|
|
319
|
+
else if (outcome === 'closed-by-other') {
|
|
320
|
+
void this.refreshAggregate(state, [`PR #${target.number} was closed without merging — discarded work`]);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
// ---- identity (R4) ------------------------------------------------------
|
|
324
|
+
async identityOk(state) {
|
|
325
|
+
if (!this.cfg.expectedGhLogin)
|
|
326
|
+
return 'identity-unconfigured';
|
|
327
|
+
const ttlMs = this.cfg.identityRecheckTicks * this.cfg.tickIntervalMs;
|
|
328
|
+
const cached = state.identity;
|
|
329
|
+
const fresh = cached && (this.deps.now() - cached.resolvedAt) < ttlMs;
|
|
330
|
+
let login = fresh ? cached.login : null;
|
|
331
|
+
if (!fresh) {
|
|
332
|
+
login = await this.deps.resolveGhLogin();
|
|
333
|
+
state.identity = { login, resolvedAt: this.deps.now() };
|
|
334
|
+
}
|
|
335
|
+
if (login === null)
|
|
336
|
+
return 'identity-unresolved';
|
|
337
|
+
if (login !== this.cfg.expectedGhLogin)
|
|
338
|
+
return 'identity-mismatch';
|
|
339
|
+
return 'ok';
|
|
340
|
+
}
|
|
341
|
+
// ---- warm-up orphan reap (R5) ------------------------------------------
|
|
342
|
+
async warmupReap(state) {
|
|
343
|
+
try {
|
|
344
|
+
const r = await this.deps.runner.reapOrphan();
|
|
345
|
+
if (r.reaped)
|
|
346
|
+
this.deps.audit({ kind: 'green-pr-automerge', event: 'orphan-reaped', outcome: r.outcome });
|
|
347
|
+
}
|
|
348
|
+
catch (e) { /* @silent-fallback-ok: green-pr-automerge fail-safe — skip/refuse, never over-merge; safe-merge is the act-time authority */
|
|
349
|
+
this.deps.audit({ kind: 'green-pr-automerge', event: 'orphan-reap-incomplete', detail: String(e?.message).slice(0, 120) });
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
// ---- aggregated attention (P17) ----------------------------------------
|
|
353
|
+
async refreshAggregate(state, addLines) {
|
|
354
|
+
const lines = new Set([...(state.attentionLines ?? []), ...addLines]);
|
|
355
|
+
state.attentionLines = [...lines].slice(-50);
|
|
356
|
+
try {
|
|
357
|
+
await this.deps.postAttentionAggregate(state.attentionLines);
|
|
358
|
+
}
|
|
359
|
+
catch { /* @silent-fallback-ok: green-pr-automerge fail-safe — skip/refuse, never over-merge; safe-merge is the act-time authority */ /* attention delivery failure is non-fatal to the tick */ }
|
|
360
|
+
}
|
|
361
|
+
// ---- runtime control (R9, via GuardLatchStore wired at boot) ------------
|
|
362
|
+
// rollback / enable / pool-disarm are driven through the routes against the
|
|
363
|
+
// GuardLatchStore directly; the watcher only READS the gate each tick.
|
|
364
|
+
breakerCfg() {
|
|
365
|
+
return {
|
|
366
|
+
busySkipBreakerThreshold: this.cfg.busySkipBreakerThreshold,
|
|
367
|
+
deadlineKillBreakerThreshold: this.cfg.deadlineKillBreakerThreshold,
|
|
368
|
+
breakerThreshold: this.cfg.breakerThreshold,
|
|
369
|
+
breakerCooldownMs: this.cfg.breakerCooldownMin * 60_000,
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
ladderCfg() {
|
|
373
|
+
return { maxAttempts: this.cfg.maxAttempts, maxRearmEpisodes: this.cfg.maxRearmEpisodes, backoffBaseMs: this.cfg.backoffBaseMs };
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
function safeBool(fn) {
|
|
377
|
+
try {
|
|
378
|
+
return !!fn();
|
|
379
|
+
}
|
|
380
|
+
catch { /* @silent-fallback-ok: green-pr-automerge fail-safe — skip/refuse, never over-merge; safe-merge is the act-time authority */
|
|
381
|
+
return false;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
function safeNum(fn) {
|
|
385
|
+
try {
|
|
386
|
+
const n = fn();
|
|
387
|
+
return Number.isFinite(n) ? n : 0;
|
|
388
|
+
}
|
|
389
|
+
catch { /* @silent-fallback-ok: green-pr-automerge fail-safe — skip/refuse, never over-merge; safe-merge is the act-time authority */
|
|
390
|
+
return 0;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
//# sourceMappingURL=GreenPrAutoMerger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GreenPrAutoMerger.js","sourceRoot":"","sources":["../../src/monitoring/GreenPrAutoMerger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GuardLatchStore — pool-visible kill-switch + marker latches for guarded
|
|
3
|
+
* autonomous authorities (green-pr-automerge-enforcement R9/R7).
|
|
4
|
+
*
|
|
5
|
+
* Two independent gate families decide whether the GreenPrAutoMerger may act:
|
|
6
|
+
* - `rollback` — set by POST /green-pr-automerge/rollback, cleared by
|
|
7
|
+
* POST /green-pr-automerge/enable (PIN-gated re-arm)
|
|
8
|
+
* - `emergency-pause` — set by the MessageSentinel emergency stop, cleared by
|
|
9
|
+
* its own resume path
|
|
10
|
+
* BOTH must be open for a merge. A third family, `pool-armed`, is a posture
|
|
11
|
+
* MARKER (not a gate): its presence makes a lease holder whose local config is
|
|
12
|
+
* disabled grade `diverged-from-default` instead of healthy dark-default.
|
|
13
|
+
*
|
|
14
|
+
* Why a store and not a flag (round-2 finding): execution follows the lease, so
|
|
15
|
+
* a machine-LOCAL latch would silently resurrect the watcher on a lease move.
|
|
16
|
+
* Each transition is therefore written BOTH to a durable local file (authoritative
|
|
17
|
+
* for this machine's own writes, present regardless of the journal feature flag)
|
|
18
|
+
* AND to the replicated `guard-latch` coherence-journal kind, so a peer that
|
|
19
|
+
* takes the lease reads the same disabled state.
|
|
20
|
+
*
|
|
21
|
+
* ABSORBING disable (round-4): a `rollback` set always wins ordering conflicts
|
|
22
|
+
* regardless of epoch — "a stale-epoch standby's STOP can never be out-ordered
|
|
23
|
+
* by an earlier /enable from a higher epoch." This is achieved structurally, not
|
|
24
|
+
* by clock comparison: every `set` mints a FRESH globally-unique latchId
|
|
25
|
+
* (`<machineId>:<seq>`), and `/enable` clears ONLY the specific latchId(s) it
|
|
26
|
+
* names. A new STOP therefore introduces a latchId no prior enable could have
|
|
27
|
+
* named, so it can never be pre-cleared. Ordering within a single latchId is by
|
|
28
|
+
* (epoch, seq), NEVER wall-clock.
|
|
29
|
+
*
|
|
30
|
+
* arrive-disabled-on-unreadable (round-3): if the merged peer view cannot be
|
|
31
|
+
* read, `isMergeAllowed()` returns false — absence of evidence is not "armed."
|
|
32
|
+
*/
|
|
33
|
+
import type { CoherenceJournal, GuardLatchAction } from '../core/CoherenceJournal.js';
|
|
34
|
+
/** A single latch transition (own or peer), as merged for resolution. */
|
|
35
|
+
export interface GuardLatchEntry {
|
|
36
|
+
machine: string;
|
|
37
|
+
latchKind: string;
|
|
38
|
+
latchId: string;
|
|
39
|
+
action: GuardLatchAction;
|
|
40
|
+
epoch: number;
|
|
41
|
+
seq: number;
|
|
42
|
+
reason?: string;
|
|
43
|
+
ts?: string;
|
|
44
|
+
}
|
|
45
|
+
export type MergeGateReason = 'allowed' | 'rollback' | 'emergency-pause' | 'unreadable-peers';
|
|
46
|
+
export interface MergeGateVerdict {
|
|
47
|
+
allowed: boolean;
|
|
48
|
+
reason: MergeGateReason;
|
|
49
|
+
/** Active latch ids per blocking family (for the status surface / audit). */
|
|
50
|
+
activeLatchIds: Record<string, string[]>;
|
|
51
|
+
}
|
|
52
|
+
/** Latch family names. */
|
|
53
|
+
export declare const ROLLBACK_FAMILY = "rollback";
|
|
54
|
+
export declare const EMERGENCY_PAUSE_FAMILY = "emergency-pause";
|
|
55
|
+
export declare const POOL_ARMED_FAMILY = "pool-armed";
|
|
56
|
+
/** The fixed, pool-shared latchId for the single pool-armed marker. */
|
|
57
|
+
export declare const POOL_MARKER_ID = "pool";
|
|
58
|
+
export interface GuardLatchStoreConfig {
|
|
59
|
+
/** Absolute path to the agent's `.instar/` directory. */
|
|
60
|
+
stateDir: string;
|
|
61
|
+
/** Stable id for THIS machine. */
|
|
62
|
+
machineId: string;
|
|
63
|
+
/** Replication writer (optional — single-machine installs pass none). */
|
|
64
|
+
journal?: Pick<CoherenceJournal, 'emitGuardLatch'>;
|
|
65
|
+
/** Current lease epoch (monotonic; stamped on every transition). */
|
|
66
|
+
leaseEpoch: () => number;
|
|
67
|
+
/**
|
|
68
|
+
* Merged peer view: the latest guard-latch transition per (machine, kind, id)
|
|
69
|
+
* from replicated peer streams. Throwing signals UNREADABLE → arrive-disabled.
|
|
70
|
+
* Single-machine installs pass `() => []`.
|
|
71
|
+
*/
|
|
72
|
+
readPeerEntries?: () => GuardLatchEntry[];
|
|
73
|
+
now?: () => Date;
|
|
74
|
+
logger?: (msg: string) => void;
|
|
75
|
+
}
|
|
76
|
+
export declare class GuardLatchStore {
|
|
77
|
+
private readonly stateDir;
|
|
78
|
+
private readonly machineId;
|
|
79
|
+
private readonly journal?;
|
|
80
|
+
private readonly leaseEpoch;
|
|
81
|
+
private readonly readPeerEntries;
|
|
82
|
+
private readonly now;
|
|
83
|
+
private readonly logger?;
|
|
84
|
+
private local;
|
|
85
|
+
constructor(cfg: GuardLatchStoreConfig);
|
|
86
|
+
/**
|
|
87
|
+
* Set (mint) a latch in `latchKind`. Returns the fresh, globally-unique
|
|
88
|
+
* latchId. Use for the absorbing families (rollback, emergency-pause): every
|
|
89
|
+
* call introduces a NEW latchId that no prior clear could have named.
|
|
90
|
+
*/
|
|
91
|
+
set(latchKind: string, reason?: string): string;
|
|
92
|
+
/** Clear the SPECIFIC named latchId(s) in `latchKind` (the /enable semantics). */
|
|
93
|
+
clear(latchKind: string, latchIds: string[], reason?: string): void;
|
|
94
|
+
/** Set/clear a FIXED-id marker (pool-armed): last-(epoch,seq)-wins, not absorbing. */
|
|
95
|
+
setMarker(latchKind: string, latchId: string, on: boolean, reason?: string): void;
|
|
96
|
+
private record;
|
|
97
|
+
/**
|
|
98
|
+
* Resolve a family to the winning action per latchId across own + peers.
|
|
99
|
+
* Within one latchId, the highest (epoch, seq) transition wins. Distinct
|
|
100
|
+
* latchIds are independent — that is what makes `set` absorbing for the
|
|
101
|
+
* fresh-id families.
|
|
102
|
+
* @throws if the peer view is unreadable (caller maps to arrive-disabled).
|
|
103
|
+
*/
|
|
104
|
+
resolveFamily(latchKind: string): Map<string, GuardLatchEntry>;
|
|
105
|
+
/** Active (currently-set) latch ids in a family. */
|
|
106
|
+
activeLatchIds(latchKind: string): string[];
|
|
107
|
+
/**
|
|
108
|
+
* The dual-latch gate (R9): both rollback and emergency-pause must be open.
|
|
109
|
+
* An unreadable peer view arrives DISABLED (absence of evidence ≠ armed).
|
|
110
|
+
*/
|
|
111
|
+
isMergeAllowed(): MergeGateVerdict;
|
|
112
|
+
markPoolArmed(): void;
|
|
113
|
+
/** PIN-gated disarm: superseding entry, grades back to healthy dark-default. */
|
|
114
|
+
markPoolDisarmed(): void;
|
|
115
|
+
/**
|
|
116
|
+
* Is this pool deliberately armed (per the replicated marker)? Used by guard
|
|
117
|
+
* posture to grade a local-disabled + pool-armed machine `diverged-from-default`.
|
|
118
|
+
* Unreadable peers → false (we never INVENT a divergence alarm on a read error).
|
|
119
|
+
*/
|
|
120
|
+
isPoolArmed(): boolean;
|
|
121
|
+
/** Snapshot for the status route + posture grading. Never throws. */
|
|
122
|
+
snapshot(): {
|
|
123
|
+
rollback: string[];
|
|
124
|
+
emergencyPause: string[];
|
|
125
|
+
poolArmed: boolean;
|
|
126
|
+
mergeAllowed: boolean;
|
|
127
|
+
reason: MergeGateReason;
|
|
128
|
+
};
|
|
129
|
+
private filePath;
|
|
130
|
+
private load;
|
|
131
|
+
private persist;
|
|
132
|
+
private log;
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=GuardLatchStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GuardLatchStore.d.ts","sourceRoot":"","sources":["../../src/monitoring/GuardLatchStore.ts"],"names":[],"mappings":"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"}
|