@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,4 @@
|
|
|
1
|
+
import { readDeferralLedger } from '../store/reduced-reads'
|
|
1
2
|
import type { AbEvent, EventEnvelope, EventWrite } from '../events/catalog'
|
|
2
3
|
import { KERNEL } from '../events/envelope'
|
|
3
4
|
import { autoMergeDeferralClasses, type AutoMergeDeferralReason } from '../ports/types'
|
|
@@ -322,10 +323,10 @@ export async function recordAutoMergeDeferralObservation(
|
|
|
322
323
|
): Promise<EventEnvelope<'observation.recorded'> | null> {
|
|
323
324
|
if (autoMergeDeferralClasses[reason.code] === 'pipeline-resolved') return null
|
|
324
325
|
while (true) {
|
|
325
|
-
const
|
|
326
|
-
if (
|
|
326
|
+
const current = await readDeferralLedger(store, slug)
|
|
327
|
+
if (deferralObservationFrom(current.value, prNumber, commandSeq)) return null
|
|
327
328
|
|
|
328
|
-
const expectedSeq =
|
|
329
|
+
const expectedSeq = current.cursor
|
|
329
330
|
const appended = await store.appendIfCurrent(
|
|
330
331
|
slug,
|
|
331
332
|
expectedSeq,
|
|
@@ -334,3 +335,18 @@ export async function recordAutoMergeDeferralObservation(
|
|
|
334
335
|
if (appended !== null) return appended
|
|
335
336
|
}
|
|
336
337
|
}
|
|
338
|
+
|
|
339
|
+
/** Clock-independent merge attention shared by persisted seeds and CLI output. */
|
|
340
|
+
export function mergeAttentionFromFacts(state: BuildState, ledger: DeferralLedger) {
|
|
341
|
+
if (
|
|
342
|
+
!state.finalizeCompletedSeq ||
|
|
343
|
+
!state.pr ||
|
|
344
|
+
state.prState !== 'open' ||
|
|
345
|
+
state.status === 'done' ||
|
|
346
|
+
state.status === 'aborted'
|
|
347
|
+
)
|
|
348
|
+
return null
|
|
349
|
+
const deferral = currentAutoMergeDeferralFrom(ledger, state)
|
|
350
|
+
if (state.autoMerge.requested && deferral === undefined) return null
|
|
351
|
+
return { pr: state.pr, reason: deferral ?? 'Auto-merge is not armed' }
|
|
352
|
+
}
|
|
@@ -18,6 +18,9 @@ export function projectDispatcherLiveness(
|
|
|
18
18
|
(e.type === 'dispatcher.tick-completed' || e.type === 'dispatcher.tick-failed') &&
|
|
19
19
|
e.payload.run === id,
|
|
20
20
|
)
|
|
21
|
+
const finished = record?.tick?.finished?.run === id ? record.tick.finished : undefined
|
|
22
|
+
const useSample = finished !== undefined && (!tick || finished.completedAt >= tick.ts)
|
|
23
|
+
const lastTickAt = useSample ? finished.completedAt : (tick?.ts ?? null)
|
|
21
24
|
const heartbeatAt =
|
|
22
25
|
record?.lease?.holder === id && start && record.heartbeatAt && record.heartbeatAt >= start.ts
|
|
23
26
|
? record.heartbeatAt
|
|
@@ -30,10 +33,13 @@ export function projectDispatcherLiveness(
|
|
|
30
33
|
heartbeatAt,
|
|
31
34
|
heartbeatAgeMs: heartbeatAt === null ? null : age(heartbeatAt),
|
|
32
35
|
leaseExpiresAt: record?.lease?.holder === id ? record.lease.expiresAt : null,
|
|
33
|
-
lastTickAt
|
|
34
|
-
lastTickAgeMs:
|
|
35
|
-
lastTick:
|
|
36
|
-
|
|
36
|
+
lastTickAt,
|
|
37
|
+
lastTickAgeMs: lastTickAt ? age(lastTickAt) : null,
|
|
38
|
+
lastTick: useSample
|
|
39
|
+
? finished.outcome === 'completed'
|
|
40
|
+
? { outcome: 'completed' as const, counters: finished.counters }
|
|
41
|
+
: { outcome: 'failed' as const, error: finished.error }
|
|
42
|
+
: tick?.type === 'dispatcher.tick-completed'
|
|
37
43
|
? { outcome: 'completed' as const, counters: tick.payload.counters }
|
|
38
44
|
: tick?.type === 'dispatcher.tick-failed'
|
|
39
45
|
? { outcome: 'failed' as const, error: tick.payload.error }
|
|
@@ -151,7 +151,7 @@ function randomWrite(rand: () => number): EventWrite {
|
|
|
151
151
|
id,
|
|
152
152
|
phase: 'plan',
|
|
153
153
|
source: 'policy',
|
|
154
|
-
policyCause: 'infrastructure-failure-limit',
|
|
154
|
+
policyCause: round % 2 === 0 ? 'execution-no-progress' : 'infrastructure-failure-limit',
|
|
155
155
|
question: 'q',
|
|
156
156
|
}),
|
|
157
157
|
() =>
|
|
@@ -119,13 +119,24 @@ export function randomSettingsJournal(seed: number, length: number): RepositoryE
|
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
const counters = {
|
|
122
|
-
claimed: 0,
|
|
123
|
-
launched: 0,
|
|
124
122
|
merged: 0,
|
|
125
123
|
closed: 0,
|
|
126
124
|
conflicted: 0,
|
|
127
|
-
|
|
125
|
+
abandoned: 0,
|
|
126
|
+
discarded: 0,
|
|
127
|
+
janitorFailed: 0,
|
|
128
|
+
recovered: 0,
|
|
129
|
+
dispatchFailed: 0,
|
|
130
|
+
resumed: 0,
|
|
131
|
+
swept: 0,
|
|
132
|
+
dispatched: 0,
|
|
133
|
+
authored: 0,
|
|
134
|
+
bounced: 0,
|
|
135
|
+
claimRaces: 0,
|
|
136
|
+
invalidTickets: 0,
|
|
137
|
+
dependencyBlocked: 0,
|
|
128
138
|
harvestStarted: 0,
|
|
139
|
+
harvestResumed: 0,
|
|
129
140
|
harvestCompleted: 0,
|
|
130
141
|
harvestEscalated: 0,
|
|
131
142
|
harvestFailed: 0,
|
|
@@ -741,32 +741,34 @@ export function harvestCreationsDuringReadyScan(
|
|
|
741
741
|
beforeEvents: readonly RepositoryEvent[],
|
|
742
742
|
afterEvents: readonly RepositoryEvent[],
|
|
743
743
|
): InFlightHarvestCreation[] {
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
744
|
+
return harvestCreationsFromStates(reduceHarvest(beforeEvents), reduceHarvest(afterEvents))
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
export function harvestCreationsFromStates(
|
|
748
|
+
before: HarvestState,
|
|
749
|
+
after: HarvestState,
|
|
750
|
+
): InFlightHarvestCreation[] {
|
|
747
751
|
const creations = new Map<string, InFlightHarvestCreation>()
|
|
748
|
-
const keyOf = (run: string, proposalKey: string)
|
|
749
|
-
const known = new Set
|
|
750
|
-
|
|
752
|
+
const keyOf = (run: string, proposalKey: string) => `${run}\0${proposalKey}`
|
|
753
|
+
const known = new Set(
|
|
754
|
+
before.runs.flatMap((run) =>
|
|
755
|
+
run.reservations.map((entry) => keyOf(run.run, entry.proposalKey)),
|
|
756
|
+
),
|
|
757
|
+
)
|
|
758
|
+
for (const creation of inFlightHarvestCreations(before))
|
|
759
|
+
creations.set(keyOf(creation.run, creation.proposalKey), creation)
|
|
760
|
+
for (const run of after.runs)
|
|
751
761
|
for (const reservation of run.reservations) {
|
|
752
|
-
|
|
762
|
+
const key = keyOf(run.run, reservation.proposalKey)
|
|
763
|
+
if (known.has(key) || reservation.seq <= before.lastSeq) continue
|
|
764
|
+
known.add(key)
|
|
765
|
+
creations.set(key, {
|
|
766
|
+
run: run.run,
|
|
767
|
+
proposalKey: reservation.proposalKey,
|
|
768
|
+
creationKey: reservation.id,
|
|
769
|
+
reservationSeq: reservation.seq,
|
|
770
|
+
})
|
|
753
771
|
}
|
|
754
|
-
}
|
|
755
|
-
for (const creation of inFlightHarvestCreations(before)) {
|
|
756
|
-
creations.set(keyOf(creation.run, creation.proposalKey), creation)
|
|
757
|
-
}
|
|
758
|
-
for (const event of afterEvents) {
|
|
759
|
-
if (event.seq <= before.lastSeq || event.type !== 'harvest.proposal.id-reserved') continue
|
|
760
|
-
const key = keyOf(event.payload.run, event.payload.proposalKey)
|
|
761
|
-
if (known.has(key)) continue
|
|
762
|
-
known.add(key)
|
|
763
|
-
creations.set(key, {
|
|
764
|
-
run: event.payload.run,
|
|
765
|
-
proposalKey: event.payload.proposalKey,
|
|
766
|
-
creationKey: event.payload.id,
|
|
767
|
-
reservationSeq: event.seq,
|
|
768
|
-
})
|
|
769
|
-
}
|
|
770
772
|
return [...creations.values()]
|
|
771
773
|
}
|
|
772
774
|
|
|
@@ -99,7 +99,12 @@ export function randomBuildItem(rand: Rand): readonly [string, Record<string, un
|
|
|
99
99
|
],
|
|
100
100
|
() => [
|
|
101
101
|
'execution.ended',
|
|
102
|
-
{
|
|
102
|
+
{
|
|
103
|
+
instance,
|
|
104
|
+
workspaceRef: ref,
|
|
105
|
+
outcome: pick(rand, ['completed', 'stopped', 'lost']),
|
|
106
|
+
...(rand() < 0.5 ? { exitCode: pick(rand, [0, 1, null]) } : {}),
|
|
107
|
+
},
|
|
103
108
|
],
|
|
104
109
|
() => [
|
|
105
110
|
'workspace.provisioned',
|
|
@@ -132,7 +137,13 @@ export function randomBuildItem(rand: Rand): readonly [string, Record<string, un
|
|
|
132
137
|
phase: pick(rand, ['setup', 'plan', 'implement', 'verify:unit']),
|
|
133
138
|
source: pick(rand, ['policy', 'agent']),
|
|
134
139
|
...(rand() < 0.7
|
|
135
|
-
? {
|
|
140
|
+
? {
|
|
141
|
+
policyCause: pick(rand, [
|
|
142
|
+
'infrastructure-failure-limit',
|
|
143
|
+
'setup-failure-limit',
|
|
144
|
+
'execution-no-progress',
|
|
145
|
+
]),
|
|
146
|
+
}
|
|
136
147
|
: {}),
|
|
137
148
|
...(rand() < 0.5 ? { round: 1 } : {}),
|
|
138
149
|
question: 'q',
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { LogIndex, LoopIndex, RoundRecord } from './log-index'
|
|
2
|
+
type JsonLoop = Omit<LoopIndex, 'rounds'> & {
|
|
3
|
+
rounds: [number, RoundRecord][]
|
|
4
|
+
}
|
|
5
|
+
export type JsonLogIndex = Omit<LogIndex, 'plan' | 'code' | 'finalizeStepsDone'> & {
|
|
6
|
+
plan: JsonLoop
|
|
7
|
+
code: JsonLoop
|
|
8
|
+
finalizeStepsDone: string[]
|
|
9
|
+
}
|
|
10
|
+
export function encodeLog(log: LogIndex): JsonLogIndex {
|
|
11
|
+
return {
|
|
12
|
+
...log,
|
|
13
|
+
plan: { ...log.plan, rounds: [...log.plan.rounds] },
|
|
14
|
+
code: { ...log.code, rounds: [...log.code.rounds] },
|
|
15
|
+
finalizeStepsDone: [...log.finalizeStepsDone],
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export function decodeLog(log: JsonLogIndex): LogIndex {
|
|
19
|
+
const loop = (value: JsonLoop): LoopIndex => {
|
|
20
|
+
for (const entry of value.rounds) {
|
|
21
|
+
if (
|
|
22
|
+
entry.length !== 2 ||
|
|
23
|
+
!Number.isSafeInteger(entry[0]) ||
|
|
24
|
+
entry[1] === null ||
|
|
25
|
+
typeof entry[1] !== 'object' ||
|
|
26
|
+
Array.isArray(entry[1])
|
|
27
|
+
)
|
|
28
|
+
throw new Error('invalid persisted log-index round entry')
|
|
29
|
+
}
|
|
30
|
+
return { ...value, rounds: new Map(value.rounds) }
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
...log,
|
|
34
|
+
plan: loop(log.plan),
|
|
35
|
+
code: loop(log.code),
|
|
36
|
+
finalizeStepsDone: new Set(log.finalizeStepsDone),
|
|
37
|
+
}
|
|
38
|
+
}
|