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,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* greenPrAutomergeWiring — builds the real gh / fs / lease / latch dependencies
|
|
3
|
+
* for the GreenPrAutoMerger (green-pr-automerge-enforcement). Extracted from
|
|
4
|
+
* server.ts so the dependency construction is itself unit-testable (Testing
|
|
5
|
+
* Integrity: wiring-integrity tests prove deps are real, not nulls/no-ops).
|
|
6
|
+
*
|
|
7
|
+
* Repo-gated: the watcher only runs in the dev/maintainer environment where an
|
|
8
|
+
* analyzable instar git repo AND scripts/safe-merge.mjs are present. On a plain
|
|
9
|
+
* npm install both are absent → the server constructs nothing → routes 503.
|
|
10
|
+
*
|
|
11
|
+
* All gh goes through `execFile` (no shell). The list query is pinned oldest-first
|
|
12
|
+
* server-side. Identity (R4) is `gh api user`. Audit is one JSONL line per
|
|
13
|
+
* transition. State is per-machine (machine-local BY DESIGN).
|
|
14
|
+
*
|
|
15
|
+
* RULE 3.1 RATIONALE
|
|
16
|
+
* Criticality: high — these adapters surface the PR/identity/protected-paths
|
|
17
|
+
* state the merge watcher acts on.
|
|
18
|
+
* Frequency: per-tick (≤ once per ~10-minute interval).
|
|
19
|
+
* Stability: stable — every read uses `gh ... --json`/`gh api --jq`
|
|
20
|
+
* STRUCTURED output, never regex over human gh text.
|
|
21
|
+
* Fallback: a failing/unparseable list call THROWS (→ the watcher's
|
|
22
|
+
* tick-failed canary feeds the breaker); protectedPaths failure →
|
|
23
|
+
* `unverifiable:true` (→ skip, never merge); identity unresolved →
|
|
24
|
+
* skip. Every path fails toward NOT merging.
|
|
25
|
+
* Verdict: deterministic; safe-merge re-verifies at act time, so a misread
|
|
26
|
+
* here can only cause a refusal, never an unintended merge.
|
|
27
|
+
*/
|
|
28
|
+
import fs from 'node:fs';
|
|
29
|
+
import path from 'node:path';
|
|
30
|
+
import { execFile } from 'node:child_process';
|
|
31
|
+
import { GuardLatchStore } from './GuardLatchStore.js';
|
|
32
|
+
import { DefaultMergeRunner } from './MergeRunner.js';
|
|
33
|
+
import { freshState, } from './GreenPrAutoMerger.js';
|
|
34
|
+
/** Protected globs (round-4/6): a PR touching these never auto-merges. */
|
|
35
|
+
export const PROTECTED_PATH_PREFIXES = [
|
|
36
|
+
'.github/',
|
|
37
|
+
'scripts/safe-merge.mjs',
|
|
38
|
+
'src/monitoring/GreenPrAutoMerger.ts',
|
|
39
|
+
'src/monitoring/MergeRunner.ts',
|
|
40
|
+
'src/monitoring/GuardLatchStore.ts',
|
|
41
|
+
'src/monitoring/greenPrLogic.ts',
|
|
42
|
+
'src/monitoring/floorDriftCanary.ts',
|
|
43
|
+
'src/monitoring/greenPrAutomergeWiring.ts',
|
|
44
|
+
];
|
|
45
|
+
/** Gate scripts the floor contexts execute (extend, never shrink). */
|
|
46
|
+
export const PROTECTED_GATE_SCRIPTS = [
|
|
47
|
+
'scripts/safe-merge.mjs',
|
|
48
|
+
];
|
|
49
|
+
export function isAnalyzableGreenPrRepo(repoPath, safeMergePath) {
|
|
50
|
+
try {
|
|
51
|
+
return fs.existsSync(path.join(repoPath, '.git')) && fs.existsSync(safeMergePath);
|
|
52
|
+
}
|
|
53
|
+
catch { /* @silent-fallback-ok: green-pr-automerge fail-safe — skip/refuse, never over-merge; safe-merge is the act-time authority */
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/** Does any changed file touch a protected path? */
|
|
58
|
+
export function diffTouchesProtected(files) {
|
|
59
|
+
return files.some((f) => PROTECTED_PATH_PREFIXES.some((p) => f === p || f.startsWith(p)));
|
|
60
|
+
}
|
|
61
|
+
function gh(args) {
|
|
62
|
+
return new Promise((resolve) => {
|
|
63
|
+
execFile('gh', args, { maxBuffer: 16 * 1024 * 1024 }, (err, stdout, stderr) => {
|
|
64
|
+
resolve({ stdout: stdout ?? '', stderr: stderr ?? '', code: err ? err.code ?? 1 : 0 });
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
/** Build the GuardLatchStore for this install. */
|
|
69
|
+
export function buildGuardLatchStore(opts) {
|
|
70
|
+
return new GuardLatchStore({
|
|
71
|
+
stateDir: opts.stateDir,
|
|
72
|
+
machineId: opts.machineId,
|
|
73
|
+
journal: opts.journal,
|
|
74
|
+
leaseEpoch: opts.leaseEpoch,
|
|
75
|
+
readPeerEntries: opts.readPeerLatches,
|
|
76
|
+
now: opts.now ? () => new Date(opts.now()) : undefined,
|
|
77
|
+
logger: opts.logger,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
/** Build the full GreenPrAutoMerger deps (real gh adapters). */
|
|
81
|
+
export function buildGreenPrDeps(opts, latches) {
|
|
82
|
+
const exec = opts.ghExec ?? gh;
|
|
83
|
+
const now = opts.now ?? (() => Date.now());
|
|
84
|
+
const runner = new DefaultMergeRunner({
|
|
85
|
+
stateDir: opts.stateDir,
|
|
86
|
+
repo: opts.repo,
|
|
87
|
+
safeMergePath: opts.safeMergePath,
|
|
88
|
+
mergeTimeoutMs: opts.mergeTimeoutMs,
|
|
89
|
+
mergeKillGraceMs: opts.mergeKillGraceMs,
|
|
90
|
+
expectedContractVersion: 2,
|
|
91
|
+
}, {
|
|
92
|
+
confirmMerged: async (pr, repo) => {
|
|
93
|
+
const r = await exec(['pr', 'view', String(pr), '--repo', repo, '--json', 'state,mergedAt']);
|
|
94
|
+
if (r.code !== 0)
|
|
95
|
+
return false;
|
|
96
|
+
try {
|
|
97
|
+
const j = JSON.parse(r.stdout);
|
|
98
|
+
return j.state === 'MERGED' || !!j.mergedAt;
|
|
99
|
+
}
|
|
100
|
+
catch { /* @silent-fallback-ok: green-pr-automerge fail-safe — skip/refuse, never over-merge; safe-merge is the act-time authority */
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
prState: async (pr, repo) => {
|
|
105
|
+
const r = await exec(['pr', 'view', String(pr), '--repo', repo, '--json', 'state']);
|
|
106
|
+
if (r.code !== 0)
|
|
107
|
+
return 'UNKNOWN';
|
|
108
|
+
try {
|
|
109
|
+
return String(JSON.parse(r.stdout).state ?? 'UNKNOWN');
|
|
110
|
+
}
|
|
111
|
+
catch { /* @silent-fallback-ok: green-pr-automerge fail-safe — skip/refuse, never over-merge; safe-merge is the act-time authority */
|
|
112
|
+
return 'UNKNOWN';
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
logger: opts.logger,
|
|
116
|
+
});
|
|
117
|
+
const audit = (event) => {
|
|
118
|
+
try {
|
|
119
|
+
const dir = path.dirname(opts.auditPath);
|
|
120
|
+
if (!fs.existsSync(dir))
|
|
121
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
122
|
+
fs.appendFileSync(opts.auditPath, JSON.stringify({ ts: new Date(now()).toISOString(), ...event }) + '\n', { mode: 0o600 });
|
|
123
|
+
}
|
|
124
|
+
catch { /* @silent-fallback-ok: green-pr-automerge fail-safe — skip/refuse, never over-merge; safe-merge is the act-time authority */ /* audit failure is non-fatal */ }
|
|
125
|
+
};
|
|
126
|
+
const statePath = path.join(opts.stateDir, 'state', 'green-pr-automerge.json');
|
|
127
|
+
const loadState = () => {
|
|
128
|
+
try {
|
|
129
|
+
const raw = fs.readFileSync(statePath, 'utf-8');
|
|
130
|
+
const obj = JSON.parse(raw);
|
|
131
|
+
return { ...freshState(), ...obj };
|
|
132
|
+
}
|
|
133
|
+
catch { /* @silent-fallback-ok: green-pr-automerge fail-safe — skip/refuse, never over-merge; safe-merge is the act-time authority */
|
|
134
|
+
return freshState();
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
const saveState = (state) => {
|
|
138
|
+
try {
|
|
139
|
+
const dir = path.dirname(statePath);
|
|
140
|
+
if (!fs.existsSync(dir))
|
|
141
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
142
|
+
const tmp = statePath + '.tmp';
|
|
143
|
+
fs.writeFileSync(tmp, JSON.stringify(state), { mode: 0o600 });
|
|
144
|
+
fs.renameSync(tmp, statePath);
|
|
145
|
+
}
|
|
146
|
+
catch { /* @silent-fallback-ok: green-pr-automerge fail-safe — skip/refuse, never over-merge; safe-merge is the act-time authority */ /* best-effort */ }
|
|
147
|
+
};
|
|
148
|
+
return {
|
|
149
|
+
holdsLease: opts.holdsLease,
|
|
150
|
+
leaseEpoch: opts.leaseEpoch,
|
|
151
|
+
listOpenPrs: async () => {
|
|
152
|
+
const r = await exec([
|
|
153
|
+
'pr', 'list', '--author', '@me', '--state', 'open', '--base', 'main',
|
|
154
|
+
'--limit', '100', '--json', 'number,title,labels,isDraft,headRefName,headRefOid,mergeable,statusCheckRollup',
|
|
155
|
+
'--search', 'sort:created-asc',
|
|
156
|
+
]);
|
|
157
|
+
if (r.code !== 0)
|
|
158
|
+
throw new Error(`gh pr list failed: ${r.stderr.slice(0, 200)}`);
|
|
159
|
+
const rows = JSON.parse(r.stdout);
|
|
160
|
+
return rows.map(mapPr);
|
|
161
|
+
},
|
|
162
|
+
protectedPaths: async (pr) => {
|
|
163
|
+
// Enumerate changed files to exhaustion via the API (paginated).
|
|
164
|
+
const r = await exec(['api', `repos/${opts.repo}/pulls/${pr.number}/files`, '--paginate', '--jq', '.[].filename']);
|
|
165
|
+
if (r.code !== 0)
|
|
166
|
+
return { touches: false, unverifiable: true };
|
|
167
|
+
const files = r.stdout.split('\n').map((s) => s.trim()).filter(Boolean);
|
|
168
|
+
return { touches: diffTouchesProtected(files), unverifiable: false };
|
|
169
|
+
},
|
|
170
|
+
refetchPr: async (pr) => {
|
|
171
|
+
const r = await exec(['pr', 'view', String(pr), '--repo', opts.repo, '--json', 'title,labels,isDraft,headRefOid,state']);
|
|
172
|
+
if (r.code !== 0)
|
|
173
|
+
return null;
|
|
174
|
+
try {
|
|
175
|
+
const j = JSON.parse(r.stdout);
|
|
176
|
+
return {
|
|
177
|
+
title: String(j.title ?? ''),
|
|
178
|
+
labels: Array.isArray(j.labels) ? j.labels.map((l) => l.name ?? '') : [],
|
|
179
|
+
isDraft: !!j.isDraft,
|
|
180
|
+
headRefOid: String(j.headRefOid ?? ''),
|
|
181
|
+
state: String(j.state ?? 'UNKNOWN'),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
catch { /* @silent-fallback-ok: green-pr-automerge fail-safe — skip/refuse, never over-merge; safe-merge is the act-time authority */
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
resolveGhLogin: async () => {
|
|
189
|
+
const r = await exec(['api', 'user', '--jq', '.login']);
|
|
190
|
+
if (r.code !== 0)
|
|
191
|
+
return null;
|
|
192
|
+
const login = r.stdout.trim();
|
|
193
|
+
return login || null;
|
|
194
|
+
},
|
|
195
|
+
holdEligible: async (pr) => {
|
|
196
|
+
const r = await exec(['pr', 'view', String(pr), '--repo', opts.repo, '--json', 'state,headRefName']);
|
|
197
|
+
if (r.code !== 0)
|
|
198
|
+
return { ok: false, status: 404, detail: 'PR not found' };
|
|
199
|
+
try {
|
|
200
|
+
const j = JSON.parse(r.stdout);
|
|
201
|
+
if (j.state !== 'OPEN')
|
|
202
|
+
return { ok: false, status: 409, detail: `PR is ${j.state}` };
|
|
203
|
+
const ns = opts.agentNamespace.endsWith('/') ? opts.agentNamespace : `${opts.agentNamespace}/`;
|
|
204
|
+
if (!String(j.headRefName ?? '').startsWith(ns))
|
|
205
|
+
return { ok: false, status: 403, detail: 'PR is not in this agent\'s namespace' };
|
|
206
|
+
return { ok: true };
|
|
207
|
+
}
|
|
208
|
+
catch { /* @silent-fallback-ok: green-pr-automerge fail-safe — skip/refuse, never over-merge; safe-merge is the act-time authority */
|
|
209
|
+
return { ok: false, status: 502, detail: 'unparseable gh response' };
|
|
210
|
+
}
|
|
211
|
+
},
|
|
212
|
+
applyHoldMarker: async (pr, reason) => {
|
|
213
|
+
// Read the current title, prefix [HOLD: …] (idempotent if already held).
|
|
214
|
+
const view = await exec(['pr', 'view', String(pr), '--repo', opts.repo, '--json', 'title']);
|
|
215
|
+
if (view.code !== 0)
|
|
216
|
+
return false;
|
|
217
|
+
let title = '';
|
|
218
|
+
try {
|
|
219
|
+
title = String(JSON.parse(view.stdout).title ?? '');
|
|
220
|
+
}
|
|
221
|
+
catch { /* @silent-fallback-ok: green-pr-automerge fail-safe — skip/refuse, never over-merge; safe-merge is the act-time authority */
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
if (title.trim().toLowerCase().startsWith('[hold'))
|
|
225
|
+
return true; // already held
|
|
226
|
+
const safeReason = reason.replace(/[\]\n\r]/g, ' ').slice(0, 80);
|
|
227
|
+
const newTitle = `[HOLD: ${safeReason || 'held'}] ${title}`.slice(0, 256);
|
|
228
|
+
const edit = await exec(['pr', 'edit', String(pr), '--repo', opts.repo, '--title', newTitle]);
|
|
229
|
+
return edit.code === 0;
|
|
230
|
+
},
|
|
231
|
+
runner,
|
|
232
|
+
latches,
|
|
233
|
+
postAttentionAggregate: opts.postAttentionAggregate,
|
|
234
|
+
audit,
|
|
235
|
+
loadState,
|
|
236
|
+
saveState,
|
|
237
|
+
now,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
function mapPr(row) {
|
|
241
|
+
const labels = Array.isArray(row.labels) ? row.labels.map((l) => l.name ?? '') : [];
|
|
242
|
+
const rollup = row.statusCheckRollup;
|
|
243
|
+
return {
|
|
244
|
+
number: Number(row.number),
|
|
245
|
+
title: String(row.title ?? ''),
|
|
246
|
+
labels,
|
|
247
|
+
isDraft: !!row.isDraft,
|
|
248
|
+
headRefName: String(row.headRefName ?? ''),
|
|
249
|
+
headRefOid: String(row.headRefOid ?? ''),
|
|
250
|
+
mergeable: String(row.mergeable ?? 'UNKNOWN'),
|
|
251
|
+
statusRollup: deriveRollup(rollup),
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
/** Derive a single SUCCESS|PENDING|FAILURE from the statusCheckRollup array. */
|
|
255
|
+
export function deriveRollup(rollup) {
|
|
256
|
+
if (!Array.isArray(rollup))
|
|
257
|
+
return typeof rollup === 'string' ? rollup : null;
|
|
258
|
+
let sawPending = false;
|
|
259
|
+
for (const c of rollup) {
|
|
260
|
+
const state = String(c.state ?? c.conclusion ?? '').toUpperCase();
|
|
261
|
+
const status = String(c.status ?? '').toUpperCase();
|
|
262
|
+
if (status && status !== 'COMPLETED') {
|
|
263
|
+
sawPending = true;
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
if (state === 'FAILURE' || state === 'ERROR' || state === 'CANCELLED' || state === 'TIMED_OUT')
|
|
267
|
+
return 'FAILURE';
|
|
268
|
+
if (state === 'PENDING' || state === 'EXPECTED' || state === 'IN_PROGRESS' || state === 'QUEUED')
|
|
269
|
+
sawPending = true;
|
|
270
|
+
}
|
|
271
|
+
return sawPending ? 'PENDING' : 'SUCCESS';
|
|
272
|
+
}
|
|
273
|
+
//# sourceMappingURL=greenPrAutomergeWiring.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"greenPrAutomergeWiring.js","sourceRoot":"","sources":["../../src/monitoring/greenPrAutomergeWiring.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* greenPrLogic — PURE decision helpers for the GreenPrAutoMerger
|
|
3
|
+
* (green-pr-automerge-enforcement). No I/O, no clock of their own (time is
|
|
4
|
+
* passed in), so every decision boundary is unit-testable on both sides.
|
|
5
|
+
*
|
|
6
|
+
* The orchestrating class (GreenPrAutoMerger.ts) wires these to real gh /
|
|
7
|
+
* safe-merge / lease / latch / audit I/O.
|
|
8
|
+
*/
|
|
9
|
+
/** One PR as returned by the `gh pr list` GraphQL projection. */
|
|
10
|
+
export interface PrSummary {
|
|
11
|
+
number: number;
|
|
12
|
+
title: string;
|
|
13
|
+
labels: string[];
|
|
14
|
+
isDraft: boolean;
|
|
15
|
+
headRefName: string;
|
|
16
|
+
headRefOid: string;
|
|
17
|
+
/** GraphQL mergeable enum: MERGEABLE | CONFLICTING | UNKNOWN. */
|
|
18
|
+
mergeable: string;
|
|
19
|
+
/** Worst-case rollup of the head's checks: SUCCESS | PENDING | FAILURE | null. */
|
|
20
|
+
statusRollup: string | null;
|
|
21
|
+
}
|
|
22
|
+
export type HoldReason = 'draft' | 'hold-title' | 'hold-label' | null;
|
|
23
|
+
/** A PR is held (excluded) by a deliberate marker. Returns WHY, or null. */
|
|
24
|
+
export declare function holdReasonOf(pr: Pick<PrSummary, 'isDraft' | 'title' | 'labels'>): HoldReason;
|
|
25
|
+
export type CandidateSkip = 'not-agent-namespace' | 'held' | 'not-mergeable' | 'not-settled-green' | 'protected-paths' | 'protected-paths-unverifiable';
|
|
26
|
+
export interface CandidateVerdict {
|
|
27
|
+
eligible: boolean;
|
|
28
|
+
skip?: CandidateSkip;
|
|
29
|
+
hold?: HoldReason;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* First-pass candidacy from the list projection alone (cheap fields). Protected
|
|
33
|
+
* paths require a separate diff enumeration (handled by the caller and folded in
|
|
34
|
+
* via `protectedPaths`), so this returns `eligible:true` only when the cheap
|
|
35
|
+
* gates ALL pass; the caller still runs the protected-paths check before acting.
|
|
36
|
+
*/
|
|
37
|
+
export declare function classifyCandidate(pr: PrSummary, agentNamespace: string): CandidateVerdict;
|
|
38
|
+
/** A head branch `<agentName>/...` filter (a FILTER, not provenance — Decision 8). */
|
|
39
|
+
export declare function headInNamespace(headRefName: string, agentNamespace: string): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Oldest-first selection over the FULL fetched set (the list query is pinned
|
|
42
|
+
* oldest-first server-side; this is the deterministic tiebreak by PR number).
|
|
43
|
+
*/
|
|
44
|
+
export declare function selectOldest(candidates: PrSummary[]): PrSummary | null;
|
|
45
|
+
export interface HoldMemoryEntry {
|
|
46
|
+
/** Consecutive ticks (same lease holder) the marker has been ABSENT. */
|
|
47
|
+
absentTicks: number;
|
|
48
|
+
/** ms timestamp the PR was first observed held this episode. */
|
|
49
|
+
firstHeldAt: number;
|
|
50
|
+
/** ms timestamp of the most recent absence observation. */
|
|
51
|
+
lastAbsentAt?: number;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Update hold memory for a PR and decide whether it is eligible to RESUME.
|
|
55
|
+
* A PR observed held resumes only after the marker is absent for
|
|
56
|
+
* `holdReleaseTicks` consecutive ticks AND `>= tickIntervalMs` elapsed
|
|
57
|
+
* (tick-AND-time so a manual double-tick cannot collapse the window).
|
|
58
|
+
*/
|
|
59
|
+
export declare function debounceHoldRelease(mem: HoldMemoryEntry | undefined, currentlyHeld: boolean, nowMs: number, holdReleaseTicks: number, tickIntervalMs: number): {
|
|
60
|
+
mem: HoldMemoryEntry | undefined;
|
|
61
|
+
resumeEligible: boolean;
|
|
62
|
+
transition: 'still-held' | 'released' | 'observing-release' | 'not-held';
|
|
63
|
+
};
|
|
64
|
+
export interface Episode {
|
|
65
|
+
pr: number;
|
|
66
|
+
headRefOid: string;
|
|
67
|
+
attempts: number;
|
|
68
|
+
rearmEpisodes: number;
|
|
69
|
+
state: 'active' | 'gave-up';
|
|
70
|
+
lastAttemptAt?: number;
|
|
71
|
+
nextEligibleAt?: number;
|
|
72
|
+
lastOutcome?: string;
|
|
73
|
+
}
|
|
74
|
+
export interface LadderConfig {
|
|
75
|
+
maxAttempts: number;
|
|
76
|
+
maxRearmEpisodes: number;
|
|
77
|
+
backoffBaseMs: number;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Fold a merge OUTCOME into the PR's episode. Returns the updated episode and
|
|
81
|
+
* whether this counts as a breaker-feeding failure. `already-merged` /
|
|
82
|
+
* `closed` are success-noops (never ladder failures). A `refused`/`error`
|
|
83
|
+
* advances the attempt ladder with backoff; at `maxAttempts` the episode is
|
|
84
|
+
* `gave-up` (re-armable by a NEW head sha up to `maxRearmEpisodes`).
|
|
85
|
+
*/
|
|
86
|
+
export declare function applyOutcome(ep: Episode, outcome: string, nowMs: number, cfg: LadderConfig): {
|
|
87
|
+
ep: Episode;
|
|
88
|
+
terminal: boolean;
|
|
89
|
+
feedsBreaker: boolean;
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Re-arm a gave-up episode when a NEW head sha appears (the author pushed a
|
|
93
|
+
* fix). Bounded by `maxRearmEpisodes` — beyond that, manual action is required.
|
|
94
|
+
*/
|
|
95
|
+
export declare function maybeRearm(ep: Episode, newHeadOid: string, cfg: LadderConfig): Episode | null;
|
|
96
|
+
/** Is the episode currently eligible to attempt (active + backoff elapsed)? */
|
|
97
|
+
export declare function episodeEligible(ep: Episode | undefined, nowMs: number): boolean;
|
|
98
|
+
export interface BreakerState {
|
|
99
|
+
open: boolean;
|
|
100
|
+
openedAt?: number;
|
|
101
|
+
consecutiveBusySkips: number;
|
|
102
|
+
consecutiveDeadlineKills: number;
|
|
103
|
+
consecutiveTickFailures: number;
|
|
104
|
+
}
|
|
105
|
+
export interface BreakerConfig {
|
|
106
|
+
busySkipBreakerThreshold: number;
|
|
107
|
+
deadlineKillBreakerThreshold: number;
|
|
108
|
+
breakerThreshold: number;
|
|
109
|
+
breakerCooldownMs: number;
|
|
110
|
+
}
|
|
111
|
+
export declare function freshBreaker(): BreakerState;
|
|
112
|
+
/** Is the breaker open AND still within its cooldown? */
|
|
113
|
+
export declare function breakerBlocking(b: BreakerState, nowMs: number, cooldownMs: number): boolean;
|
|
114
|
+
/** Fold a breaker SIGNAL; returns the updated state (may open the breaker). */
|
|
115
|
+
export declare function feedBreaker(b: BreakerState, signal: 'busy-skip' | 'deadline-kill' | 'tick-failed' | 'reset', nowMs: number, cfg: BreakerConfig): BreakerState;
|
|
116
|
+
/** Boot-time invariant (B24): busy-skip budget must outlast a single merge. */
|
|
117
|
+
export declare function validateTimeoutInvariant(busySkipBreakerThreshold: number, tickIntervalMs: number, mergeTimeoutMs: number, mergeKillGraceMs: number): {
|
|
118
|
+
ok: boolean;
|
|
119
|
+
reason?: string;
|
|
120
|
+
};
|
|
121
|
+
//# sourceMappingURL=greenPrLogic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"greenPrLogic.d.ts","sourceRoot":"","sources":["../../src/monitoring/greenPrLogic.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* greenPrLogic — PURE decision helpers for the GreenPrAutoMerger
|
|
3
|
+
* (green-pr-automerge-enforcement). No I/O, no clock of their own (time is
|
|
4
|
+
* passed in), so every decision boundary is unit-testable on both sides.
|
|
5
|
+
*
|
|
6
|
+
* The orchestrating class (GreenPrAutoMerger.ts) wires these to real gh /
|
|
7
|
+
* safe-merge / lease / latch / audit I/O.
|
|
8
|
+
*/
|
|
9
|
+
/** A PR is held (excluded) by a deliberate marker. Returns WHY, or null. */
|
|
10
|
+
export function holdReasonOf(pr) {
|
|
11
|
+
if (pr.isDraft)
|
|
12
|
+
return 'draft';
|
|
13
|
+
// Title trimmed before the prefix match (round-5: a hand-edited " [HOLD]" with
|
|
14
|
+
// leading whitespace must not silently fail).
|
|
15
|
+
const title = (pr.title ?? '').trim().toLowerCase();
|
|
16
|
+
if (title.startsWith('[hold'))
|
|
17
|
+
return 'hold-title';
|
|
18
|
+
const labels = (pr.labels ?? []).map((l) => l.toLowerCase());
|
|
19
|
+
if (labels.includes('hold') || labels.includes('do-not-merge'))
|
|
20
|
+
return 'hold-label';
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* First-pass candidacy from the list projection alone (cheap fields). Protected
|
|
25
|
+
* paths require a separate diff enumeration (handled by the caller and folded in
|
|
26
|
+
* via `protectedPaths`), so this returns `eligible:true` only when the cheap
|
|
27
|
+
* gates ALL pass; the caller still runs the protected-paths check before acting.
|
|
28
|
+
*/
|
|
29
|
+
export function classifyCandidate(pr, agentNamespace) {
|
|
30
|
+
if (!headInNamespace(pr.headRefName, agentNamespace)) {
|
|
31
|
+
return { eligible: false, skip: 'not-agent-namespace' };
|
|
32
|
+
}
|
|
33
|
+
const hold = holdReasonOf(pr);
|
|
34
|
+
if (hold)
|
|
35
|
+
return { eligible: false, skip: 'held', hold };
|
|
36
|
+
if (pr.mergeable !== 'MERGEABLE')
|
|
37
|
+
return { eligible: false, skip: 'not-mergeable' };
|
|
38
|
+
// "Settled green" — the watcher NEVER invokes safe-merge into a pending wait
|
|
39
|
+
// (keeps each attempt window seconds-long; timeout inversion impossible).
|
|
40
|
+
if (pr.statusRollup !== 'SUCCESS')
|
|
41
|
+
return { eligible: false, skip: 'not-settled-green' };
|
|
42
|
+
return { eligible: true };
|
|
43
|
+
}
|
|
44
|
+
/** A head branch `<agentName>/...` filter (a FILTER, not provenance — Decision 8). */
|
|
45
|
+
export function headInNamespace(headRefName, agentNamespace) {
|
|
46
|
+
if (!agentNamespace)
|
|
47
|
+
return false;
|
|
48
|
+
const prefix = agentNamespace.endsWith('/') ? agentNamespace : `${agentNamespace}/`;
|
|
49
|
+
return headRefName.startsWith(prefix);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Oldest-first selection over the FULL fetched set (the list query is pinned
|
|
53
|
+
* oldest-first server-side; this is the deterministic tiebreak by PR number).
|
|
54
|
+
*/
|
|
55
|
+
export function selectOldest(candidates) {
|
|
56
|
+
if (candidates.length === 0)
|
|
57
|
+
return null;
|
|
58
|
+
return [...candidates].sort((a, b) => a.number - b.number)[0];
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Update hold memory for a PR and decide whether it is eligible to RESUME.
|
|
62
|
+
* A PR observed held resumes only after the marker is absent for
|
|
63
|
+
* `holdReleaseTicks` consecutive ticks AND `>= tickIntervalMs` elapsed
|
|
64
|
+
* (tick-AND-time so a manual double-tick cannot collapse the window).
|
|
65
|
+
*/
|
|
66
|
+
export function debounceHoldRelease(mem, currentlyHeld, nowMs, holdReleaseTicks, tickIntervalMs) {
|
|
67
|
+
if (currentlyHeld) {
|
|
68
|
+
return {
|
|
69
|
+
mem: { absentTicks: 0, firstHeldAt: mem?.firstHeldAt ?? nowMs },
|
|
70
|
+
resumeEligible: false,
|
|
71
|
+
transition: 'still-held',
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
// Not held right now.
|
|
75
|
+
if (!mem) {
|
|
76
|
+
// Never seen held → no debounce needed.
|
|
77
|
+
return { mem: undefined, resumeEligible: true, transition: 'not-held' };
|
|
78
|
+
}
|
|
79
|
+
const absentTicks = mem.absentTicks + 1;
|
|
80
|
+
const elapsedOk = (nowMs - (mem.firstHeldAt ?? nowMs)) >= tickIntervalMs;
|
|
81
|
+
if (absentTicks >= holdReleaseTicks && elapsedOk) {
|
|
82
|
+
return { mem: undefined, resumeEligible: true, transition: 'released' };
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
mem: { absentTicks, firstHeldAt: mem.firstHeldAt, lastAbsentAt: nowMs },
|
|
86
|
+
resumeEligible: false,
|
|
87
|
+
transition: 'observing-release',
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Fold a merge OUTCOME into the PR's episode. Returns the updated episode and
|
|
92
|
+
* whether this counts as a breaker-feeding failure. `already-merged` /
|
|
93
|
+
* `closed` are success-noops (never ladder failures). A `refused`/`error`
|
|
94
|
+
* advances the attempt ladder with backoff; at `maxAttempts` the episode is
|
|
95
|
+
* `gave-up` (re-armable by a NEW head sha up to `maxRearmEpisodes`).
|
|
96
|
+
*/
|
|
97
|
+
export function applyOutcome(ep, outcome, nowMs, cfg) {
|
|
98
|
+
const next = { ...ep };
|
|
99
|
+
if (outcome === 'merged') {
|
|
100
|
+
next.state = 'gave-up'; // episode complete; reaped by the caller on confirm
|
|
101
|
+
next.lastOutcome = 'merged';
|
|
102
|
+
return { ep: next, terminal: true, feedsBreaker: false };
|
|
103
|
+
}
|
|
104
|
+
if (outcome === 'already-merged' || outcome === 'closed' || outcome === 'merged-by-other' || outcome === 'closed-by-other') {
|
|
105
|
+
next.lastOutcome = outcome;
|
|
106
|
+
next.state = 'gave-up';
|
|
107
|
+
return { ep: next, terminal: true, feedsBreaker: false };
|
|
108
|
+
}
|
|
109
|
+
// A real refusal/error: advance the ladder.
|
|
110
|
+
next.attempts = ep.attempts + 1;
|
|
111
|
+
next.lastAttemptAt = nowMs;
|
|
112
|
+
next.lastOutcome = outcome;
|
|
113
|
+
const backoff = cfg.backoffBaseMs * Math.pow(2, Math.min(next.attempts - 1, 6));
|
|
114
|
+
next.nextEligibleAt = nowMs + backoff;
|
|
115
|
+
if (next.attempts >= cfg.maxAttempts) {
|
|
116
|
+
next.state = 'gave-up';
|
|
117
|
+
}
|
|
118
|
+
return { ep: next, terminal: next.state === 'gave-up', feedsBreaker: true };
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Re-arm a gave-up episode when a NEW head sha appears (the author pushed a
|
|
122
|
+
* fix). Bounded by `maxRearmEpisodes` — beyond that, manual action is required.
|
|
123
|
+
*/
|
|
124
|
+
export function maybeRearm(ep, newHeadOid, cfg) {
|
|
125
|
+
if (ep.state !== 'gave-up')
|
|
126
|
+
return ep;
|
|
127
|
+
if (ep.headRefOid === newHeadOid)
|
|
128
|
+
return ep; // same head, still given up
|
|
129
|
+
if (ep.rearmEpisodes >= cfg.maxRearmEpisodes)
|
|
130
|
+
return null; // exhausted
|
|
131
|
+
return {
|
|
132
|
+
pr: ep.pr,
|
|
133
|
+
headRefOid: newHeadOid,
|
|
134
|
+
attempts: 0,
|
|
135
|
+
rearmEpisodes: ep.rearmEpisodes + 1,
|
|
136
|
+
state: 'active',
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/** Is the episode currently eligible to attempt (active + backoff elapsed)? */
|
|
140
|
+
export function episodeEligible(ep, nowMs) {
|
|
141
|
+
if (!ep)
|
|
142
|
+
return true; // first attempt
|
|
143
|
+
if (ep.state === 'gave-up')
|
|
144
|
+
return false;
|
|
145
|
+
if (ep.nextEligibleAt && nowMs < ep.nextEligibleAt)
|
|
146
|
+
return false;
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
export function freshBreaker() {
|
|
150
|
+
return { open: false, consecutiveBusySkips: 0, consecutiveDeadlineKills: 0, consecutiveTickFailures: 0 };
|
|
151
|
+
}
|
|
152
|
+
/** Is the breaker open AND still within its cooldown? */
|
|
153
|
+
export function breakerBlocking(b, nowMs, cooldownMs) {
|
|
154
|
+
if (!b.open)
|
|
155
|
+
return false;
|
|
156
|
+
if (b.openedAt && nowMs - b.openedAt >= cooldownMs)
|
|
157
|
+
return false; // cooled down
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
/** Fold a breaker SIGNAL; returns the updated state (may open the breaker). */
|
|
161
|
+
export function feedBreaker(b, signal, nowMs, cfg) {
|
|
162
|
+
const next = { ...b };
|
|
163
|
+
if (signal === 'reset') {
|
|
164
|
+
return { ...next, consecutiveBusySkips: 0, consecutiveDeadlineKills: 0, consecutiveTickFailures: 0, open: false, openedAt: undefined };
|
|
165
|
+
}
|
|
166
|
+
if (signal === 'busy-skip') {
|
|
167
|
+
next.consecutiveBusySkips += 1;
|
|
168
|
+
if (next.consecutiveBusySkips >= cfg.busySkipBreakerThreshold) {
|
|
169
|
+
next.open = true;
|
|
170
|
+
next.openedAt = nowMs;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
else if (signal === 'deadline-kill') {
|
|
174
|
+
next.consecutiveDeadlineKills += 1;
|
|
175
|
+
if (next.consecutiveDeadlineKills >= cfg.deadlineKillBreakerThreshold) {
|
|
176
|
+
next.open = true;
|
|
177
|
+
next.openedAt = nowMs;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
else if (signal === 'tick-failed') {
|
|
181
|
+
next.consecutiveTickFailures += 1;
|
|
182
|
+
if (next.consecutiveTickFailures >= cfg.breakerThreshold) {
|
|
183
|
+
next.open = true;
|
|
184
|
+
next.openedAt = nowMs;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return next;
|
|
188
|
+
}
|
|
189
|
+
/** Boot-time invariant (B24): busy-skip budget must outlast a single merge. */
|
|
190
|
+
export function validateTimeoutInvariant(busySkipBreakerThreshold, tickIntervalMs, mergeTimeoutMs, mergeKillGraceMs) {
|
|
191
|
+
const budget = busySkipBreakerThreshold * tickIntervalMs;
|
|
192
|
+
const need = mergeTimeoutMs + mergeKillGraceMs;
|
|
193
|
+
if (budget <= need) {
|
|
194
|
+
return { ok: false, reason: `busySkipBreakerThreshold(${busySkipBreakerThreshold}) × tickIntervalMs(${tickIntervalMs}) = ${budget} must exceed mergeTimeoutMs(${mergeTimeoutMs}) + mergeKillGraceMs(${mergeKillGraceMs}) = ${need}` };
|
|
195
|
+
}
|
|
196
|
+
return { ok: true };
|
|
197
|
+
}
|
|
198
|
+
//# sourceMappingURL=greenPrLogic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"greenPrLogic.js","sourceRoot":"","sources":["../../src/monitoring/greenPrLogic.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"guardManifest.d.ts","sourceRoot":"","sources":["../../src/monitoring/guardManifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,cAAc,CAAC;AAClD,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,UAAU,CAAC;AAEjD,MAAM,WAAW,kBAAkB;IACjC,6FAA6F;IAC7F,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,SAAS,CAAC;IAChB,6EAA6E;IAC7E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;qFAEiF;IACjF,cAAc,EAAE,OAAO,CAAC;IACxB,6DAA6D;IAC7D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,8EAA8E;IAC9E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;wEACoE;IACpE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,YAAY,CAAC;IACtB;;;;2DAIuD;IACvD,aAAa,EAAE,OAAO,CAAC;IACvB,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,cAAc,EAAE,SAAS,kBAAkB,
|
|
1
|
+
{"version":3,"file":"guardManifest.d.ts","sourceRoot":"","sources":["../../src/monitoring/guardManifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,cAAc,CAAC;AAClD,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,UAAU,CAAC;AAEjD,MAAM,WAAW,kBAAkB;IACjC,6FAA6F;IAC7F,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,SAAS,CAAC;IAChB,6EAA6E;IAC7E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;qFAEiF;IACjF,cAAc,EAAE,OAAO,CAAC;IACxB,6DAA6D;IAC7D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,8EAA8E;IAC9E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;wEACoE;IACpE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,YAAY,CAAC;IACtB;;;;2DAIuD;IACvD,aAAa,EAAE,OAAO,CAAC;IACvB,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,cAAc,EAAE,SAAS,kBAAkB,EA0c9C,CAAC;AAEX;;;;;;GAMG;AACH,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,eAAO,MAAM,WAAW,EAAE,SAAS,cAAc,EA8EvC,CAAC;AAEX,qFAAqF;AACrF,wBAAgB,aAAa,IAAI,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAI/D;AAED,wBAAgB,kBAAkB,IAAI,GAAG,CAAC,MAAM,CAAC,CAIhD;AAED,wBAAgB,mBAAmB,IAAI,GAAG,CAAC,MAAM,CAAC,CAEjD"}
|