@defrex/autobuild 0.12.0 → 0.14.0
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 +1 -1
- package/SPEC.md +138 -16
- package/bin/ab-dispatch-kernel.ts +4 -6
- package/docs/architecture.md +155 -73
- package/docs/configuration.md +41 -9
- package/docs/hosted-service.md +31 -5
- package/docs/operator-api.md +8 -1
- package/docs/remote-store-protocol.md +94 -2
- package/docs/remote-ticket-protocol.md +151 -9
- package/docs/setup.md +33 -7
- package/docs/ticket-migration.md +64 -0
- package/package.json +4 -3
- package/packages/core/src/cli/attention.ts +18 -164
- package/packages/core/src/cli/build-control.ts +23 -16
- package/packages/core/src/cli/bulk-control.ts +3 -2
- package/packages/core/src/cli/connect.ts +141 -38
- package/packages/core/src/cli/context.ts +23 -79
- package/packages/core/src/cli/dashboard/model.ts +36 -22
- package/packages/core/src/cli/dashboard/poll.ts +1 -1
- package/packages/core/src/cli/dispatch-frontend.ts +24 -67
- package/packages/core/src/cli/dispatch-process.ts +14 -7
- package/packages/core/src/cli/dispatch-repository-feed.ts +196 -0
- package/packages/core/src/cli/dispatch.ts +304 -82
- package/packages/core/src/cli/dispatcher-liveness.ts +26 -19
- package/packages/core/src/cli/harvest.ts +84 -42
- package/packages/core/src/cli/help.ts +14 -8
- package/packages/core/src/cli/historical-build-state.ts +27 -0
- package/packages/core/src/cli/installation.ts +67 -13
- package/packages/core/src/cli/main.ts +23 -5
- package/packages/core/src/cli/mcp.ts +2 -1
- package/packages/core/src/cli/pr-attachments.ts +10 -13
- package/packages/core/src/cli/pr-summary-publication.ts +27 -60
- package/packages/core/src/cli/pr-summary.ts +43 -13
- package/packages/core/src/cli/publication-settlement.ts +10 -20
- package/packages/core/src/cli/repository-status.ts +38 -2
- package/packages/core/src/cli/self-update.ts +5 -1
- package/packages/core/src/cli/status.ts +62 -32
- package/packages/core/src/cli/terminals.ts +25 -142
- package/packages/core/src/cli/ticket-assets.ts +7 -2
- package/packages/core/src/cli/ticket-editor.ts +82 -0
- package/packages/core/src/cli/ticket.ts +166 -36
- package/packages/core/src/cli/wait.ts +91 -84
- package/packages/core/src/cli/watch.ts +60 -97
- package/packages/core/src/config/pipeline-source.ts +4 -3
- package/packages/core/src/config/schema.ts +35 -3
- package/packages/core/src/connection.ts +3 -3
- package/packages/core/src/events/catalog.ts +3 -2
- package/packages/core/src/events/payloads.ts +6 -0
- package/packages/core/src/events/repository.ts +7 -3
- package/packages/core/src/integration/harness.ts +38 -4
- package/packages/core/src/kernel/__snapshots__/reducer-registry.test.ts.snap +2444 -9
- package/packages/core/src/kernel/auto-merge.ts +19 -3
- package/packages/core/src/kernel/dispatcher-liveness.ts +10 -4
- package/packages/core/src/kernel/generators/build-log.ts +1 -1
- package/packages/core/src/kernel/generators/repository-journals.ts +14 -3
- package/packages/core/src/kernel/harvest.ts +25 -23
- package/packages/core/src/kernel/incremental-fixtures.ts +13 -2
- package/packages/core/src/kernel/log-index-value.ts +38 -0
- package/packages/core/src/kernel/read-facts.ts +637 -0
- package/packages/core/src/kernel/reducer-registry.ts +139 -3
- package/packages/core/src/kernel/repository-attention.ts +179 -0
- package/packages/core/src/kernel/service-facts.ts +135 -0
- package/packages/core/src/kernel/terminal-guard.ts +133 -0
- package/packages/core/src/ontology.ts +1 -0
- package/packages/core/src/operator/annotations.ts +12 -0
- package/packages/core/src/operator/control.ts +5 -6
- package/packages/core/src/operator/index.ts +7 -0
- package/packages/core/src/operator/protocol.ts +19 -2
- package/packages/core/src/operator/query.ts +37 -20
- package/packages/core/src/operator/read-view.ts +4 -13
- package/packages/core/src/operator/registry.ts +61 -8
- package/packages/core/src/operator/requests.ts +2 -14
- package/packages/core/src/operator/sandbox.ts +10 -9
- package/packages/core/src/operator/ticket-source-backend.ts +17 -21
- package/packages/core/src/operator/tickets.ts +132 -22
- package/packages/core/src/plugin-sdk/index.ts +19 -0
- package/packages/core/src/plugins/runtimes.ts +5 -1
- package/packages/core/src/ports/runner/session-env.ts +2 -0
- package/packages/core/src/ports/tickets/contract.ts +66 -1
- package/packages/core/src/ports/tickets/create.ts +48 -12
- package/packages/core/src/ports/tickets/fake.ts +30 -1
- package/packages/core/src/ports/tickets/file-assets.ts +159 -0
- package/packages/core/src/ports/tickets/file-codec.ts +90 -0
- package/packages/core/src/ports/tickets/file.ts +114 -120
- package/packages/core/src/ports/tickets/grants.ts +47 -0
- package/packages/core/src/ports/tickets/hosted-index.ts +17 -0
- package/packages/core/src/ports/tickets/identity.ts +20 -0
- package/packages/core/src/ports/tickets/lifecycle.ts +18 -0
- package/packages/core/src/ports/tickets/linear.ts +74 -9
- package/packages/core/src/ports/tickets/migration.ts +336 -0
- package/packages/core/src/ports/tickets/operations.ts +38 -7
- package/packages/core/src/ports/tickets/portable-assets.ts +54 -0
- package/packages/core/src/ports/tickets/remote-protocol.ts +95 -0
- package/packages/core/src/ports/tickets/remote.ts +60 -3
- package/packages/core/src/ports/tickets/requested-id.ts +46 -0
- package/packages/core/src/ports/tickets/revision.ts +62 -0
- package/packages/core/src/ports/tickets/states.ts +61 -0
- package/packages/core/src/ports/types.ts +40 -3
- package/packages/core/src/ports/workspace/build-execution.ts +1 -0
- package/packages/core/src/ports/workspace/create.ts +5 -1
- package/packages/core/src/ports/workspace/operator-sandbox.ts +2 -0
- package/packages/core/src/ports/workspace/vercel-sandbox.ts +8 -4
- package/packages/core/src/processes/build-child.ts +6 -6
- package/packages/core/src/processes/build-runner.ts +62 -70
- package/packages/core/src/processes/dispatch-failure-budget.ts +5 -4
- package/packages/core/src/processes/dispatcher.ts +223 -205
- package/packages/core/src/processes/execution-no-progress.ts +176 -0
- package/packages/core/src/processes/harvest-child.ts +30 -4
- package/packages/core/src/processes/harvest-execution-state.ts +15 -9
- package/packages/core/src/processes/harvest-runner.ts +58 -74
- package/packages/core/src/processes/harvest.ts +72 -29
- package/packages/core/src/processes/infrastructure-failure-budget.ts +13 -15
- package/packages/core/src/processes/orchestrator-tick.ts +2 -2
- package/packages/core/src/processes/phase-failure-budget.ts +1 -0
- package/packages/core/src/processes/publication-loss.ts +35 -21
- package/packages/core/src/processes/repo-view.ts +229 -1099
- package/packages/core/src/processes/tick-publication.ts +110 -0
- package/packages/core/src/store/adapter.ts +23 -0
- package/packages/core/src/store/build-scope.ts +17 -0
- package/packages/core/src/store/contract.ts +283 -1
- package/packages/core/src/store/local/schema.ts +1 -0
- package/packages/core/src/store/local/store.ts +180 -55
- package/packages/core/src/store/memory.ts +119 -29
- package/packages/core/src/store/phase-session.ts +17 -0
- package/packages/core/src/store/reduced-reads.ts +239 -0
- package/packages/core/src/store/reducer-value-shapes.json +14056 -0
- package/packages/core/src/store/reducer-value-validation.ts +96 -0
- package/packages/core/src/store/reducer-values.ts +145 -0
- package/packages/core/src/store/remote/client.ts +41 -0
- package/packages/core/src/store/remote/compatibility.ts +2 -0
- package/packages/core/src/store/remote/index.ts +19 -0
- package/packages/core/src/store/remote/protocol.ts +16 -0
- package/packages/core/src/store/remote/version.ts +3 -1
- package/packages/core/src/store/repo-state-events.ts +1 -1
- package/packages/core/src/store/repo-tick.ts +72 -0
- package/packages/core/src/store/session-handle.ts +19 -0
- package/packages/core/src/store/snapshots.ts +10 -0
- package/packages/core/src/store/ticket-asset-snapshots.ts +192 -0
- package/packages/core/src/store/ticket-assets.ts +86 -8
- package/packages/core/src/store/types.ts +40 -3
- package/packages/core/src/testing/dispatch-read-budget.ts +215 -0
- package/packages/core/src/testing/index.ts +8 -0
- package/packages/core/src/testing/reduced-read-budget.ts +462 -0
- package/skills/guide/SKILL.md +97 -12
- package/skills/guide/references/operator-api.md +8 -1
- package/skills/guide/references/remote-store-protocol.md +94 -2
- package/skills/guide/references/remote-ticket-protocol.md +151 -9
- package/skills/guide/references/setup.md +33 -7
- package/skills/guide/references/ticket-migration.md +64 -0
- package/skills/tickets/SKILL.md +71 -0
- package/templates/hosted-service/package.json +2 -2
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { readBuildDigests } from '../store/reduced-reads'
|
|
2
|
+
import { readRepositoryFacts, readBuildFacts } from '../store/reduced-reads'
|
|
1
3
|
/** Deterministic halves of observation harvest: scan, dedup, validation, and
|
|
2
4
|
* proposal rendering. Agent judgment is deliberately absent from this file. */
|
|
3
5
|
import type { AbEvent } from '../events/catalog'
|
|
@@ -135,10 +137,12 @@ export function evaluateHarvestPressure(
|
|
|
135
137
|
* so latest-wins carries exactly the drift fact the scan collects. */
|
|
136
138
|
export function evaluateHarvestPressureFromDigests(input: {
|
|
137
139
|
digests: Map<string, BuildDigest>
|
|
138
|
-
harvestEvents: RepositoryEvent[]
|
|
140
|
+
harvestEvents: RepositoryEvent[] | HarvestState
|
|
139
141
|
policy: { harvestThreshold: number; harvestMaxDrift: number }
|
|
140
142
|
}): HarvestPressure {
|
|
141
|
-
const claimed = claimedOccurrenceKeys(
|
|
143
|
+
const claimed = claimedOccurrenceKeys(
|
|
144
|
+
Array.isArray(input.harvestEvents) ? reduceHarvest(input.harvestEvents) : input.harvestEvents,
|
|
145
|
+
)
|
|
142
146
|
const unclaimed: HarvestPressureFacts['unclaimed'] = []
|
|
143
147
|
const merges: HarvestPressureFacts['merges'] = []
|
|
144
148
|
for (const digest of input.digests.values()) {
|
|
@@ -160,11 +164,11 @@ export function evaluateHarvestPressureFromDigests(input: {
|
|
|
160
164
|
export async function evaluateHarvestPressureFromStore(input: {
|
|
161
165
|
store: BuildStore
|
|
162
166
|
repo: string
|
|
163
|
-
harvestEvents: RepositoryEvent[]
|
|
167
|
+
harvestEvents: RepositoryEvent[] | HarvestState
|
|
164
168
|
policy: { harvestThreshold: number; harvestMaxDrift: number }
|
|
165
169
|
}): Promise<HarvestPressure> {
|
|
166
170
|
await input.store.ensureRepo(input.repo)
|
|
167
|
-
const digests = await input.store
|
|
171
|
+
const digests = await readBuildDigests(input.store, input.repo)
|
|
168
172
|
return evaluateHarvestPressureFromDigests({
|
|
169
173
|
digests,
|
|
170
174
|
harvestEvents: input.harvestEvents,
|
|
@@ -182,9 +186,11 @@ export function collectUnclaimedObservations(input: {
|
|
|
182
186
|
repo: string
|
|
183
187
|
records: BuildRecord[]
|
|
184
188
|
eventsByBuild: Map<string, AbEvent[]>
|
|
185
|
-
harvestEvents: RepositoryEvent[]
|
|
189
|
+
harvestEvents: RepositoryEvent[] | HarvestState
|
|
186
190
|
}): HarvestScanResult {
|
|
187
|
-
const state =
|
|
191
|
+
const state = Array.isArray(input.harvestEvents)
|
|
192
|
+
? reduceHarvest(input.harvestEvents)
|
|
193
|
+
: input.harvestEvents
|
|
188
194
|
const claimed = claimedOccurrenceKeys(state)
|
|
189
195
|
const observations: HarvestObservation[] = []
|
|
190
196
|
const merges: HarvestMerge[] = []
|
|
@@ -235,9 +241,11 @@ export function collectUnclaimedObservations(input: {
|
|
|
235
241
|
* are not assumed globally unique. */
|
|
236
242
|
export function unclaimedObservationCount(input: {
|
|
237
243
|
digests: Map<string, BuildDigest>
|
|
238
|
-
harvestEvents: RepositoryEvent[]
|
|
244
|
+
harvestEvents: RepositoryEvent[] | HarvestState
|
|
239
245
|
}): number {
|
|
240
|
-
const claimed = claimedOccurrenceKeys(
|
|
246
|
+
const claimed = claimedOccurrenceKeys(
|
|
247
|
+
Array.isArray(input.harvestEvents) ? reduceHarvest(input.harvestEvents) : input.harvestEvents,
|
|
248
|
+
)
|
|
241
249
|
let count = 0
|
|
242
250
|
for (const digest of input.digests.values()) {
|
|
243
251
|
for (const observation of digest.observations) {
|
|
@@ -279,7 +287,7 @@ export async function sampleUnclaimedObservationCount(
|
|
|
279
287
|
): Promise<number> {
|
|
280
288
|
const [digests, harvestEvents] = await Promise.all([
|
|
281
289
|
store.getRepoBuildDigests(repo),
|
|
282
|
-
|
|
290
|
+
readRepositoryFacts(store, repo).then((result) => result.value.harvest),
|
|
283
291
|
])
|
|
284
292
|
return unclaimedObservationCount({ digests, harvestEvents })
|
|
285
293
|
}
|
|
@@ -292,16 +300,31 @@ export async function scanUnclaimedObservations(
|
|
|
292
300
|
repo: string,
|
|
293
301
|
): Promise<HarvestScanResult> {
|
|
294
302
|
await store.ensureRepo(repo)
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
const harvestEvents = await store.getRepoStateEvents(repo)
|
|
303
|
+
const state = (await readRepositoryFacts(store, repo)).value.harvest
|
|
304
|
+
const claimed = claimedOccurrenceKeys(state)
|
|
298
305
|
const records = await store.listBuilds()
|
|
299
|
-
const
|
|
306
|
+
const observations: HarvestObservation[] = []
|
|
307
|
+
const merges: HarvestMerge[] = []
|
|
300
308
|
for (const record of records) {
|
|
301
309
|
if (record.repo !== repo) continue
|
|
302
|
-
|
|
310
|
+
const facts = (await readBuildFacts(store, record.slug)).value
|
|
311
|
+
for (const ts of facts.mergeTimes) merges.push({ build: record.slug, ts })
|
|
312
|
+
for (const observation of facts.observationFacts) {
|
|
313
|
+
const occurrence = { build: record.slug, seq: observation.seq }
|
|
314
|
+
if (claimed.has(occurrenceKey(occurrence))) continue
|
|
315
|
+
observations.push({
|
|
316
|
+
occurrence,
|
|
317
|
+
...observation.payload,
|
|
318
|
+
ts: observation.ts,
|
|
319
|
+
...(record.ticket ? { ticket: structuredClone(record.ticket) } : {}),
|
|
320
|
+
})
|
|
321
|
+
}
|
|
303
322
|
}
|
|
304
|
-
|
|
323
|
+
observations.sort(
|
|
324
|
+
(a, b) =>
|
|
325
|
+
a.occurrence.build.localeCompare(b.occurrence.build) || a.occurrence.seq - b.occurrence.seq,
|
|
326
|
+
)
|
|
327
|
+
return { state, observations, merges }
|
|
305
328
|
}
|
|
306
329
|
|
|
307
330
|
/** Distinct previously filed/joined proposal tickets, reconciled through the
|
|
@@ -327,12 +350,17 @@ export async function reconcileHarvestLedger(
|
|
|
327
350
|
)
|
|
328
351
|
if (entries.length === 0) return []
|
|
329
352
|
|
|
330
|
-
const ids = entries
|
|
353
|
+
const ids = entries
|
|
354
|
+
.filter((entry) => entry.ticket.source === tickets.name)
|
|
355
|
+
.map((entry) => entry.ticket.id)
|
|
331
356
|
const states = await tickets.dependencyStates(ids)
|
|
357
|
+
const byId = new Map(states.map((state) => [state.id, state]))
|
|
332
358
|
const out: HarvestLedgerTicket[] = []
|
|
333
359
|
for (let index = 0; index < entries.length; index += 1) {
|
|
334
360
|
const entry = entries[index]!
|
|
335
|
-
const dependency =
|
|
361
|
+
const dependency = (entry.ticket.source === tickets.name
|
|
362
|
+
? byId.get(entry.ticket.id)
|
|
363
|
+
: undefined) ?? {
|
|
336
364
|
id: entry.ticket.id,
|
|
337
365
|
exists: false,
|
|
338
366
|
resolved: false,
|
|
@@ -341,7 +369,7 @@ export async function reconcileHarvestLedger(
|
|
|
341
369
|
const current = dependency.exists ? await tickets.get(entry.ticket.id) : null
|
|
342
370
|
out.push({
|
|
343
371
|
proposalKey: entry.proposalKey,
|
|
344
|
-
ticket: structuredClone(entry.ticket),
|
|
372
|
+
ticket: structuredClone(current?.ref ?? entry.ticket),
|
|
345
373
|
exists: dependency.exists,
|
|
346
374
|
resolved: dependency.resolved,
|
|
347
375
|
...(current?.title !== undefined ? { title: current.title } : {}),
|
|
@@ -371,15 +399,25 @@ export async function reconcileOriginatingTickets(
|
|
|
371
399
|
const states = matchingIds.length > 0 ? await tickets.dependencyStates(matchingIds) : []
|
|
372
400
|
const byId = new Map(states.map((state) => [state.id, state]))
|
|
373
401
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
ticket
|
|
379
|
-
sourceMatches
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
402
|
+
const identity = new Set<string>()
|
|
403
|
+
const projected = await Promise.all(
|
|
404
|
+
refs.map(async (ticket) => {
|
|
405
|
+
const sourceMatches = ticket.source === tickets.name
|
|
406
|
+
const state = sourceMatches ? byId.get(ticket.id) : undefined
|
|
407
|
+
const current = sourceMatches && state?.exists ? await tickets.get(ticket.id) : null
|
|
408
|
+
return {
|
|
409
|
+
ticket: current?.ref ?? ticket,
|
|
410
|
+
sourceMatches,
|
|
411
|
+
exists: state?.exists ?? false,
|
|
412
|
+
resolved: state?.resolved ?? false,
|
|
413
|
+
}
|
|
414
|
+
}),
|
|
415
|
+
)
|
|
416
|
+
return projected.filter((entry) => {
|
|
417
|
+
const key = `${entry.ticket.source}:${entry.ticket.id}`
|
|
418
|
+
if (identity.has(key)) return false
|
|
419
|
+
identity.add(key)
|
|
420
|
+
return true
|
|
383
421
|
})
|
|
384
422
|
}
|
|
385
423
|
|
|
@@ -536,9 +574,14 @@ export async function resolveHarvestCreateBlockers(
|
|
|
536
574
|
const state = byId.get(id)
|
|
537
575
|
return state?.exists === true && !state.resolved
|
|
538
576
|
})
|
|
577
|
+
const canonical = new Map<string, string>()
|
|
578
|
+
for (const id of candidates) {
|
|
579
|
+
if (byId.get(id)?.exists) canonical.set(id, (await tickets.get(id))?.ref.id ?? id)
|
|
580
|
+
}
|
|
581
|
+
const project = (ids: string[]) => [...new Set(ids.map((id) => canonical.get(id) ?? id))]
|
|
539
582
|
return {
|
|
540
|
-
blockedBy:
|
|
541
|
-
provenance: { declared, derived },
|
|
583
|
+
blockedBy: project([...declared, ...derived]),
|
|
584
|
+
provenance: { declared: project(declared), derived: project(derived) },
|
|
542
585
|
}
|
|
543
586
|
}
|
|
544
587
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readBuildFacts, type CurrentBuildFacts } from '../store/reduced-reads'
|
|
1
2
|
import type { AbEvent } from '../events/catalog'
|
|
2
3
|
import { DISPATCHER } from '../events/envelope'
|
|
3
4
|
import type { EventPayload } from '../events/payloads'
|
|
@@ -10,7 +11,7 @@ export type InfrastructureCause = EventPayload<'infrastructure.failed'>['cause']
|
|
|
10
11
|
|
|
11
12
|
export interface InfrastructureFailureInput {
|
|
12
13
|
slug: string
|
|
13
|
-
events: readonly AbEvent[]
|
|
14
|
+
events: readonly AbEvent[] | CurrentBuildFacts
|
|
14
15
|
maxAttempts: number
|
|
15
16
|
provider: string
|
|
16
17
|
workspaceRef: string
|
|
@@ -23,7 +24,7 @@ export interface InfrastructureFailureInput {
|
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
export interface InfrastructureFailureDependencies {
|
|
26
|
-
store: Pick<BuildStore, 'append' | '
|
|
27
|
+
store: Pick<BuildStore, 'append' | 'getReducerValue'>
|
|
27
28
|
ids: IdSource
|
|
28
29
|
}
|
|
29
30
|
|
|
@@ -104,10 +105,14 @@ export async function recordInfrastructureFailure(
|
|
|
104
105
|
deps: InfrastructureFailureDependencies,
|
|
105
106
|
input: InfrastructureFailureInput,
|
|
106
107
|
): Promise<AbEvent[]> {
|
|
107
|
-
const lastReset =
|
|
108
|
-
|
|
109
|
-
input.events
|
|
110
|
-
|
|
108
|
+
const lastReset = Array.isArray(input.events)
|
|
109
|
+
? infrastructureFailureResetSeq(input.events)
|
|
110
|
+
: (input.events as CurrentBuildFacts).infrastructureReset
|
|
111
|
+
const attempt = Array.isArray(input.events)
|
|
112
|
+
? input.events.filter(
|
|
113
|
+
(event) => event.type === 'infrastructure.failed' && event.seq > lastReset,
|
|
114
|
+
).length + 1
|
|
115
|
+
: (input.events as CurrentBuildFacts).infrastructureCount + 1
|
|
111
116
|
const message = normalizeInfrastructureError(input.error)
|
|
112
117
|
const failure = await deps.store.append(input.slug, {
|
|
113
118
|
actor: DISPATCHER,
|
|
@@ -130,15 +135,8 @@ export async function recordInfrastructureFailure(
|
|
|
130
135
|
|
|
131
136
|
if (attempt < input.maxAttempts) return appended
|
|
132
137
|
|
|
133
|
-
const
|
|
134
|
-
if (
|
|
135
|
-
!currentEvents.some(
|
|
136
|
-
(event) =>
|
|
137
|
-
event.seq > lastReset &&
|
|
138
|
-
event.type === 'escalation.raised' &&
|
|
139
|
-
event.payload.policyCause === 'infrastructure-failure-limit',
|
|
140
|
-
)
|
|
141
|
-
) {
|
|
138
|
+
const current = (await readBuildFacts(deps.store, input.slug)).value
|
|
139
|
+
if (current.infrastructureRaisedSeq <= lastReset) {
|
|
142
140
|
appended.push(
|
|
143
141
|
await deps.store.append(input.slug, {
|
|
144
142
|
actor: DISPATCHER,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readBuildFacts } from '../store/reduced-reads'
|
|
1
2
|
/**
|
|
2
3
|
* The dispatcher tick's orchestrator step (AUT-342): resume suspended turns,
|
|
3
4
|
* reap crashed ones, and wake idle sessions from build attention events.
|
|
@@ -28,7 +29,6 @@ import { buildRegistry, type OperatorToolRegistry } from '../operator/registry'
|
|
|
28
29
|
import type { OperatorSandboxService } from '../operator/sandbox'
|
|
29
30
|
import { ticketBackendFromSource } from '../operator/ticket-source-backend'
|
|
30
31
|
import { reduceSession, type SessionTurnTrigger } from '../store/session-reducer'
|
|
31
|
-
import { reduceBuild } from '../kernel/reducer'
|
|
32
32
|
import type { AbEvent } from '../events/catalog'
|
|
33
33
|
import type { BuildStore, Clock } from '../store/types'
|
|
34
34
|
import type { RepoViewStore } from './repo-view'
|
|
@@ -315,7 +315,7 @@ export async function runOrchestratorTickStep(
|
|
|
315
315
|
event,
|
|
316
316
|
buildState: (options.view !== undefined
|
|
317
317
|
? await options.view.buildState(newest.build)
|
|
318
|
-
:
|
|
318
|
+
: (await readBuildFacts(store, newest.build)).value.state) as unknown as Record<
|
|
319
319
|
string,
|
|
320
320
|
unknown
|
|
321
321
|
>,
|
|
@@ -12,6 +12,7 @@ export const POLICY_PHASE_FAILURE_RESET_SCOPES = {
|
|
|
12
12
|
'verify-failure-limit': 'all-phase-rounds',
|
|
13
13
|
'reconcile-no-progress': 'none',
|
|
14
14
|
'dispatch-failure-limit': 'none',
|
|
15
|
+
'execution-no-progress': 'none',
|
|
15
16
|
'setup-failure-limit': 'none',
|
|
16
17
|
'infrastructure-failure-limit': 'none',
|
|
17
18
|
'phase-attempt-limit': 'matching-round',
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { summaryBoundary } from '../kernel/pr-summary'
|
|
2
1
|
import { isRemoteWorkspace } from '../events/workspace-remote'
|
|
3
2
|
/**
|
|
4
3
|
* Last-chance publication settlement at the workspace-release boundary
|
|
@@ -12,11 +11,6 @@ import { isRemoteWorkspace } from '../events/workspace-remote'
|
|
|
12
11
|
*/
|
|
13
12
|
import { DISPATCHER } from '../events/envelope'
|
|
14
13
|
import type { AbEvent } from '../events/catalog'
|
|
15
|
-
import {
|
|
16
|
-
latestUncompletedPublicationRequest,
|
|
17
|
-
publicationLostRecorded,
|
|
18
|
-
publicationRequestCompleted,
|
|
19
|
-
} from './publication-state'
|
|
20
14
|
import type { BuildStore } from '../store/types'
|
|
21
15
|
|
|
22
16
|
export interface PublicationLossDeps {
|
|
@@ -35,36 +29,56 @@ export interface PublicationLossDeps {
|
|
|
35
29
|
export async function settlePublicationBeforeRelease(
|
|
36
30
|
deps: PublicationLossDeps,
|
|
37
31
|
slug: string,
|
|
38
|
-
|
|
32
|
+
_events: AbEvent[],
|
|
39
33
|
reason: string,
|
|
40
34
|
): Promise<AbEvent[]> {
|
|
41
|
-
const
|
|
35
|
+
const { readBuildFacts } = await import('../store/reduced-reads')
|
|
36
|
+
await settlePublicationFactsBeforeRelease(
|
|
37
|
+
deps,
|
|
38
|
+
slug,
|
|
39
|
+
(await readBuildFacts(deps.store, slug)).value,
|
|
40
|
+
reason,
|
|
41
|
+
)
|
|
42
|
+
// Legacy callers explicitly requested the raw event output.
|
|
43
|
+
return deps.store.getEvents(slug)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Current-state release guard; the returned facts follow every settlement write. */
|
|
47
|
+
export async function settlePublicationFactsBeforeRelease(
|
|
48
|
+
deps: PublicationLossDeps,
|
|
49
|
+
slug: string,
|
|
50
|
+
facts: import('../store/reduced-reads').CurrentBuildFacts,
|
|
51
|
+
reason: string,
|
|
52
|
+
) {
|
|
53
|
+
const { readBuildFacts } = await import('../store/reduced-reads')
|
|
54
|
+
const request = facts.publication.latestUncompletedRequest()
|
|
42
55
|
if (request === undefined) {
|
|
43
|
-
const workspace = events.findLast((event) => event.type === 'workspace.provisioned')
|
|
44
56
|
if (
|
|
45
|
-
workspace
|
|
46
|
-
isRemoteWorkspace(workspace
|
|
47
|
-
summaryBoundary
|
|
57
|
+
facts.workspace &&
|
|
58
|
+
isRemoteWorkspace(facts.workspace) &&
|
|
59
|
+
facts.summaryBoundary !== undefined
|
|
48
60
|
) {
|
|
49
61
|
try {
|
|
50
62
|
await deps.settlePublication?.(slug)
|
|
51
63
|
} catch {
|
|
52
64
|
/* Summary is best effort. */
|
|
53
65
|
}
|
|
54
|
-
return deps.store
|
|
66
|
+
return (await readBuildFacts(deps.store, slug)).value
|
|
55
67
|
}
|
|
56
|
-
return
|
|
68
|
+
return facts
|
|
57
69
|
}
|
|
58
70
|
try {
|
|
59
71
|
await deps.settlePublication?.(slug)
|
|
60
72
|
} catch {
|
|
61
|
-
|
|
62
|
-
// either way the loss record below is what makes the release safe.
|
|
73
|
+
/* Record loss before release. */
|
|
63
74
|
}
|
|
64
|
-
const refreshed = await deps.store
|
|
65
|
-
if (
|
|
66
|
-
|
|
67
|
-
|
|
75
|
+
const refreshed = (await readBuildFacts(deps.store, slug)).value
|
|
76
|
+
if (
|
|
77
|
+
refreshed.publication.requestCompleted(request) ||
|
|
78
|
+
refreshed.publication.lostRecorded(request)
|
|
79
|
+
)
|
|
80
|
+
return refreshed
|
|
81
|
+
await deps.store.append(slug, {
|
|
68
82
|
actor: DISPATCHER,
|
|
69
83
|
type: 'publication.lost',
|
|
70
84
|
payload: {
|
|
@@ -75,5 +89,5 @@ export async function settlePublicationBeforeRelease(
|
|
|
75
89
|
reason,
|
|
76
90
|
},
|
|
77
91
|
})
|
|
78
|
-
return
|
|
92
|
+
return (await readBuildFacts(deps.store, slug)).value
|
|
79
93
|
}
|