@defrex/autobuild 0.8.0 → 0.10.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 +323 -26
- package/bin/ab-build-runner.ts +8 -2
- package/docs/architecture.md +166 -14
- package/docs/assets/headline-wide.png +0 -0
- package/docs/configuration.md +175 -28
- package/docs/hosted-dispatcher.md +69 -13
- package/docs/operator-api.md +2 -2
- package/docs/remote-store-protocol.md +204 -16
- package/docs/remote-ticket-protocol.md +1 -1
- package/docs/setup.md +12 -9
- package/docs/website.md +14 -0
- package/package.json +45 -23
- package/packages/core/src/cli/artifact.ts +130 -6
- package/packages/core/src/cli/binary.ts +9 -1
- package/packages/core/src/cli/build-control.ts +110 -13
- package/packages/core/src/cli/context.ts +46 -0
- package/packages/core/src/cli/dashboard/facts.ts +215 -0
- package/packages/core/src/cli/dashboard/frame-parse.ts +223 -0
- package/packages/core/src/cli/dashboard/model.ts +141 -153
- package/packages/core/src/cli/dashboard/poll.ts +76 -12
- package/packages/core/src/cli/dashboard/render.ts +34 -5
- package/packages/core/src/cli/dispatch-frontend.ts +54 -5
- package/packages/core/src/cli/dispatch-process.ts +1 -13
- package/packages/core/src/cli/dispatch.ts +392 -75
- package/packages/core/src/cli/harvest.ts +122 -8
- package/packages/core/src/cli/help.ts +62 -9
- package/packages/core/src/cli/init-readiness-remote.ts +110 -0
- package/packages/core/src/cli/init-readiness-shared.ts +72 -0
- package/packages/core/src/cli/init-validation.ts +266 -222
- package/packages/core/src/cli/installation.ts +50 -8
- package/packages/core/src/cli/main.ts +70 -11
- package/packages/core/src/cli/mcp.ts +23 -1
- package/packages/core/src/cli/plugin.ts +7 -4
- package/packages/core/src/cli/remote-poll.ts +277 -0
- package/packages/core/src/cli/repo-state.ts +22 -0
- package/packages/core/src/cli/repository-status.ts +39 -1
- package/packages/core/src/cli/self-update.ts +77 -2
- package/packages/core/src/cli/status.ts +150 -28
- package/packages/core/src/cli/terminals.ts +2 -1
- package/packages/core/src/cli/ticket-assets.ts +247 -0
- package/packages/core/src/cli/ticket.ts +453 -8
- package/packages/core/src/cli/wait.ts +157 -41
- package/packages/core/src/cli/watch.ts +138 -168
- package/packages/core/src/config/live.ts +23 -0
- package/packages/core/src/config/pipeline-source.ts +388 -0
- package/packages/core/src/config/schema.ts +251 -19
- package/packages/core/src/events/catalog.ts +1 -0
- package/packages/core/src/events/globs.ts +47 -0
- package/packages/core/src/events/payloads.ts +52 -2
- package/packages/core/src/events/repository.ts +52 -1
- package/packages/core/src/events/sessions.ts +69 -10
- package/packages/core/src/events/workspace-remote.ts +12 -0
- package/packages/core/src/integration/harness.ts +115 -2
- package/packages/core/src/kernel/__snapshots__/reducer-registry.test.ts.snap +1369 -0
- package/packages/core/src/kernel/auto-merge-default.ts +157 -0
- package/packages/core/src/kernel/auto-merge.ts +99 -22
- package/packages/core/src/kernel/dispatch-settings.ts +42 -17
- package/packages/core/src/kernel/dispatch-status.ts +23 -0
- package/packages/core/src/kernel/engine.ts +9 -438
- package/packages/core/src/kernel/generators/build-log.ts +259 -0
- package/packages/core/src/kernel/generators/repository-journals.ts +206 -0
- package/packages/core/src/kernel/generators/session-log.ts +72 -0
- package/packages/core/src/kernel/harvest.ts +62 -18
- package/packages/core/src/kernel/incremental-contract.ts +75 -0
- package/packages/core/src/kernel/incremental-fixtures.ts +211 -0
- package/packages/core/src/kernel/incremental.ts +53 -0
- package/packages/core/src/kernel/log-index.ts +620 -0
- package/packages/core/src/kernel/phases.ts +16 -2
- package/packages/core/src/kernel/pr-attachments.ts +164 -46
- package/packages/core/src/kernel/reducer-registry.ts +164 -0
- package/packages/core/src/kernel/reducer.ts +254 -73
- package/packages/core/src/operator/annotations.ts +6 -0
- package/packages/core/src/operator/control.ts +19 -5
- package/packages/core/src/operator/index.ts +71 -6
- package/packages/core/src/operator/query.ts +253 -24
- package/packages/core/src/operator/read-view.ts +98 -0
- package/packages/core/src/operator/registry.ts +55 -10
- package/packages/core/src/operator/requests.ts +9 -6
- package/packages/core/src/operator/sandbox-host.ts +140 -0
- package/packages/core/src/operator/sandbox.ts +247 -14
- package/packages/core/src/operator/ticket-source-backend.ts +47 -0
- package/packages/core/src/orchestrator/approvals.ts +115 -0
- package/packages/core/src/orchestrator/conversation.ts +167 -0
- package/packages/core/src/orchestrator/model.ts +86 -0
- package/packages/core/src/orchestrator/prompt.ts +61 -0
- package/packages/core/src/orchestrator/tools.ts +117 -0
- package/packages/core/src/orchestrator/turn-runner.ts +457 -0
- package/packages/core/src/plugin-sdk/index.ts +51 -1
- package/packages/core/src/plugins/load.ts +135 -7
- package/packages/core/src/plugins/manifest.ts +183 -10
- package/packages/core/src/plugins/registry.ts +73 -4
- package/packages/core/src/ports/forge/create.ts +1 -1
- package/packages/core/src/ports/forge/fake.ts +31 -0
- package/packages/core/src/ports/forge/github-transport.ts +13 -2
- package/packages/core/src/ports/forge/github.ts +13 -2
- package/packages/core/src/ports/forge/local-git.ts +31 -1
- package/packages/core/src/ports/runner/claude.ts +2 -2
- package/packages/core/src/ports/runner/codex.ts +6 -2
- package/packages/core/src/ports/runner/fake.ts +7 -1
- package/packages/core/src/ports/runner/pi.ts +8 -6
- package/packages/core/src/ports/runner/provider-error.ts +13 -3
- package/packages/core/src/ports/tickets/fake.ts +5 -1
- package/packages/core/src/ports/tickets/file.ts +169 -48
- package/packages/core/src/ports/tickets/{remote-index.ts → hosted-index.ts} +3 -2
- package/packages/core/src/ports/tickets/linear.ts +33 -1
- package/packages/core/src/ports/tickets/operations.ts +1 -1
- package/packages/core/src/ports/tickets/remote.ts +8 -1
- package/packages/core/src/ports/types.ts +49 -3
- package/packages/core/src/ports/workspace/builtin-capabilities.ts +114 -0
- package/packages/core/src/ports/workspace/create.ts +74 -39
- package/packages/core/src/ports/workspace/distribution-archive.ts +171 -12
- package/packages/core/src/ports/workspace/fake.ts +169 -4
- package/packages/core/src/ports/workspace/git-worktree.ts +246 -26
- package/packages/core/src/ports/workspace/github-origin.ts +36 -0
- package/packages/core/src/ports/workspace/operator-sandbox.ts +7 -1
- package/packages/core/src/ports/workspace/provider-capabilities.ts +171 -0
- package/packages/core/src/ports/workspace/vercel-capabilities.ts +110 -0
- package/packages/core/src/ports/workspace/vercel-sandbox.ts +142 -114
- package/packages/core/src/processes/build-child.ts +17 -2
- package/packages/core/src/processes/build-execution-state.ts +68 -15
- package/packages/core/src/processes/build-runner.ts +238 -93
- package/packages/core/src/processes/dispatcher-selectors.ts +123 -0
- package/packages/core/src/processes/dispatcher.ts +501 -201
- package/packages/core/src/processes/execution-settlement.ts +72 -39
- package/packages/core/src/processes/harvest-child.ts +5 -0
- package/packages/core/src/processes/harvest-execution-state.ts +41 -29
- package/packages/core/src/processes/harvest-runner.ts +66 -19
- package/packages/core/src/processes/harvest.ts +196 -24
- package/packages/core/src/processes/infrastructure-failure-budget.ts +31 -18
- package/packages/core/src/processes/orchestrator-tick.ts +360 -0
- package/packages/core/src/processes/publication-state.ts +164 -52
- package/packages/core/src/processes/repo-view.ts +1436 -0
- package/packages/core/src/processes/sandbox-state.ts +36 -2
- package/packages/core/src/processes/tick-state.ts +33 -0
- package/packages/core/src/store/adapter.ts +41 -0
- package/packages/core/src/store/build-scope.ts +90 -0
- package/packages/core/src/store/contract.ts +1205 -0
- package/packages/core/src/store/digest.ts +102 -0
- package/packages/core/src/store/local/schema.ts +46 -2
- package/packages/core/src/store/local/store.ts +445 -4
- package/packages/core/src/store/memory.ts +246 -0
- package/packages/core/src/store/new-build.ts +17 -0
- package/packages/core/src/store/phase-session.ts +94 -0
- package/packages/core/src/store/remote/client.ts +310 -2
- package/packages/core/src/store/remote/index.ts +14 -2
- package/packages/core/src/store/remote/protocol.ts +115 -12
- package/packages/core/src/store/remote/version.ts +6 -2
- package/packages/core/src/store/repo-state-events.ts +205 -0
- package/packages/core/src/store/retention.ts +7 -0
- package/packages/core/src/store/session-handle.ts +94 -0
- package/packages/core/src/{sessions/reducer.ts → store/session-reducer.ts} +79 -19
- package/packages/core/src/store/snapshots.ts +88 -0
- package/packages/core/src/store/streams/session-writer.ts +42 -2
- package/packages/core/src/store/streams/wait.ts +4 -5
- package/packages/core/src/store/ticket-assets.ts +458 -0
- package/packages/core/src/store/types.ts +160 -1
- package/packages/core/src/testing/index.ts +59 -0
- package/skills/code-review/SKILL.md +5 -1
- package/skills/guide/SKILL.md +152 -40
- package/skills/guide/references/operator-api.md +2 -2
- package/skills/guide/references/plugin-authoring.md +17 -0
- package/skills/guide/references/remote-store-protocol.md +204 -16
- package/skills/guide/references/remote-ticket-protocol.md +1 -1
- package/skills/guide/references/setup.md +12 -9
- package/skills/implement/SKILL.md +3 -0
- package/skills/operate/SKILL.md +126 -0
- package/skills/plan/SKILL.md +4 -0
- package/skills/plan-review/SKILL.md +5 -1
- package/skills/spec/SKILL.md +46 -3
- package/skills/tickets/SKILL.md +57 -5
- package/packages/core/src/cli/dashboard/frame-image.ts +0 -373
- package/packages/core/src/cli/store-opening.contract.ts +0 -289
- package/packages/core/src/cli/testkit.ts +0 -194
- package/packages/core/src/markdown.ts +0 -67
- package/packages/core/src/operator/client.ts +0 -311
- package/packages/core/src/operator/server.ts +0 -616
- package/packages/core/src/operator/sessions.ts +0 -223
- package/packages/core/src/ports/runner/live-contract-fixture.ts +0 -176
- package/packages/core/src/ports/tickets/remote-server.ts +0 -185
- package/packages/core/src/store/remote/server.ts +0 -923
- package/packages/core/src/testing/store-failures.ts +0 -32
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { Actor } from '../events/envelope'
|
|
11
11
|
import type { AbEvent } from '../events/catalog'
|
|
12
|
-
import { normalizeVerifyCompletion, type EventPayload } from '../events/payloads'
|
|
12
|
+
import { normalizeVerifyCompletion, type EventPayload, type PinnedAsset } from '../events/payloads'
|
|
13
13
|
import type {
|
|
14
14
|
ArtifactRef,
|
|
15
15
|
BuildOutcome,
|
|
@@ -24,6 +24,7 @@ import type {
|
|
|
24
24
|
VerifyOutcome,
|
|
25
25
|
} from '../ontology'
|
|
26
26
|
import { verifyPhase } from '../ontology'
|
|
27
|
+
import { defineReducer, type IncrementalReducer } from './incremental'
|
|
27
28
|
|
|
28
29
|
/** An `escalation.raised` without a matching `escalation.answered` — the
|
|
29
30
|
* definition of `blocked` (§15.5). Matched by id, so answers may arrive in
|
|
@@ -52,6 +53,8 @@ export interface AnsweredEscalation extends OpenEscalation {
|
|
|
52
53
|
resolution: EscalationResolution
|
|
53
54
|
/** Exact replacement spec revision authorized by a `revise-spec` answer. */
|
|
54
55
|
artifact?: ArtifactRef
|
|
56
|
+
/** Ticket assets sampled with a revise-from-ticket authorization. */
|
|
57
|
+
assets?: PinnedAsset[]
|
|
55
58
|
/** Absolute review budget accepted with a review-round-limit answer. */
|
|
56
59
|
reviewRoundCeiling?: number
|
|
57
60
|
/** seq of the `escalation.answered` event. */
|
|
@@ -130,6 +133,11 @@ export interface AutoMergeProjection {
|
|
|
130
133
|
/** Latest human command. False with no commandSeq means the default: off. */
|
|
131
134
|
requested: boolean
|
|
132
135
|
commandSeq?: number
|
|
136
|
+
/** The repository seq of the `dispatcher.auto-merge-default-set` fact the
|
|
137
|
+
* latest answering command (claim-time seed or fan-out) sampled, when one
|
|
138
|
+
* did. A per-build command with no `defaultSeq` preserves the previous
|
|
139
|
+
* value, so the default fan-out can tell an override from staleness. */
|
|
140
|
+
defaultSeq?: number
|
|
133
141
|
/** Latest recorded external application. It may acknowledge an older
|
|
134
142
|
* command; such a stale fact never changes `requested`/`commandSeq`. */
|
|
135
143
|
applied?: { enabled: boolean; commandSeq: number }
|
|
@@ -194,6 +202,9 @@ export interface BuildState {
|
|
|
194
202
|
lastEvent?: AbEvent
|
|
195
203
|
/** 0 for an empty log; `runner.attached {resumedFromSeq}` cites this. */
|
|
196
204
|
lastSeq: number
|
|
205
|
+
/** Ticket assets the build froze: set by `build.created`, replaced by a
|
|
206
|
+
* `spec.revised` that carries `assets` (§6.3). Empty for historical logs. */
|
|
207
|
+
pinnedAssets: PinnedAsset[]
|
|
197
208
|
/** Latest spec artifact rev — `spec.imported`/`spec.authored` set it,
|
|
198
209
|
* `spec.revised` bumps it (§6.3: rev N+1 restarts the build from plan). */
|
|
199
210
|
specRev?: number
|
|
@@ -279,48 +290,143 @@ export interface BuildState {
|
|
|
279
290
|
failures: Record<string, number>
|
|
280
291
|
}
|
|
281
292
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
293
|
+
/** Whether the build carries an outstanding `discardRequest`. Present exactly
|
|
294
|
+
* while the build is non-terminal and its discard is unsettled; the reducer
|
|
295
|
+
* settles it only by terminal completion. This is the single predicate shared
|
|
296
|
+
* by the auto-merge default fan-out (`autoMergeDefaultEligible`, AUT-418) and
|
|
297
|
+
* the per-build consent guard (`controlBuild`'s `discard-pending` refusal), so
|
|
298
|
+
* the two controls' discard exclusions cannot diverge silently. */
|
|
299
|
+
export function discardInFlight(state: Pick<BuildState, 'discardRequest'>): boolean {
|
|
300
|
+
return state.discardRequest !== undefined
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/** The reducer's carried accumulator: everything `reduceBuild` folds, before
|
|
304
|
+
* the final status derivation. Plain JSON-shaped data (the escalation and
|
|
305
|
+
* session maps are insertion-ordered entry arrays) so it survives a round
|
|
306
|
+
* trip through `JSON.stringify` — the foundation for incremental advancement
|
|
307
|
+
* and cached state. */
|
|
308
|
+
export interface BuildAcc {
|
|
309
|
+
attached: boolean
|
|
310
|
+
pausedFlag: boolean
|
|
311
|
+
terminal?: 'done' | 'aborted'
|
|
312
|
+
outcome?: BuildOutcome
|
|
313
|
+
round: number
|
|
314
|
+
currentPhase?: PhaseContext
|
|
315
|
+
lastCompletedPhase?: PhaseContext
|
|
316
|
+
openEscalations: OpenEscalation[]
|
|
317
|
+
answeredEscalations: AnsweredEscalation[]
|
|
318
|
+
reviewRoundCeilings: BuildState['reviewRoundCeilings']
|
|
319
|
+
pr: BuildState['pr']
|
|
320
|
+
prState?: PrLifecycle
|
|
321
|
+
autoMerge: AutoMergeProjection
|
|
322
|
+
specRev?: number
|
|
323
|
+
restartSince: number
|
|
324
|
+
pinnedAssets: PinnedAsset[]
|
|
325
|
+
finalizeCompletedSeq: number
|
|
326
|
+
finalizeSteps: BuildState['finalizeSteps']
|
|
327
|
+
plan: BuildState['plan']
|
|
328
|
+
implement: BuildState['implement']
|
|
329
|
+
codeReviewApproved: boolean
|
|
330
|
+
codeReviewApproval: BuildState['codeReviewApproval']
|
|
331
|
+
planReviewFindings: Finding[][]
|
|
332
|
+
codeReviewFindings: Finding[][]
|
|
333
|
+
verify: BuildState['verify']
|
|
334
|
+
reconcileAttempts: number
|
|
335
|
+
observations: ObservationRecord[]
|
|
336
|
+
setupFailures: BuildState['setupFailures']
|
|
337
|
+
setupFailure: BuildState['setupFailure']
|
|
338
|
+
dispatchFailures: BuildState['dispatchFailures']
|
|
339
|
+
executions: BuildState['executions']
|
|
340
|
+
infrastructureFailures: BuildState['infrastructureFailures']
|
|
341
|
+
infrastructureFailure: BuildState['infrastructureFailure']
|
|
342
|
+
cleanupAttempts: BuildState['cleanupAttempts']
|
|
343
|
+
discardRequest: BuildState['discardRequest']
|
|
344
|
+
pending: Record<PendingCommand['command'], PendingCommand[]>
|
|
345
|
+
openSessions: OpenSession[]
|
|
346
|
+
failures: Record<string, number>
|
|
347
|
+
lastEvent?: AbEvent
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/** Bump when `BuildAcc`'s shape or the fold's semantics change. */
|
|
351
|
+
export const BUILD_REDUCER_VERSION = 1
|
|
352
|
+
|
|
353
|
+
function initialBuildAcc(): BuildAcc {
|
|
354
|
+
return {
|
|
355
|
+
attached: false,
|
|
356
|
+
pausedFlag: false,
|
|
357
|
+
round: 0,
|
|
358
|
+
openEscalations: [],
|
|
359
|
+
answeredEscalations: [],
|
|
360
|
+
reviewRoundCeilings: {},
|
|
361
|
+
pr: undefined,
|
|
362
|
+
autoMerge: { requested: false },
|
|
363
|
+
restartSince: 0,
|
|
364
|
+
pinnedAssets: [],
|
|
365
|
+
finalizeCompletedSeq: 0,
|
|
366
|
+
finalizeSteps: [],
|
|
367
|
+
plan: { round: 0, approved: false },
|
|
368
|
+
implement: { round: 0 },
|
|
369
|
+
codeReviewApproved: false,
|
|
370
|
+
codeReviewApproval: undefined,
|
|
371
|
+
planReviewFindings: [],
|
|
372
|
+
codeReviewFindings: [],
|
|
373
|
+
verify: { maxAttemptSeen: 0, results: [], cycleSince: 0 },
|
|
374
|
+
reconcileAttempts: 0,
|
|
375
|
+
observations: [],
|
|
376
|
+
setupFailures: [],
|
|
377
|
+
setupFailure: undefined,
|
|
378
|
+
dispatchFailures: [],
|
|
379
|
+
executions: [],
|
|
380
|
+
infrastructureFailures: [],
|
|
381
|
+
infrastructureFailure: undefined,
|
|
382
|
+
cleanupAttempts: [],
|
|
383
|
+
discardRequest: undefined,
|
|
384
|
+
pending: { pause: [], resume: [], abort: [] },
|
|
385
|
+
openSessions: [],
|
|
386
|
+
failures: {},
|
|
321
387
|
}
|
|
322
|
-
|
|
323
|
-
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/** Fold `events` into `acc` in place. */
|
|
391
|
+
function foldBuild(acc: BuildAcc, events: readonly AbEvent[]): void {
|
|
392
|
+
let attached = acc.attached
|
|
393
|
+
let pausedFlag = acc.pausedFlag
|
|
394
|
+
let terminal = acc.terminal
|
|
395
|
+
let outcome = acc.outcome
|
|
396
|
+
let round = acc.round
|
|
397
|
+
let currentPhase = acc.currentPhase
|
|
398
|
+
let lastCompletedPhase = acc.lastCompletedPhase
|
|
399
|
+
const openEscalations = new Map(acc.openEscalations.map((e) => [e.id, e]))
|
|
400
|
+
const answeredEscalations = acc.answeredEscalations
|
|
401
|
+
const reviewRoundCeilings = acc.reviewRoundCeilings
|
|
402
|
+
let pr = acc.pr
|
|
403
|
+
let prState = acc.prState
|
|
404
|
+
const autoMerge = acc.autoMerge
|
|
405
|
+
let specRev = acc.specRev
|
|
406
|
+
let restartSince = acc.restartSince
|
|
407
|
+
let pinnedAssets = acc.pinnedAssets
|
|
408
|
+
let finalizeCompletedSeq = acc.finalizeCompletedSeq
|
|
409
|
+
const finalizeSteps = acc.finalizeSteps
|
|
410
|
+
const plan = acc.plan
|
|
411
|
+
const implement = acc.implement
|
|
412
|
+
let codeReviewApproved = acc.codeReviewApproved
|
|
413
|
+
let codeReviewApproval = acc.codeReviewApproval
|
|
414
|
+
const planReviewFindings = acc.planReviewFindings
|
|
415
|
+
const codeReviewFindings = acc.codeReviewFindings
|
|
416
|
+
const verify = acc.verify
|
|
417
|
+
let reconcileAttempts = acc.reconcileAttempts
|
|
418
|
+
const observations = acc.observations
|
|
419
|
+
const setupFailures = acc.setupFailures
|
|
420
|
+
let setupFailure = acc.setupFailure
|
|
421
|
+
const dispatchFailures = acc.dispatchFailures
|
|
422
|
+
const executions = acc.executions
|
|
423
|
+
const infrastructureFailures = acc.infrastructureFailures
|
|
424
|
+
let infrastructureFailure = acc.infrastructureFailure
|
|
425
|
+
const cleanupAttempts = acc.cleanupAttempts
|
|
426
|
+
let discardRequest = acc.discardRequest
|
|
427
|
+
const pending = acc.pending
|
|
428
|
+
const openSessions = new Map(acc.openSessions.map((s) => [s.session, s]))
|
|
429
|
+
const failures = acc.failures
|
|
324
430
|
|
|
325
431
|
const start = (ctx: PhaseContext): void => {
|
|
326
432
|
currentPhase = ctx
|
|
@@ -341,12 +447,14 @@ export function reduceBuild(events: AbEvent[]): BuildState {
|
|
|
341
447
|
per[r - 1] = findings
|
|
342
448
|
}
|
|
343
449
|
|
|
344
|
-
let lastEvent
|
|
450
|
+
let lastEvent = acc.lastEvent
|
|
345
451
|
for (const event of events) {
|
|
346
452
|
switch (event.type) {
|
|
347
453
|
// Facts the projection does not need (workspace liveness is the
|
|
348
454
|
// dispatcher's concern; finalize post-steps are failure-tolerant §5).
|
|
349
455
|
case 'build.created':
|
|
456
|
+
pinnedAssets = event.payload.assets ?? []
|
|
457
|
+
break
|
|
350
458
|
case 'workspace.provisioned':
|
|
351
459
|
case 'workspace.released':
|
|
352
460
|
case 'publication.requested':
|
|
@@ -443,10 +551,23 @@ export function reduceBuild(events: AbEvent[]): BuildState {
|
|
|
443
551
|
case 'build.auto-merge-requested':
|
|
444
552
|
autoMerge.requested = true
|
|
445
553
|
autoMerge.commandSeq = event.seq
|
|
554
|
+
// A fan-out (or seeded) command carries the default fact it answers; a
|
|
555
|
+
// bare per-build command must preserve the previous provenance.
|
|
556
|
+
if (event.payload.defaultSeq !== undefined) autoMerge.defaultSeq = event.payload.defaultSeq
|
|
446
557
|
break
|
|
447
558
|
case 'build.auto-merge-cancelled':
|
|
448
559
|
autoMerge.requested = false
|
|
449
560
|
autoMerge.commandSeq = event.seq
|
|
561
|
+
if (event.payload.defaultSeq !== undefined) autoMerge.defaultSeq = event.payload.defaultSeq
|
|
562
|
+
break
|
|
563
|
+
case 'build.auto-merge-default-observed':
|
|
564
|
+
// A no-op fan-out still advances the build's provenance. Without it, a
|
|
565
|
+
// matching default fact would stay strictly ahead of a bare per-build
|
|
566
|
+
// command and be re-applied on the next tick, reverting the operator's
|
|
567
|
+
// explicit choice (f_28b3fba1).
|
|
568
|
+
if (event.payload.defaultSeq > (autoMerge.defaultSeq ?? 0)) {
|
|
569
|
+
autoMerge.defaultSeq = event.payload.defaultSeq
|
|
570
|
+
}
|
|
450
571
|
break
|
|
451
572
|
case 'build.paused':
|
|
452
573
|
pausedFlag = true
|
|
@@ -467,6 +588,7 @@ export function reduceBuild(events: AbEvent[]): BuildState {
|
|
|
467
588
|
break
|
|
468
589
|
case 'spec.revised':
|
|
469
590
|
specRev = event.payload.artifact.rev
|
|
591
|
+
if (event.payload.assets !== undefined) pinnedAssets = event.payload.assets
|
|
470
592
|
// §6.3: rev N+1 restarts the build from plan. The restart boundary
|
|
471
593
|
// invalidates every approval and result at or before it, so it is also
|
|
472
594
|
// a verify cycle boundary. Finalize post-steps remain in full-log
|
|
@@ -661,6 +783,7 @@ export function reduceBuild(events: AbEvent[]): BuildState {
|
|
|
661
783
|
answer: event.payload.answer,
|
|
662
784
|
resolution: event.payload.resolution,
|
|
663
785
|
...(event.payload.artifact !== undefined ? { artifact: event.payload.artifact } : {}),
|
|
786
|
+
...(event.payload.assets !== undefined ? { assets: event.payload.assets } : {}),
|
|
664
787
|
...(event.payload.reviewRoundCeiling !== undefined
|
|
665
788
|
? { reviewRoundCeiling: event.payload.reviewRoundCeiling }
|
|
666
789
|
: {}),
|
|
@@ -699,54 +822,112 @@ export function reduceBuild(events: AbEvent[]): BuildState {
|
|
|
699
822
|
lastEvent = event
|
|
700
823
|
}
|
|
701
824
|
|
|
825
|
+
acc.attached = attached
|
|
826
|
+
acc.pausedFlag = pausedFlag
|
|
827
|
+
acc.terminal = terminal
|
|
828
|
+
acc.outcome = outcome
|
|
829
|
+
acc.round = round
|
|
830
|
+
acc.currentPhase = currentPhase
|
|
831
|
+
acc.lastCompletedPhase = lastCompletedPhase
|
|
832
|
+
acc.openEscalations = [...openEscalations.values()]
|
|
833
|
+
acc.pr = pr
|
|
834
|
+
acc.prState = prState
|
|
835
|
+
acc.specRev = specRev
|
|
836
|
+
acc.restartSince = restartSince
|
|
837
|
+
acc.pinnedAssets = pinnedAssets
|
|
838
|
+
acc.finalizeCompletedSeq = finalizeCompletedSeq
|
|
839
|
+
acc.codeReviewApproved = codeReviewApproved
|
|
840
|
+
acc.codeReviewApproval = codeReviewApproval
|
|
841
|
+
acc.reconcileAttempts = reconcileAttempts
|
|
842
|
+
acc.setupFailure = setupFailure
|
|
843
|
+
acc.infrastructureFailure = infrastructureFailure
|
|
844
|
+
acc.discardRequest = discardRequest
|
|
845
|
+
acc.openSessions = [...openSessions.values()]
|
|
846
|
+
acc.lastEvent = lastEvent
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
function finishBuild(acc: BuildAcc): BuildState {
|
|
850
|
+
const {
|
|
851
|
+
terminal,
|
|
852
|
+
pausedFlag,
|
|
853
|
+
attached,
|
|
854
|
+
currentPhase,
|
|
855
|
+
lastCompletedPhase,
|
|
856
|
+
lastEvent,
|
|
857
|
+
discardRequest,
|
|
858
|
+
pending,
|
|
859
|
+
} = acc
|
|
860
|
+
|
|
702
861
|
// §15.5 precedence: aborted/done (terminal, latest wins) > paused >
|
|
703
862
|
// blocked > running > queued. See BuildState.status for the paused+blocked
|
|
704
863
|
// overlap rule.
|
|
705
864
|
const status: BuildStatus =
|
|
706
865
|
terminal ??
|
|
707
|
-
(pausedFlag
|
|
866
|
+
(pausedFlag
|
|
867
|
+
? 'paused'
|
|
868
|
+
: acc.openEscalations.length > 0
|
|
869
|
+
? 'blocked'
|
|
870
|
+
: attached
|
|
871
|
+
? 'running'
|
|
872
|
+
: 'queued')
|
|
708
873
|
|
|
709
874
|
const active = currentPhase ?? lastCompletedPhase
|
|
710
875
|
|
|
711
876
|
return {
|
|
712
877
|
status,
|
|
713
|
-
outcome,
|
|
878
|
+
outcome: acc.outcome,
|
|
714
879
|
phase: active?.phase,
|
|
715
|
-
round,
|
|
880
|
+
round: acc.round,
|
|
716
881
|
currentPhase,
|
|
717
882
|
lastCompletedPhase,
|
|
718
|
-
openEscalations: [...openEscalations
|
|
719
|
-
answeredEscalations,
|
|
720
|
-
reviewRoundCeilings,
|
|
721
|
-
pr,
|
|
722
|
-
prState,
|
|
723
|
-
autoMerge,
|
|
724
|
-
finalizeCompletedSeq,
|
|
725
|
-
finalizeSteps,
|
|
883
|
+
openEscalations: [...acc.openEscalations],
|
|
884
|
+
answeredEscalations: acc.answeredEscalations,
|
|
885
|
+
reviewRoundCeilings: acc.reviewRoundCeilings,
|
|
886
|
+
pr: acc.pr,
|
|
887
|
+
prState: acc.prState,
|
|
888
|
+
autoMerge: acc.autoMerge,
|
|
889
|
+
finalizeCompletedSeq: acc.finalizeCompletedSeq,
|
|
890
|
+
finalizeSteps: acc.finalizeSteps,
|
|
726
891
|
lastEvent,
|
|
727
892
|
lastSeq: lastEvent?.seq ?? 0,
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
893
|
+
pinnedAssets: acc.pinnedAssets,
|
|
894
|
+
specRev: acc.specRev,
|
|
895
|
+
restartSince: acc.restartSince,
|
|
896
|
+
plan: acc.plan,
|
|
897
|
+
implement: acc.implement,
|
|
898
|
+
codeReviewApproved: acc.codeReviewApproved,
|
|
899
|
+
codeReviewApproval: acc.codeReviewApproval,
|
|
900
|
+
reviewFindings: { planReview: acc.planReviewFindings, codeReview: acc.codeReviewFindings },
|
|
901
|
+
verify: acc.verify,
|
|
902
|
+
reconcileAttempts: acc.reconcileAttempts,
|
|
903
|
+
observations: acc.observations,
|
|
904
|
+
setupFailures: acc.setupFailures,
|
|
905
|
+
setupFailure: acc.setupFailure,
|
|
906
|
+
dispatchFailures: acc.dispatchFailures,
|
|
907
|
+
executions: acc.executions,
|
|
908
|
+
infrastructureFailures: acc.infrastructureFailures,
|
|
909
|
+
infrastructureFailure: acc.infrastructureFailure,
|
|
910
|
+
cleanupAttempts: acc.cleanupAttempts,
|
|
745
911
|
...(terminal === undefined && discardRequest !== undefined ? { discardRequest } : {}),
|
|
746
912
|
pendingCommands: [...pending.pause, ...pending.resume, ...pending.abort].sort(
|
|
747
913
|
(a, b) => a.seq - b.seq,
|
|
748
914
|
),
|
|
749
|
-
sessions: { open: [...openSessions
|
|
750
|
-
failures,
|
|
915
|
+
sessions: { open: [...acc.openSessions] },
|
|
916
|
+
failures: acc.failures,
|
|
751
917
|
}
|
|
752
918
|
}
|
|
919
|
+
|
|
920
|
+
/** The incremental form of `reduceBuild`: advance a carried accumulator with
|
|
921
|
+
* only the events newer than it, then derive the state. */
|
|
922
|
+
export const buildReducer: IncrementalReducer<BuildAcc, AbEvent, BuildState> = defineReducer({
|
|
923
|
+
version: BUILD_REDUCER_VERSION,
|
|
924
|
+
initial: initialBuildAcc,
|
|
925
|
+
fold: foldBuild,
|
|
926
|
+
finish: finishBuild,
|
|
927
|
+
})
|
|
928
|
+
|
|
929
|
+
export function reduceBuild(events: AbEvent[]): BuildState {
|
|
930
|
+
const acc = initialBuildAcc()
|
|
931
|
+
foldBuild(acc, events)
|
|
932
|
+
return finishBuild(acc)
|
|
933
|
+
}
|
|
@@ -182,6 +182,12 @@ export const OPERATOR_TOOL_ANNOTATIONS = {
|
|
|
182
182
|
idempotentHint: false,
|
|
183
183
|
approval: 'default',
|
|
184
184
|
},
|
|
185
|
+
'sandbox.publish': {
|
|
186
|
+
readOnlyHint: false,
|
|
187
|
+
destructiveHint: true,
|
|
188
|
+
idempotentHint: false,
|
|
189
|
+
approval: 'default',
|
|
190
|
+
},
|
|
185
191
|
} as const satisfies Record<string, ToolAnnotationsTableEntry>
|
|
186
192
|
|
|
187
193
|
export type OperatorToolName = keyof typeof OPERATOR_TOOL_ANNOTATIONS
|
|
@@ -29,19 +29,27 @@ export async function setRepositorySetting(opts: {
|
|
|
29
29
|
enabled: boolean
|
|
30
30
|
/** Delegated-write attribution marker threaded onto the event's actor. */
|
|
31
31
|
via?: Via
|
|
32
|
+
/** Dispatcher invocation that performed the write. Present on the
|
|
33
|
+
* dashboard/launch-flag write paths so the two are distinguishable in the
|
|
34
|
+
* journal; optional so the operator API (which has no run) is unchanged. */
|
|
35
|
+
run?: string
|
|
32
36
|
}): Promise<{ enabled: boolean; event: RepositoryEventEnvelope }> {
|
|
33
37
|
await opts.store.ensureRepo(opts.repo)
|
|
38
|
+
const payload = {
|
|
39
|
+
enabled: opts.enabled,
|
|
40
|
+
...(opts.run !== undefined ? { run: opts.run } : {}),
|
|
41
|
+
}
|
|
34
42
|
const event =
|
|
35
43
|
opts.setting === 'intake'
|
|
36
44
|
? await opts.store.appendRepo(opts.repo, {
|
|
37
45
|
actor: humanActor(opts.user, opts.via),
|
|
38
46
|
type: 'dispatcher.intake-set',
|
|
39
|
-
payload
|
|
47
|
+
payload,
|
|
40
48
|
})
|
|
41
49
|
: await opts.store.appendRepo(opts.repo, {
|
|
42
50
|
actor: humanActor(opts.user, opts.via),
|
|
43
51
|
type: 'dispatcher.auto-merge-default-set',
|
|
44
|
-
payload
|
|
52
|
+
payload,
|
|
45
53
|
})
|
|
46
54
|
return { enabled: opts.enabled, event }
|
|
47
55
|
}
|
|
@@ -52,9 +60,12 @@ export async function toggleRepositorySetting(opts: {
|
|
|
52
60
|
user: string
|
|
53
61
|
setting: RepositorySetting
|
|
54
62
|
via?: Via
|
|
63
|
+
/** Dispatcher invocation that performed the write (see setRepositorySetting). */
|
|
64
|
+
run?: string
|
|
55
65
|
}): Promise<{ enabled: boolean; event: RepositoryEventEnvelope }> {
|
|
56
66
|
await opts.store.ensureRepo(opts.repo)
|
|
57
|
-
|
|
67
|
+
// Bounded read (AUT-489): the settings reducer consumes durable types only.
|
|
68
|
+
const settings = reduceDispatchSettings(await opts.store.getRepoStateEvents(opts.repo))
|
|
58
69
|
const enabled = opts.setting === 'intake' ? !settings.intake : !settings.defaultAutoMerge
|
|
59
70
|
return setRepositorySetting({ ...opts, enabled })
|
|
60
71
|
}
|
|
@@ -66,7 +77,8 @@ export async function toggleHarvestGate(opts: {
|
|
|
66
77
|
via?: Via
|
|
67
78
|
}): Promise<{ command: 'pause' | 'resume'; event: RepositoryEventEnvelope }> {
|
|
68
79
|
await opts.store.ensureRepo(opts.repo)
|
|
69
|
-
|
|
80
|
+
// Bounded read (AUT-489): the harvest reducer consumes durable types only.
|
|
81
|
+
const state = reduceHarvest(await opts.store.getRepoStateEvents(opts.repo))
|
|
70
82
|
const pending = state.pendingCommands.at(-1)
|
|
71
83
|
const requestedPaused = pending === undefined ? state.paused : pending.command === 'pause'
|
|
72
84
|
const command = requestedPaused ? 'resume' : 'pause'
|
|
@@ -87,7 +99,9 @@ export async function controlHarvestRun(opts: {
|
|
|
87
99
|
via?: Via
|
|
88
100
|
}): Promise<{ action: HarvestRunAction; event: RepositoryEventEnvelope }> {
|
|
89
101
|
await opts.store.ensureRepo(opts.repo)
|
|
90
|
-
|
|
102
|
+
// Bounded read (AUT-489): the harvest reducer and the dashboard harvest
|
|
103
|
+
// projection consume durable types only.
|
|
104
|
+
const events = await opts.store.getRepoStateEvents(opts.repo)
|
|
91
105
|
const state = reduceHarvest(events)
|
|
92
106
|
const projected = projectHarvest(events)
|
|
93
107
|
if (projected === undefined || projected.run !== opts.run) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
// Core's operator surface: the tool registry behind `ab mcp`, the CLI-facing
|
|
2
|
+
// control/query/tickets/requests primitives, and the operator wire protocol.
|
|
3
|
+
// The operator HTTP server and client ship in
|
|
4
|
+
// `@defrex/autobuild-hosted-store-service` (see its `operator-api` subpath).
|
|
5
5
|
export {
|
|
6
6
|
answerRequestSchema,
|
|
7
7
|
buildControlRequestSchema,
|
|
@@ -9,6 +9,10 @@ export {
|
|
|
9
9
|
bulkControlRequestSchema,
|
|
10
10
|
harvestControlRequestSchema,
|
|
11
11
|
operatorErrorSchema,
|
|
12
|
+
sessionApprovalRequestSchema,
|
|
13
|
+
sessionCreateRequestSchema,
|
|
14
|
+
sessionMessageRequestSchema,
|
|
15
|
+
sessionWakeRequestSchema,
|
|
12
16
|
settingRequestSchema,
|
|
13
17
|
ticketBlockerRequestSchema,
|
|
14
18
|
ticketCreateRequestSchema,
|
|
@@ -19,6 +23,10 @@ export type {
|
|
|
19
23
|
OperatorAnswerRequest,
|
|
20
24
|
OperatorBuildControlRequest,
|
|
21
25
|
OperatorErrorBody,
|
|
26
|
+
OperatorSessionApprovalRequest,
|
|
27
|
+
OperatorSessionCreateRequest,
|
|
28
|
+
OperatorSessionMessageRequest,
|
|
29
|
+
OperatorSessionWakeRequest,
|
|
22
30
|
OperatorTicketBlockerRequest,
|
|
23
31
|
OperatorTicketCreateRequest,
|
|
24
32
|
OperatorTicketMoveRequest,
|
|
@@ -29,8 +37,32 @@ export type {
|
|
|
29
37
|
OperatorBuildView,
|
|
30
38
|
OperatorDashboardSnapshot,
|
|
31
39
|
} from './query'
|
|
32
|
-
export {
|
|
33
|
-
|
|
40
|
+
export {
|
|
41
|
+
controlHarvestRun,
|
|
42
|
+
OperatorControlError,
|
|
43
|
+
setRepositorySetting,
|
|
44
|
+
toggleHarvestGate,
|
|
45
|
+
toggleRepositorySetting,
|
|
46
|
+
} from './control'
|
|
47
|
+
export {
|
|
48
|
+
getHarvestStatus,
|
|
49
|
+
getOperatorBuild,
|
|
50
|
+
getOperatorDashboard,
|
|
51
|
+
getRepositoryStatus,
|
|
52
|
+
listOperatorBuilds,
|
|
53
|
+
OperatorQueryError,
|
|
54
|
+
OrchestratorConfigError,
|
|
55
|
+
orchestratorConfig,
|
|
56
|
+
} from './query'
|
|
57
|
+
export {
|
|
58
|
+
orchestratorWakeGlobs,
|
|
59
|
+
ORCHESTRATOR_ROUTE_LIMIT_SECONDS,
|
|
60
|
+
type OrchestratorConfig,
|
|
61
|
+
} from '../config/schema'
|
|
62
|
+
export type { OrchestratorTurnRunner } from '../orchestrator/turn-runner'
|
|
63
|
+
export { createOrchestratorTurnRunner } from '../orchestrator/turn-runner'
|
|
64
|
+
export { randomIds } from '../ids'
|
|
65
|
+
export type { Config } from '../config/schema'
|
|
34
66
|
export {
|
|
35
67
|
OPERATOR_NOTES_ARTIFACT,
|
|
36
68
|
TOOLS,
|
|
@@ -59,4 +91,37 @@ export type {
|
|
|
59
91
|
OperatorTicketDetail,
|
|
60
92
|
OperatorTicketQueue,
|
|
61
93
|
} from './tickets'
|
|
94
|
+
export {
|
|
95
|
+
getOperatorTicket,
|
|
96
|
+
listOperatorTickets,
|
|
97
|
+
mutateOperatorTicket,
|
|
98
|
+
} from './tickets'
|
|
99
|
+
export type { OperatorSandboxService } from './sandbox'
|
|
62
100
|
export * from './presentation'
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Hosted operator-server composition. The moved server (in
|
|
104
|
+
* `@defrex/autobuild-hosted-store-service`) builds its routes from these core
|
|
105
|
+
* primitives; they are re-exported here so the hosted package needs no core
|
|
106
|
+
* internals. Deliberately not a general public API — do not grow it for
|
|
107
|
+
* core-internal convenience.
|
|
108
|
+
*/
|
|
109
|
+
export {
|
|
110
|
+
BuildControlError,
|
|
111
|
+
controlBuild,
|
|
112
|
+
type BuildControlAction,
|
|
113
|
+
type BuildControlResult,
|
|
114
|
+
} from '../cli/build-control'
|
|
115
|
+
export {
|
|
116
|
+
BulkWalkError,
|
|
117
|
+
bulkControlRepository,
|
|
118
|
+
type BulkControlSummary,
|
|
119
|
+
} from '../cli/bulk-control'
|
|
120
|
+
export { effectiveStatus } from '../cli/dashboard/model'
|
|
121
|
+
export { reduceBuild } from '../kernel/reducer'
|
|
122
|
+
export { TicketOperationError } from '../ports/tickets/operations'
|
|
123
|
+
export { humanActor, type Via } from '../events/envelope'
|
|
124
|
+
export type { HarvestStatusView } from '../cli/harvest'
|
|
125
|
+
export type { RepositoryStatus } from '../cli/repository-status'
|
|
126
|
+
export type { BuildSummary } from '../cli/status'
|
|
127
|
+
export type { SessionRecord } from '../store/types'
|